diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 3d27cfd116..0000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: "[BUG] ..." -labels: bug -assignees: '' - ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Screenshots** -If applicable, add screenshots to help explain your problem. - -**Smartphone (please complete the following information):** - - Device: [e.g. Samsung Galaxy S20 Ultra] - - OS: [e.g. Android 11] - -**Additional context** -Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000000..38b4964c21 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,63 @@ +name: Bug Report +description: Create a report to help us improve +title: "[BUG] ..." +labels: ["bug"] +body: + - type: checkboxes + id: terms + attributes: + label: Please confirm the following + options: + - label: I checked [the current issues](https://github.com/ILIYANGERMANOV/ivy-wallet/issues) for duplicate problems + required: true + - type: textarea + id: bug-description + attributes: + label: Describe the bug + description: A clear and concise description of what the bug is. + placeholder: I clicked something and my millions are not displayed anymore! + validations: + required: true + - type: textarea + id: reproduce + attributes: + label: To Reproduce + description: "Steps to reproduce the behavior:" + placeholder: | + 1. Go to '...' + 2. Click on '....' + 3. Scroll down to '....' + 4. See error + value: "1. " + validations: + required: false + - type: textarea + id: expected-behavior + attributes: + label: Expected behavior + description: A clear and concise description of what you expected to happen. + placeholder: My millions should be displayed! + validations: + required: false + - type: textarea + id: screenshots + attributes: + label: Screenshots + description: If applicable, add screenshots to help explain your problem. + validations: + required: false + - type: input + id: smartphone + attributes: + label: Smartphone + description: Specify the phone model and Android version. + placeholder: Samsung Galaxy S20 Ultra, Android 11 + validations: + required: true + - type: textarea + id: additional + attributes: + label: Additional context + description: Add any other context about the problem here. + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/dev-contributor-request.md b/.github/ISSUE_TEMPLATE/dev-contributor-request.md deleted file mode 100644 index 06e443b552..0000000000 --- a/.github/ISSUE_TEMPLATE/dev-contributor-request.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -name: Dev/Contributor Request -about: Report a problem or suggest an improvement -title: '' -labels: dev -assignees: '' - ---- - -**Is the project building?** -Yes/No - -**What would you like to improve?** -I want to: -- a -- b -- c -- d - -Because: - -- e -- f -- g - -**Other** -Share your thoughts in free format. - -**Logs** -Share any logs or information that might help us resolve your issue faster. diff --git a/.github/ISSUE_TEMPLATE/dev-contributor-request.yml b/.github/ISSUE_TEMPLATE/dev-contributor-request.yml new file mode 100644 index 0000000000..12d930151f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/dev-contributor-request.yml @@ -0,0 +1,56 @@ +name: Dev/Contributor Request +description: Report a problem or suggest an improvement +labels: ["dev"] +body: + - type: checkboxes + id: terms + attributes: + label: Please confirm the following + options: + - label: I checked [the current issues](https://github.com/ILIYANGERMANOV/ivy-wallet/issues) for duplicate problems + required: true + - type: dropdown + id: project-building + attributes: + label: Is the project building? + options: + - "Yes" + - "No" + validations: + required: false + - type: textarea + id: description + attributes: + label: What would you like to improve? + description: "I want to:" + placeholder: | + - a + - b + - c + value: "- " + validations: + required: true + - type: textarea + id: because + attributes: + label: Because... + placeholder: | + - d + - e + - f + validations: + required: false + - type: textarea + id: other + attributes: + label: Other + description: Share your thoughts in free format. + validations: + required: false + - type: textarea + id: logs + attributes: + label: Logs + description: Share any logs or information that might help us resolve your issue faster. + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md deleted file mode 100644 index 4c93063285..0000000000 --- a/.github/ISSUE_TEMPLATE/feature-request.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -name: Feature Request -about: Request a feature to make Ivy Wallet better -title: '' -labels: user request -assignees: '' - ---- - -**What do you want to be added or improved?** -As a user I want to ... - -**Is your request already listed in [Issues](https://github.com/ILIYANGERMANOV/ivy-wallet/issues)?** -- [ ] Yes, it already exists. -- [ ] No, it's a new issue. -- [ ] I'm not sure but I've checked. - -_Put an `x` in the boxes that apply._ - -- [x] Demo: Checking checkbox using `[x]` - -**Explain with bullets why do you need it** -- a -- b -- c -- d - -**Explain how do you imagine it in detail** -Write any implementation details that might help us better fulfill your feature request. -**I'd like to:** -- a -- b -- c -- d diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml new file mode 100644 index 0000000000..b583c2d01d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.yml @@ -0,0 +1,40 @@ +name: Feature Request +description: Request a feature to make Ivy Wallet better +title: "[FEATURES] ..." +labels: ["user request"] +body: + - type: checkboxes + id: terms + attributes: + label: Please confirm the following + options: + - label: I checked [the current issues](https://github.com/ILIYANGERMANOV/ivy-wallet/issues) for duplicate problems + required: true + - type: textarea + id: description + attributes: + label: What do you want to be added or improved? + description: "I want to:" + placeholder: As a user I want to ... + value: As a user I want to + validations: + required: true + - type: textarea + id: reason + attributes: + label: Why do you need it? + description: Explain with bullets why do you need it. + placeholder: | + - a + - b + - c + value: "- " + validations: + required: false + - type: textarea + id: because + attributes: + label: How do you imagine it? + description: Write any implementation details that might help us better fulfill your feature request. + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/import-csv-request.md b/.github/ISSUE_TEMPLATE/import-csv-request.md deleted file mode 100644 index cc83e24b90..0000000000 --- a/.github/ISSUE_TEMPLATE/import-csv-request.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -name: Import CSV Request -about: Request an import CSV from specific app to be supported. -title: 'Import CSV from {APP_NAME} ({PACKAGE_NAME})' -labels: user request -assignees: '' - ---- - -**Is your request already listed in [Issues](https://github.com/ILIYANGERMANOV/ivy-wallet/issues)?** - -- [ ] Yes -- [ ] No -- [ ] Kinda (please describe): - -### Google PlayStore URL to the app from which you want to import: -- URL: - -### Attach Samples CSV from the app: - -Attach an exported CSV file from the app to this issue. - -**CSV file requirements:** -- [ ] includes Income type transaction -- [ ] includes Expense type transaction -- [ ] includes Transfer type transaction (optional) -- [ ] includes title, description, all attributes available in the app (optional) - -**Is CSV export from the app free?** - -- [ ] Yes -- [ ] No -- [ ] It depends (please describe): - -_Put an `x` in the boxes that apply._ - -- [x] Demo: Checking checkbox using `[x]` - -## Developer/Contributor Notes - -- Checkout - this **[Sample Commit](https://github.com/ILIYANGERMANOV/ivy-wallet/commit/323ed68343a0904ee33f226480716a30368fd646)** - to see how import CSV works in Ivy Wallet -- Make sure that the imported dates of the transactions are correct \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/import-csv-request.yml b/.github/ISSUE_TEMPLATE/import-csv-request.yml new file mode 100644 index 0000000000..89b2fd6d6e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/import-csv-request.yml @@ -0,0 +1,59 @@ +name: Import CSV Request +description: Request an import CSV from specific app to be supported. +title: "[IMPORT] {APP_NAME} ({PACKAGE_NAME})" +labels: ["user request"] +body: + - type: checkboxes + id: terms + attributes: + label: Please confirm the following + options: + - label: I checked [the current issues](https://github.com/ILIYANGERMANOV/ivy-wallet/issues) for duplicate problems + required: true + - type: input + id: url-to-app + attributes: + label: Google PlayStore URL to the app from which you want to import + description: A clear and concise description of what the bug is. + placeholder: https://play.google.com/store/apps/details?id=com.cool.app + validations: + required: false + - type: textarea + id: csv-sample + attributes: + label: Attach Samples CSV from the app + description: Attach an exported CSV file from the app to this issue. + validations: + required: false + - type: dropdown + id: requirements + attributes: + label: CSV file requirements + multiple: true + options: + - includes Income type transaction + - includes Expense type transaction + - includes Transfer type transaction (optional) + - includes title, description, all attributes available in the app (optional) + validations: + required: false + - type: dropdown + id: cost + attributes: + label: Is CSV export from the app free? + options: + - "Yes" + - "No" + - It depends + validations: + required: false + - type: textarea + id: notes + attributes: + label: Developer/Contributor Notes + description: Just a few notes for developers. Ignore the content and don't change it, please. + value: | + - Checkout this **[Sample Commit](https://github.com/ILIYANGERMANOV/ivy-wallet/commit/323ed68343a0904ee33f226480716a30368fd646)** to see how import CSV works in Ivy Wallet + - Make sure that the imported dates of the transactions are correct" + validations: + required: true diff --git a/.github/workflows/internal_release.yml b/.github/workflows/internal_release.yml index 1bf49fa874..527bc5668e 100644 --- a/.github/workflows/internal_release.yml +++ b/.github/workflows/internal_release.yml @@ -139,6 +139,7 @@ jobs: REPO: ${{ github.repository }} - name: Create GitHub Release + if: always() #Execute even the generation of changelog has failed id: create_release uses: actions/create-release@latest env: diff --git a/README.md b/README.md index 91e5eb91de..0488d60f0f 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ Our plan is: 1. Create [Ivy Telegram Community](https://t.me/+ETavgioAvWg4NThk). :heavy_check_mark: 1. Create a crypto donations mechanism (BTC, ETH, ADA, SOL...) 1. Use the donations to setup a dev fund where contributors can earn money by helping the project. -1. Create a proposal and voting system based using Cardano (ADA) smart contracts. +1. Create a proposal and voting system using Cardano (ADA) smart contracts. 1. So far, so good! Let us know what do you think should be next? Correct us, if we're wrong! Share your opinion. Be the change. :star: @@ -154,7 +154,7 @@ compact **[Contributors Guide](https://github.com/ILIYANGERMANOV/ivy-wallet/blob to begin. TL;DR: -- Submit pull requests for bug fixes / code improvements +- Submit pull requests for bug fixes / code improvements to the `develop` branch - Implement and submit PRs for opened issues - Report (or fix) bugs/glitches - Create new issues to give us ideas and feedback diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 7f1f804381..83b9d01306 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -20,14 +20,17 @@ # hide the original source file name. #-renamesourcefileattribute SourceFile +# Fix broken stuff by R8 +-keep class com.ivy.wallet.ui.widget.** { *; } +-keep class com.ivy.wallet.domain.data.** { *; } +-keep class com.ivy.wallet.io.network.** { *; } +-keep class com.ivy.wallet.io.persistence.data.** { *; } +-keep class com.ivy.wallet.io.network.data.** { *; } +-keep class com.ivy.wallet.domain.event.** { *; } + -keepattributes EnclosingMethod -keepattributes InnerClasses -# Widget --keep class com.ivy.wallet.widget.** { *; } -# ------ - - # Firebase Crashlytics -dontwarn org.xmlpull.v1.** -dontnote org.xmlpull.v1.** @@ -136,12 +139,6 @@ # Application classes that will be serialized/deserialized over Gson -keep class com.ivy.wallet.model.** { ; } -# Fix broken stuff by R8 --keep class com.ivy.wallet.domain.data.** { *; } --keep class com.ivy.wallet.ui.widget.** { *; } --keep class com.ivy.wallet.io.network.** { *; } --keep class com.ivy.wallet.domain.event.** { *; } - # Prevent proguard from stripping interface information from TypeAdapter, TypeAdapterFactory, # JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter) -keep class * implements com.google.gson.TypeAdapter diff --git a/app/schemas/com.ivy.wallet.io.persistence.IvyRoomDatabase/120.json b/app/schemas/com.ivy.wallet.io.persistence.IvyRoomDatabase/120.json new file mode 100644 index 0000000000..84070e16b9 --- /dev/null +++ b/app/schemas/com.ivy.wallet.io.persistence.IvyRoomDatabase/120.json @@ -0,0 +1,793 @@ +{ + "formatVersion": 1, + "database": { + "version": 120, + "identityHash": "751c82ed72a54493f42000cd47a99137", + "entities": [ + { + "tableName": "accounts", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `currency` TEXT, `color` INTEGER NOT NULL, `icon` TEXT, `orderNum` REAL NOT NULL, `includeInBalance` INTEGER NOT NULL, `seAccountId` TEXT, `isSynced` INTEGER NOT NULL, `isDeleted` INTEGER NOT NULL, `id` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "currency", + "columnName": "currency", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "orderNum", + "columnName": "orderNum", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "includeInBalance", + "columnName": "includeInBalance", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "seAccountId", + "columnName": "seAccountId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isSynced", + "columnName": "isSynced", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDeleted", + "columnName": "isDeleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "transactions", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` TEXT NOT NULL, `type` TEXT NOT NULL, `amount` REAL NOT NULL, `toAccountId` TEXT, `toAmount` REAL, `title` TEXT, `description` TEXT, `dateTime` INTEGER, `categoryId` TEXT, `dueDate` INTEGER, `recurringRuleId` TEXT, `attachmentUrl` TEXT, `loanId` TEXT, `loanRecordId` TEXT, `seTransactionId` TEXT, `seAutoCategoryId` TEXT, `isSynced` INTEGER NOT NULL, `isDeleted` INTEGER NOT NULL, `id` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "toAccountId", + "columnName": "toAccountId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "toAmount", + "columnName": "toAmount", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "dateTime", + "columnName": "dateTime", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "categoryId", + "columnName": "categoryId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "dueDate", + "columnName": "dueDate", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "recurringRuleId", + "columnName": "recurringRuleId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "attachmentUrl", + "columnName": "attachmentUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "loanId", + "columnName": "loanId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "loanRecordId", + "columnName": "loanRecordId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "seTransactionId", + "columnName": "seTransactionId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "seAutoCategoryId", + "columnName": "seAutoCategoryId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isSynced", + "columnName": "isSynced", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDeleted", + "columnName": "isDeleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "categories", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `color` INTEGER NOT NULL, `icon` TEXT, `orderNum` REAL NOT NULL, `seCategoryName` TEXT, `isSynced` INTEGER NOT NULL, `isDeleted` INTEGER NOT NULL, `id` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "orderNum", + "columnName": "orderNum", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "seCategoryName", + "columnName": "seCategoryName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isSynced", + "columnName": "isSynced", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDeleted", + "columnName": "isDeleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "wishlist_items", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `price` REAL NOT NULL, `accountId` TEXT NOT NULL, `categoryId` TEXT, `description` TEXT, `plannedDateTime` INTEGER, `orderNum` REAL NOT NULL, `id` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "price", + "columnName": "price", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "categoryId", + "columnName": "categoryId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "plannedDateTime", + "columnName": "plannedDateTime", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "orderNum", + "columnName": "orderNum", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "settings", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`theme` TEXT NOT NULL, `currency` TEXT NOT NULL, `bufferAmount` REAL NOT NULL, `name` TEXT NOT NULL, `isSynced` INTEGER NOT NULL, `isDeleted` INTEGER NOT NULL, `id` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "theme", + "columnName": "theme", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "currency", + "columnName": "currency", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "bufferAmount", + "columnName": "bufferAmount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isSynced", + "columnName": "isSynced", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDeleted", + "columnName": "isDeleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "planned_payment_rules", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`startDate` INTEGER, `intervalN` INTEGER, `intervalType` TEXT, `oneTime` INTEGER NOT NULL, `type` TEXT NOT NULL, `accountId` TEXT NOT NULL, `amount` REAL NOT NULL, `categoryId` TEXT, `title` TEXT, `description` TEXT, `isSynced` INTEGER NOT NULL, `isDeleted` INTEGER NOT NULL, `id` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "startDate", + "columnName": "startDate", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "intervalN", + "columnName": "intervalN", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "intervalType", + "columnName": "intervalType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "oneTime", + "columnName": "oneTime", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "categoryId", + "columnName": "categoryId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isSynced", + "columnName": "isSynced", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDeleted", + "columnName": "isDeleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "users", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`email` TEXT NOT NULL, `authProviderType` TEXT NOT NULL, `firstName` TEXT NOT NULL, `lastName` TEXT, `profilePicture` TEXT, `color` INTEGER NOT NULL, `testUser` INTEGER NOT NULL, `id` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "authProviderType", + "columnName": "authProviderType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstName", + "columnName": "firstName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lastName", + "columnName": "lastName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "profilePicture", + "columnName": "profilePicture", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "testUser", + "columnName": "testUser", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "exchange_rates", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`baseCurrency` TEXT NOT NULL, `currency` TEXT NOT NULL, `rate` REAL NOT NULL, PRIMARY KEY(`baseCurrency`, `currency`))", + "fields": [ + { + "fieldPath": "baseCurrency", + "columnName": "baseCurrency", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "currency", + "columnName": "currency", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "rate", + "columnName": "rate", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "baseCurrency", + "currency" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "budgets", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `amount` REAL NOT NULL, `categoryIdsSerialized` TEXT, `accountIdsSerialized` TEXT, `isSynced` INTEGER NOT NULL, `isDeleted` INTEGER NOT NULL, `orderId` REAL NOT NULL, `id` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "categoryIdsSerialized", + "columnName": "categoryIdsSerialized", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "accountIdsSerialized", + "columnName": "accountIdsSerialized", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isSynced", + "columnName": "isSynced", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDeleted", + "columnName": "isDeleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "orderId", + "columnName": "orderId", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "loans", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `amount` REAL NOT NULL, `type` TEXT NOT NULL, `color` INTEGER NOT NULL, `icon` TEXT, `orderNum` REAL NOT NULL, `accountId` TEXT, `isSynced` INTEGER NOT NULL, `isDeleted` INTEGER NOT NULL, `id` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "orderNum", + "columnName": "orderNum", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isSynced", + "columnName": "isSynced", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDeleted", + "columnName": "isDeleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "loan_records", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`loanId` TEXT NOT NULL, `amount` REAL NOT NULL, `note` TEXT, `dateTime` INTEGER NOT NULL, `interest` INTEGER NOT NULL, `accountId` TEXT, `convertedAmount` REAL, `isSynced` INTEGER NOT NULL, `isDeleted` INTEGER NOT NULL, `id` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "loanId", + "columnName": "loanId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "dateTime", + "columnName": "dateTime", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "interest", + "columnName": "interest", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "convertedAmount", + "columnName": "convertedAmount", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "isSynced", + "columnName": "isSynced", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDeleted", + "columnName": "isDeleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '751c82ed72a54493f42000cd47a99137')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/com.ivy.wallet.io.persistence.IvyRoomDatabase/121.json b/app/schemas/com.ivy.wallet.io.persistence.IvyRoomDatabase/121.json new file mode 100644 index 0000000000..83dd1a6fb9 --- /dev/null +++ b/app/schemas/com.ivy.wallet.io.persistence.IvyRoomDatabase/121.json @@ -0,0 +1,707 @@ +{ + "formatVersion": 1, + "database": { + "version": 121, + "identityHash": "319b13332051b3e936a5902b2b7a2ad5", + "entities": [ + { + "tableName": "accounts", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `currency` TEXT, `color` INTEGER NOT NULL, `icon` TEXT, `orderNum` REAL NOT NULL, `includeInBalance` INTEGER NOT NULL, `isSynced` INTEGER NOT NULL, `isDeleted` INTEGER NOT NULL, `id` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "currency", + "columnName": "currency", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "orderNum", + "columnName": "orderNum", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "includeInBalance", + "columnName": "includeInBalance", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isSynced", + "columnName": "isSynced", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDeleted", + "columnName": "isDeleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "transactions", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` TEXT NOT NULL, `type` TEXT NOT NULL, `amount` REAL NOT NULL, `toAccountId` TEXT, `toAmount` REAL, `title` TEXT, `description` TEXT, `dateTime` INTEGER, `categoryId` TEXT, `dueDate` INTEGER, `recurringRuleId` TEXT, `attachmentUrl` TEXT, `loanId` TEXT, `loanRecordId` TEXT, `isSynced` INTEGER NOT NULL, `isDeleted` INTEGER NOT NULL, `id` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "toAccountId", + "columnName": "toAccountId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "toAmount", + "columnName": "toAmount", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "dateTime", + "columnName": "dateTime", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "categoryId", + "columnName": "categoryId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "dueDate", + "columnName": "dueDate", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "recurringRuleId", + "columnName": "recurringRuleId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "attachmentUrl", + "columnName": "attachmentUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "loanId", + "columnName": "loanId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "loanRecordId", + "columnName": "loanRecordId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isSynced", + "columnName": "isSynced", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDeleted", + "columnName": "isDeleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "categories", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `color` INTEGER NOT NULL, `icon` TEXT, `orderNum` REAL NOT NULL, `isSynced` INTEGER NOT NULL, `isDeleted` INTEGER NOT NULL, `id` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "orderNum", + "columnName": "orderNum", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "isSynced", + "columnName": "isSynced", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDeleted", + "columnName": "isDeleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "settings", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`theme` TEXT NOT NULL, `currency` TEXT NOT NULL, `bufferAmount` REAL NOT NULL, `name` TEXT NOT NULL, `isSynced` INTEGER NOT NULL, `isDeleted` INTEGER NOT NULL, `id` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "theme", + "columnName": "theme", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "currency", + "columnName": "currency", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "bufferAmount", + "columnName": "bufferAmount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isSynced", + "columnName": "isSynced", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDeleted", + "columnName": "isDeleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "planned_payment_rules", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`startDate` INTEGER, `intervalN` INTEGER, `intervalType` TEXT, `oneTime` INTEGER NOT NULL, `type` TEXT NOT NULL, `accountId` TEXT NOT NULL, `amount` REAL NOT NULL, `categoryId` TEXT, `title` TEXT, `description` TEXT, `isSynced` INTEGER NOT NULL, `isDeleted` INTEGER NOT NULL, `id` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "startDate", + "columnName": "startDate", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "intervalN", + "columnName": "intervalN", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "intervalType", + "columnName": "intervalType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "oneTime", + "columnName": "oneTime", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "categoryId", + "columnName": "categoryId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isSynced", + "columnName": "isSynced", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDeleted", + "columnName": "isDeleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "users", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`email` TEXT NOT NULL, `authProviderType` TEXT NOT NULL, `firstName` TEXT NOT NULL, `lastName` TEXT, `profilePicture` TEXT, `color` INTEGER NOT NULL, `testUser` INTEGER NOT NULL, `id` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "authProviderType", + "columnName": "authProviderType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstName", + "columnName": "firstName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lastName", + "columnName": "lastName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "profilePicture", + "columnName": "profilePicture", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "testUser", + "columnName": "testUser", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "exchange_rates", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`baseCurrency` TEXT NOT NULL, `currency` TEXT NOT NULL, `rate` REAL NOT NULL, PRIMARY KEY(`baseCurrency`, `currency`))", + "fields": [ + { + "fieldPath": "baseCurrency", + "columnName": "baseCurrency", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "currency", + "columnName": "currency", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "rate", + "columnName": "rate", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "baseCurrency", + "currency" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "budgets", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `amount` REAL NOT NULL, `categoryIdsSerialized` TEXT, `accountIdsSerialized` TEXT, `isSynced` INTEGER NOT NULL, `isDeleted` INTEGER NOT NULL, `orderId` REAL NOT NULL, `id` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "categoryIdsSerialized", + "columnName": "categoryIdsSerialized", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "accountIdsSerialized", + "columnName": "accountIdsSerialized", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isSynced", + "columnName": "isSynced", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDeleted", + "columnName": "isDeleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "orderId", + "columnName": "orderId", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "loans", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `amount` REAL NOT NULL, `type` TEXT NOT NULL, `color` INTEGER NOT NULL, `icon` TEXT, `orderNum` REAL NOT NULL, `accountId` TEXT, `isSynced` INTEGER NOT NULL, `isDeleted` INTEGER NOT NULL, `id` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "orderNum", + "columnName": "orderNum", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isSynced", + "columnName": "isSynced", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDeleted", + "columnName": "isDeleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "loan_records", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`loanId` TEXT NOT NULL, `amount` REAL NOT NULL, `note` TEXT, `dateTime` INTEGER NOT NULL, `interest` INTEGER NOT NULL, `accountId` TEXT, `convertedAmount` REAL, `isSynced` INTEGER NOT NULL, `isDeleted` INTEGER NOT NULL, `id` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "loanId", + "columnName": "loanId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "dateTime", + "columnName": "dateTime", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "interest", + "columnName": "interest", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "convertedAmount", + "columnName": "convertedAmount", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "isSynced", + "columnName": "isSynced", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDeleted", + "columnName": "isDeleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '319b13332051b3e936a5902b2b7a2ad5')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/com.ivy.wallet.io.persistence.IvyRoomDatabase/122.json b/app/schemas/com.ivy.wallet.io.persistence.IvyRoomDatabase/122.json new file mode 100644 index 0000000000..295aa84e59 --- /dev/null +++ b/app/schemas/com.ivy.wallet.io.persistence.IvyRoomDatabase/122.json @@ -0,0 +1,707 @@ +{ + "formatVersion": 1, + "database": { + "version": 122, + "identityHash": "319b13332051b3e936a5902b2b7a2ad5", + "entities": [ + { + "tableName": "accounts", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `currency` TEXT, `color` INTEGER NOT NULL, `icon` TEXT, `orderNum` REAL NOT NULL, `includeInBalance` INTEGER NOT NULL, `isSynced` INTEGER NOT NULL, `isDeleted` INTEGER NOT NULL, `id` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "currency", + "columnName": "currency", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "orderNum", + "columnName": "orderNum", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "includeInBalance", + "columnName": "includeInBalance", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isSynced", + "columnName": "isSynced", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDeleted", + "columnName": "isDeleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "transactions", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` TEXT NOT NULL, `type` TEXT NOT NULL, `amount` REAL NOT NULL, `toAccountId` TEXT, `toAmount` REAL, `title` TEXT, `description` TEXT, `dateTime` INTEGER, `categoryId` TEXT, `dueDate` INTEGER, `recurringRuleId` TEXT, `attachmentUrl` TEXT, `loanId` TEXT, `loanRecordId` TEXT, `isSynced` INTEGER NOT NULL, `isDeleted` INTEGER NOT NULL, `id` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "toAccountId", + "columnName": "toAccountId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "toAmount", + "columnName": "toAmount", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "dateTime", + "columnName": "dateTime", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "categoryId", + "columnName": "categoryId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "dueDate", + "columnName": "dueDate", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "recurringRuleId", + "columnName": "recurringRuleId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "attachmentUrl", + "columnName": "attachmentUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "loanId", + "columnName": "loanId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "loanRecordId", + "columnName": "loanRecordId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isSynced", + "columnName": "isSynced", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDeleted", + "columnName": "isDeleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "categories", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `color` INTEGER NOT NULL, `icon` TEXT, `orderNum` REAL NOT NULL, `isSynced` INTEGER NOT NULL, `isDeleted` INTEGER NOT NULL, `id` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "orderNum", + "columnName": "orderNum", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "isSynced", + "columnName": "isSynced", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDeleted", + "columnName": "isDeleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "settings", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`theme` TEXT NOT NULL, `currency` TEXT NOT NULL, `bufferAmount` REAL NOT NULL, `name` TEXT NOT NULL, `isSynced` INTEGER NOT NULL, `isDeleted` INTEGER NOT NULL, `id` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "theme", + "columnName": "theme", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "currency", + "columnName": "currency", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "bufferAmount", + "columnName": "bufferAmount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isSynced", + "columnName": "isSynced", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDeleted", + "columnName": "isDeleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "planned_payment_rules", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`startDate` INTEGER, `intervalN` INTEGER, `intervalType` TEXT, `oneTime` INTEGER NOT NULL, `type` TEXT NOT NULL, `accountId` TEXT NOT NULL, `amount` REAL NOT NULL, `categoryId` TEXT, `title` TEXT, `description` TEXT, `isSynced` INTEGER NOT NULL, `isDeleted` INTEGER NOT NULL, `id` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "startDate", + "columnName": "startDate", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "intervalN", + "columnName": "intervalN", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "intervalType", + "columnName": "intervalType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "oneTime", + "columnName": "oneTime", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "categoryId", + "columnName": "categoryId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isSynced", + "columnName": "isSynced", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDeleted", + "columnName": "isDeleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "users", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`email` TEXT NOT NULL, `authProviderType` TEXT NOT NULL, `firstName` TEXT NOT NULL, `lastName` TEXT, `profilePicture` TEXT, `color` INTEGER NOT NULL, `testUser` INTEGER NOT NULL, `id` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "authProviderType", + "columnName": "authProviderType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "firstName", + "columnName": "firstName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lastName", + "columnName": "lastName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "profilePicture", + "columnName": "profilePicture", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "testUser", + "columnName": "testUser", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "exchange_rates", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`baseCurrency` TEXT NOT NULL, `currency` TEXT NOT NULL, `rate` REAL NOT NULL, PRIMARY KEY(`baseCurrency`, `currency`))", + "fields": [ + { + "fieldPath": "baseCurrency", + "columnName": "baseCurrency", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "currency", + "columnName": "currency", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "rate", + "columnName": "rate", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "baseCurrency", + "currency" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "budgets", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `amount` REAL NOT NULL, `categoryIdsSerialized` TEXT, `accountIdsSerialized` TEXT, `isSynced` INTEGER NOT NULL, `isDeleted` INTEGER NOT NULL, `orderId` REAL NOT NULL, `id` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "categoryIdsSerialized", + "columnName": "categoryIdsSerialized", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "accountIdsSerialized", + "columnName": "accountIdsSerialized", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isSynced", + "columnName": "isSynced", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDeleted", + "columnName": "isDeleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "orderId", + "columnName": "orderId", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "loans", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `amount` REAL NOT NULL, `type` TEXT NOT NULL, `color` INTEGER NOT NULL, `icon` TEXT, `orderNum` REAL NOT NULL, `accountId` TEXT, `isSynced` INTEGER NOT NULL, `isDeleted` INTEGER NOT NULL, `id` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "orderNum", + "columnName": "orderNum", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isSynced", + "columnName": "isSynced", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDeleted", + "columnName": "isDeleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "loan_records", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`loanId` TEXT NOT NULL, `amount` REAL NOT NULL, `note` TEXT, `dateTime` INTEGER NOT NULL, `interest` INTEGER NOT NULL, `accountId` TEXT, `convertedAmount` REAL, `isSynced` INTEGER NOT NULL, `isDeleted` INTEGER NOT NULL, `id` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "loanId", + "columnName": "loanId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "dateTime", + "columnName": "dateTime", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "interest", + "columnName": "interest", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "convertedAmount", + "columnName": "convertedAmount", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "isSynced", + "columnName": "isSynced", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDeleted", + "columnName": "isDeleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '319b13332051b3e936a5902b2b7a2ad5')" + ] + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/com/ivy/wallet/HiltTestRunner.kt b/app/src/androidTest/java/com/ivy/wallet/HiltTestRunner.kt index d00ad1665d..3a8a0f9a8d 100644 --- a/app/src/androidTest/java/com/ivy/wallet/HiltTestRunner.kt +++ b/app/src/androidTest/java/com/ivy/wallet/HiltTestRunner.kt @@ -8,7 +8,8 @@ import dagger.hilt.android.testing.HiltTestApplication // A custom runner to set up the instrumented application class for tests. class HiltTestRunner : AndroidJUnitRunner() { - override fun newApplication(cl: ClassLoader?, name: String?, context: Context?): Application { + override fun newApplication(cl: ClassLoader?, name: String?, context: Context): Application { + IvyAndroidApp.appContext = context return super.newApplication(cl, HiltTestApplication::class.java.name, context) } } \ No newline at end of file diff --git a/app/src/androidTest/java/com/ivy/wallet/compose/IvyComposeTest.kt b/app/src/androidTest/java/com/ivy/wallet/compose/IvyComposeTest.kt index e599f9a326..9fa1a19ca3 100644 --- a/app/src/androidTest/java/com/ivy/wallet/compose/IvyComposeTest.kt +++ b/app/src/androidTest/java/com/ivy/wallet/compose/IvyComposeTest.kt @@ -1,12 +1,17 @@ package com.ivy.wallet.compose import android.content.Context +import android.content.Context.INPUT_METHOD_SERVICE import android.util.Log +import android.view.inputmethod.InputMethodManager +import androidx.activity.ComponentActivity import androidx.compose.ui.test.IdlingResource import androidx.compose.ui.test.SemanticsNodeInteraction +import androidx.compose.ui.test.junit4.AndroidComposeTestRule import androidx.compose.ui.test.junit4.ComposeTestRule import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.compose.ui.test.performClick +import androidx.test.ext.junit.rules.ActivityScenarioRule import androidx.test.platform.app.InstrumentationRegistry import androidx.work.Configuration import androidx.work.impl.utils.SynchronousExecutor @@ -20,6 +25,7 @@ import com.ivy.wallet.ui.RootActivity import com.ivy.wallet.utils.* import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest +import kotlinx.coroutines.test.runTest import org.junit.After import org.junit.Before import org.junit.Rule @@ -98,7 +104,7 @@ abstract class IvyComposeTest { SharedPrefs(context()).removeAll() } - private fun resetDatabase() { + private fun resetDatabase() = runTest { ivyRoomDatabase.reset() } @@ -190,4 +196,14 @@ fun ComposeTestRule.clickWithRetry( ) } } +} + +fun AndroidComposeTestRule, A>.hideKeyboard() { + with(this.activity) { + if (currentFocus != null) { + val inputMethodManager: InputMethodManager = + this.getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager + inputMethodManager.hideSoftInputFromWindow(currentFocus!!.windowToken, 0) + } + } } \ No newline at end of file diff --git a/app/src/androidTest/java/com/ivy/wallet/compose/helpers/AccountsTab.kt b/app/src/androidTest/java/com/ivy/wallet/compose/helpers/AccountsTab.kt index 6e7437fc4c..1d1f9f5328 100644 --- a/app/src/androidTest/java/com/ivy/wallet/compose/helpers/AccountsTab.kt +++ b/app/src/androidTest/java/com/ivy/wallet/compose/helpers/AccountsTab.kt @@ -6,6 +6,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.test.* import androidx.compose.ui.test.junit4.AndroidComposeTestRule import androidx.test.ext.junit.rules.ActivityScenarioRule +import com.ivy.wallet.compose.hideKeyboard import com.ivy.wallet.compose.printTree import com.ivy.wallet.ui.theme.Ivy @@ -65,6 +66,8 @@ class AccountsTab( accountModal.apply { enterTitle(name) + composeTestRule.hideKeyboard() + ivyColorPicker.chooseColor(color = color) if (icon != null) { diff --git a/app/src/androidTest/java/com/ivy/wallet/compose/helpers/AmountInput.kt b/app/src/androidTest/java/com/ivy/wallet/compose/helpers/AmountInput.kt index bf05c9a2d8..81b67f45d1 100644 --- a/app/src/androidTest/java/com/ivy/wallet/compose/helpers/AmountInput.kt +++ b/app/src/androidTest/java/com/ivy/wallet/compose/helpers/AmountInput.kt @@ -4,30 +4,46 @@ import androidx.activity.ComponentActivity import androidx.compose.ui.test.hasTestTag import androidx.compose.ui.test.hasText import androidx.compose.ui.test.junit4.AndroidComposeTestRule +import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.performClick import androidx.test.ext.junit.rules.ActivityScenarioRule class AmountInput( private val composeTestRule: AndroidComposeTestRule, A> ) { - fun enterNumber(number: String) { + fun enterNumber( + number: String, + onCalculator: Boolean = false, + autoPressNonCalculator: Boolean = true, + ) { composeTestRule.waitForIdle() for (char in number) { when (char) { - in '0'..'9' -> pressNumber(char.toString().toInt()) + in '0'..'9' -> pressNumber( + number = char.toString().toInt(), + onCalculator = onCalculator + ) ',' -> { //do nothing } - '.' -> pressDecimalSeparator() + '.' -> pressDecimalSeparator( + onCalculator = onCalculator + ) } } - clickSet() + if (!onCalculator && autoPressNonCalculator) { + clickSet() + } } - fun pressNumber(number: Int) { - composeTestRule.onNode(hasTestTag("key_$number")) + private fun pressNumber(number: Int, onCalculator: Boolean) { + composeTestRule.onNode( + hasTestTag( + if (onCalculator) "calc_key_$number" else "key_$number" + ) + ) .performClick() } @@ -36,8 +52,54 @@ class AmountInput( .performClick() } - fun pressDecimalSeparator() { - composeTestRule.onNode(hasTestTag("key_decimal_separator")) + fun pressDecimalSeparator( + onCalculator: Boolean + ) { + composeTestRule.onNode( + hasTestTag( + if (onCalculator) "calc_key_decimal_separator" else "key_decimal_separator" + ) + ) + .performClick() + } + + fun pressPlus() { + composeTestRule.onNodeWithTag("key_+") + .performClick() + } + + fun pressMinus() { + composeTestRule.onNodeWithTag("key_-") + .performClick() + } + + fun pressMultiplication() { + composeTestRule.onNodeWithTag("key_*") + .performClick() + } + + fun pressDivision() { + composeTestRule.onNodeWithTag("key_/") + .performClick() + } + + fun pressLeftBracket() { + composeTestRule.onNodeWithTag("key_(") + .performClick() + } + + fun pressRightBracket() { + composeTestRule.onNodeWithTag("key_)") + .performClick() + } + + fun pressCalcEqual() { + composeTestRule.onNodeWithTag("key_=") + .performClick() + } + + fun clickCalcSet() { + composeTestRule.onNodeWithTag("calc_set") .performClick() } @@ -45,4 +107,9 @@ class AmountInput( composeTestRule.onNode(hasText("Enter")) .performClick() } + + fun clickCalculator() { + composeTestRule.onNodeWithTag("btn_calculator") + .performClick() + } } \ No newline at end of file diff --git a/app/src/androidTest/java/com/ivy/wallet/compose/helpers/HomeTab.kt b/app/src/androidTest/java/com/ivy/wallet/compose/helpers/HomeTab.kt index a1fb280da4..b254d8bb67 100644 --- a/app/src/androidTest/java/com/ivy/wallet/compose/helpers/HomeTab.kt +++ b/app/src/androidTest/java/com/ivy/wallet/compose/helpers/HomeTab.kt @@ -4,6 +4,7 @@ import androidx.activity.ComponentActivity import androidx.compose.ui.test.* import androidx.compose.ui.test.junit4.AndroidComposeTestRule import androidx.test.ext.junit.rules.ActivityScenarioRule +import com.ivy.wallet.compose.printTree class HomeTab( private val composeTestRule: AndroidComposeTestRule, A> @@ -24,25 +25,46 @@ class HomeTab( account: String? = null, category: String? = null ) { - var matcher = hasTestTag("transaction_card") - .and(hasText(amount)) + var matcher = hasTestTag("type_amount_currency") + .and(hasAnyDescendant(hasText(amount))) if (account != null) { - matcher = matcher.and(hasAnyDescendant(hasText(account))) + matcher = matcher.and( + hasAnySibling( + hasAnyDescendant( + hasText(account) + ) + ) + ) } if (category != null) { - matcher = matcher.and(hasAnyDescendant(hasText(category))) + matcher = matcher.and( + hasAnySibling( + hasAnyDescendant( + hasText(category) + ) + ) + ) } if (title != null) { - matcher = matcher.and(hasText(title)) + matcher = matcher.and( + hasAnySibling( + hasText(title) + ) + ) } - composeTestRule.onNode(matcher) + composeTestRule.printTree( + useUnmergedTree = true + ) + + composeTestRule.onNode( + matcher = matcher, + useUnmergedTree = true + ) .assertIsDisplayed() - .assertHasClickAction() - .performScrollTo() .performClick() } @@ -109,4 +131,14 @@ class HomeTab( composeTestRule.onNodeWithTag("home_greeting_text", useUnmergedTree = true) .assertTextEquals(greeting) } + + fun clickIncomeCard() { + composeTestRule.onNodeWithTag("home_card_income") + .performClick() + } + + fun clickExpenseCard() { + composeTestRule.onNodeWithTag("home_card_expense") + .performClick() + } } \ No newline at end of file diff --git a/app/src/androidTest/java/com/ivy/wallet/compose/helpers/IvyColorPicker.kt b/app/src/androidTest/java/com/ivy/wallet/compose/helpers/IvyColorPicker.kt index 010f4ea57d..3da9ea5db5 100644 --- a/app/src/androidTest/java/com/ivy/wallet/compose/helpers/IvyColorPicker.kt +++ b/app/src/androidTest/java/com/ivy/wallet/compose/helpers/IvyColorPicker.kt @@ -7,15 +7,12 @@ import androidx.compose.ui.test.junit4.AndroidComposeTestRule import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performScrollTo import androidx.test.ext.junit.rules.ActivityScenarioRule -import com.ivy.wallet.compose.printTree class IvyColorPicker( private val composeTestRule: AndroidComposeTestRule, A> ) { fun chooseColor(color: Color) { - composeTestRule.printTree() - composeTestRule.onNode(hasTestTag("color_item_${color.value}")) .performScrollTo() .performClick() diff --git a/app/src/androidTest/java/com/ivy/wallet/compose/helpers/PieChartScreen.kt b/app/src/androidTest/java/com/ivy/wallet/compose/helpers/PieChartScreen.kt new file mode 100644 index 0000000000..4fc1a66658 --- /dev/null +++ b/app/src/androidTest/java/com/ivy/wallet/compose/helpers/PieChartScreen.kt @@ -0,0 +1,39 @@ +package com.ivy.wallet.compose.helpers + +import androidx.activity.ComponentActivity +import androidx.compose.ui.test.* +import androidx.compose.ui.test.junit4.AndroidComposeTestRule +import androidx.test.ext.junit.rules.ActivityScenarioRule + +class PieChartScreen( + private val composeTestRule: AndroidComposeTestRule, A> +) { + fun assertTitle(title: String) { + composeTestRule.onNodeWithTag("piechart_title") + .assertTextContains(title) + } + + fun assertTotalAmount( + amountInt: String, + decimalPart: String, + currency: String = "USD" + ) { + val matchText: (String) -> SemanticsMatcher = { text -> + hasTestTag("piechart_total_amount") + .and( + hasAnyDescendant( + hasText(text) + ) + ) + } + + composeTestRule.onNode(matchText(amountInt)) + .assertIsDisplayed() + + composeTestRule.onNode(matchText(decimalPart)) + .assertIsDisplayed() + + composeTestRule.onNode(matchText(currency)) + .assertIsDisplayed() + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/com/ivy/wallet/compose/helpers/EditTransactionScreen.kt b/app/src/androidTest/java/com/ivy/wallet/compose/helpers/TransactionScreen.kt similarity index 86% rename from app/src/androidTest/java/com/ivy/wallet/compose/helpers/EditTransactionScreen.kt rename to app/src/androidTest/java/com/ivy/wallet/compose/helpers/TransactionScreen.kt index c7f0341169..7b2da68e23 100644 --- a/app/src/androidTest/java/com/ivy/wallet/compose/helpers/EditTransactionScreen.kt +++ b/app/src/androidTest/java/com/ivy/wallet/compose/helpers/TransactionScreen.kt @@ -5,7 +5,7 @@ import androidx.compose.ui.test.* import androidx.compose.ui.test.junit4.AndroidComposeTestRule import androidx.test.ext.junit.rules.ActivityScenarioRule -class EditTransactionScreen( +class TransactionScreen( private val composeTestRule: AndroidComposeTestRule, A> ) { private val amountInput = AmountInput(composeTestRule) @@ -60,4 +60,14 @@ class EditTransactionScreen( composeTestRule.onNodeWithText("Save") .performClick() } + + fun clickAdd() { + composeTestRule.onNodeWithText("Add") + .performClick() + } + + fun skipCategory() { + composeTestRule.onNodeWithText("Skip") + .performClick() + } } \ No newline at end of file diff --git a/app/src/androidTest/java/com/ivy/wallet/compose/scenario/AccountsTest.kt b/app/src/androidTest/java/com/ivy/wallet/compose/scenario/AccountsTest.kt index 9c0e746450..d673804769 100644 --- a/app/src/androidTest/java/com/ivy/wallet/compose/scenario/AccountsTest.kt +++ b/app/src/androidTest/java/com/ivy/wallet/compose/scenario/AccountsTest.kt @@ -20,7 +20,7 @@ class AccountsTest : IvyComposeTest() { private val transactionFlow = TransactionFlow(composeTestRule) private val homeTab = HomeTab(composeTestRule) private val accountsTab = AccountsTab(composeTestRule) - private val editTransactionScreen = EditTransactionScreen(composeTestRule) + private val editTransactionScreen = TransactionScreen(composeTestRule) private val itemStatisticScreen = ItemStatisticScreen(composeTestRule) private val reorderModal = ReorderModal(composeTestRule) private val deleteConfirmationModal = DeleteConfirmationModal(composeTestRule) diff --git a/app/src/androidTest/java/com/ivy/wallet/compose/scenario/CalculatorTest.kt b/app/src/androidTest/java/com/ivy/wallet/compose/scenario/CalculatorTest.kt new file mode 100644 index 0000000000..5aee0aec5e --- /dev/null +++ b/app/src/androidTest/java/com/ivy/wallet/compose/scenario/CalculatorTest.kt @@ -0,0 +1,250 @@ +package com.ivy.wallet.compose.scenario + +import com.ivy.wallet.compose.IvyComposeTest +import com.ivy.wallet.compose.helpers.* +import dagger.hilt.android.testing.HiltAndroidTest +import org.junit.Test + +@HiltAndroidTest +class CalculatorTest : IvyComposeTest() { + private val onboarding = OnboardingFlow(composeTestRule) + private val homeTab = HomeTab(composeTestRule) + private val mainBottomBar = MainBottomBar(composeTestRule) + private val amountInput = AmountInput(composeTestRule) + private val transactionScreen = TransactionScreen(composeTestRule) + + + @Test + fun calcAmount_viaExtraction() = testWithRetry { + onboarding.quickOnboarding() + mainBottomBar.clickAddFAB() + mainBottomBar.clickAddExpense() + + //--------------------------- + amountInput.clickCalculator() + + amountInput.enterNumber( + number = "21", + onCalculator = true + ) + + amountInput.pressMinus() + + amountInput.enterNumber( + number = "3.52", + onCalculator = true + ) + + amountInput.clickCalcSet() + amountInput.clickSet() + + transactionScreen.skipCategory() + + transactionScreen.editTitle("Calc 1") + + transactionScreen.clickAdd() + + //---------------------------------------- + + homeTab.dismissPrompt() + + //21 - 3.52 = 17.48 + homeTab.assertBalance( + amount = "-17", + amountDecimal = ".48" + ) + + homeTab.clickTransaction( + amount = "17.48", + title = "Calc 1" + ) + } + + @Test + fun setAmount_withAddition() = testWithRetry { + onboarding.quickOnboarding() + mainBottomBar.clickAddFAB() + mainBottomBar.clickAddIncome() + + amountInput.enterNumber( + number = "38.16", + autoPressNonCalculator = false + ) + amountInput.clickCalculator() + + //--------------------------- + + amountInput.pressPlus() + amountInput.enterNumber( + number = "80.74", + onCalculator = true + ) + + amountInput.clickCalcSet() + amountInput.clickSet() + + transactionScreen.skipCategory() + transactionScreen.editTitle("Calc 2") + transactionScreen.clickAdd() + + //---------------------------- + + homeTab.dismissPrompt() + + //38.16 + 80.74 = 118.90 + homeTab.assertBalance( + amount = "118", + amountDecimal = ".90" + ) + + homeTab.clickTransaction( + amount = "118.90", + title = "Calc 2" + ) + } + + @Test + fun calcAmount_viaDivision() = testWithRetry { + onboarding.quickOnboarding() + mainBottomBar.clickAddFAB() + mainBottomBar.clickAddExpense() + + amountInput.clickCalculator() + + //--------------------------- + + amountInput.enterNumber( + number = "72.50", + onCalculator = true + ) + + amountInput.pressDivision() + + amountInput.enterNumber( + number = "3", + onCalculator = true + ) + + amountInput.pressCalcEqual() + + amountInput.clickCalcSet() + amountInput.clickSet() + + transactionScreen.skipCategory() + transactionScreen.editTitle("Calc 3") + + transactionScreen.clickAdd() + //---------------------------------------- + + homeTab.dismissPrompt() + + //72.50 / 3 = 24.17 + homeTab.assertBalance( + amount = "-24", + amountDecimal = ".17" + ) + + homeTab.clickTransaction( + amount = "24.17", + title = "Calc 3" + ) + } + + @Test + fun setAmount_withMultiplication_percentDiscount() = testWithRetry { + onboarding.quickOnboarding() + mainBottomBar.clickAddFAB() + mainBottomBar.clickAddIncome() + + amountInput.enterNumber( + number = "83,000.50", + autoPressNonCalculator = false + ) + amountInput.clickCalculator() + + //--------------------------- + + amountInput.pressMultiplication() + + amountInput.enterNumber( + number = "0.9", + onCalculator = true + ) + + amountInput.clickCalcSet() + amountInput.clickSet() + + transactionScreen.skipCategory() + transactionScreen.editTitle("Calc 4") + transactionScreen.clickAdd() + + //---------------------------------------- + + homeTab.dismissPrompt() + + //83,000.50 * 0.9 = 74,700.45 + homeTab.assertBalance( + amount = "74,700", + amountDecimal = ".45" + ) + + homeTab.clickTransaction( + amount = "74,700.45", + title = "Calc 4" + ) + } + + @Test + fun calcAmount_complexExpression() = testWithRetry { + onboarding.quickOnboarding() + mainBottomBar.clickAddFAB() + mainBottomBar.clickAddExpense() + + amountInput.clickCalculator() + //--------------------------- + + //(523.90+16.7-4+2345.88)*0.9*0.7 + + amountInput.pressLeftBracket() + amountInput.enterNumber("523.90", onCalculator = true) + amountInput.pressPlus() + amountInput.enterNumber("16.7", onCalculator = true) + amountInput.pressMinus() + amountInput.enterNumber("4", onCalculator = true) + amountInput.pressPlus() + amountInput.enterNumber("2345.88", onCalculator = true) + amountInput.pressRightBracket() + amountInput.pressMultiplication() + amountInput.enterNumber("0.9", onCalculator = true) + amountInput.pressMultiplication() + amountInput.enterNumber("0.7", onCalculator = true) + + + //+ 10 = + amountInput.pressCalcEqual() + amountInput.pressPlus() + amountInput.enterNumber("10", onCalculator = true) + amountInput.pressCalcEqual() + + amountInput.clickCalcSet() + amountInput.clickSet() + transactionScreen.skipCategory() + transactionScreen.editTitle("Calc Complex") + transactionScreen.clickAdd() + + //--------------------------------------------------------- + + homeTab.dismissPrompt() + + //(523.90+16.7-4+2345.88)*0.9*0.7 = 1815.9624 ; 1815.9624 + 10; = 1,825.96 + homeTab.assertBalance( + amount = "-1,825", + amountDecimal = ".96" + ) + + homeTab.clickTransaction( + amount = "1,825.96", + title = "Calc Complex" + ) + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/com/ivy/wallet/compose/scenario/OperationsCoreTest.kt b/app/src/androidTest/java/com/ivy/wallet/compose/scenario/OperationsCoreTest.kt index 9110a3f926..de300cbc56 100644 --- a/app/src/androidTest/java/com/ivy/wallet/compose/scenario/OperationsCoreTest.kt +++ b/app/src/androidTest/java/com/ivy/wallet/compose/scenario/OperationsCoreTest.kt @@ -19,7 +19,7 @@ class OperationsCoreTest : IvyComposeTest() { private val transactionFlow = TransactionFlow(composeTestRule) private val homeTab = HomeTab(composeTestRule) private val accountsTab = AccountsTab(composeTestRule) - private val editTransactionScreen = EditTransactionScreen(composeTestRule) + private val editTransactionScreen = TransactionScreen(composeTestRule) private val itemStatisticScreen = ItemStatisticScreen(composeTestRule) private val deleteConfirmationModal = DeleteConfirmationModal(composeTestRule) diff --git a/app/src/androidTest/java/com/ivy/wallet/compose/scenario/PieChartTest.kt b/app/src/androidTest/java/com/ivy/wallet/compose/scenario/PieChartTest.kt new file mode 100644 index 0000000000..91fe7e9e99 --- /dev/null +++ b/app/src/androidTest/java/com/ivy/wallet/compose/scenario/PieChartTest.kt @@ -0,0 +1,144 @@ +package com.ivy.wallet.compose.scenario + +import com.ivy.wallet.compose.IvyComposeTest +import com.ivy.wallet.compose.helpers.HomeTab +import com.ivy.wallet.compose.helpers.OnboardingFlow +import com.ivy.wallet.compose.helpers.PieChartScreen +import com.ivy.wallet.compose.helpers.TransactionFlow +import dagger.hilt.android.testing.HiltAndroidTest +import org.junit.Test + +@HiltAndroidTest +class PieChartTest : IvyComposeTest() { + private val onboarding = OnboardingFlow(composeTestRule) + private val homeTab = HomeTab(composeTestRule) + private val transactionFlow = TransactionFlow(composeTestRule) + private val pieChartScreen = PieChartScreen(composeTestRule) + + @Test + fun expensePieChart_realistic() = testWithRetry { + onboarding.quickOnboarding() + + transactionFlow.addExpense( + amount = 50.23 + ) + + transactionFlow.addExpense( + amount = 150.72, + category = "Food & Drinks" + ) + + transactionFlow.addExpense( + amount = 75.0, + category = "Groceries" + ) + + transactionFlow.addExpense( + amount = 5.0, + title = "Bread", + category = "Groceries" + ) + //---------------------------------------------------- + + homeTab.clickExpenseCard() + + pieChartScreen.assertTitle("Expenses") + pieChartScreen.assertTotalAmount( + amountInt = "280", + decimalPart = ".95", + currency = "USD" + ) + } + + @Test + fun expensePieChart_empty() = testWithRetry { + onboarding.quickOnboarding() + + transactionFlow.addIncome( + amount = 23.23 + ) + + //---------------------------------------------------- + + homeTab.clickExpenseCard() + + pieChartScreen.assertTitle("Expenses") + pieChartScreen.assertTotalAmount( + amountInt = "0", + decimalPart = ".00", + currency = "USD" + ) + } + + @Test + fun expensePieChart_oneTrn() = testWithRetry { + onboarding.quickOnboarding() + + transactionFlow.addExpense( + amount = 55.01 + ) + + //---------------------------------------------------- + + homeTab.clickExpenseCard() + + pieChartScreen.assertTitle("Expenses") + pieChartScreen.assertTotalAmount( + amountInt = "55", + decimalPart = ".01", + currency = "USD" + ) + } + + @Test + fun incomePieChart_realistic() = testWithRetry { + onboarding.quickOnboarding() + + //To ensure that the code filters expenses + transactionFlow.addExpense( + amount = 10.0 + ) + + transactionFlow.addIncome( + amount = 7200.0, + title = "Salary", + category = "Groceries" + ) + + transactionFlow.addIncome( + amount = 1.1, + title = "Adjust balance" + ) + + //---------------------------------------------------- + + homeTab.clickIncomeCard() + + pieChartScreen.assertTitle("Income") + pieChartScreen.assertTotalAmount( + amountInt = "7,201", + decimalPart = ".10", + currency = "USD" + ) + } + + @Test + fun incomePieChart_empty() = testWithRetry { + onboarding.quickOnboarding() + + transactionFlow.addExpense( + amount = 23.23 + ) + + //---------------------------------------------------- + + homeTab.clickIncomeCard() + + pieChartScreen.assertTitle("Income") + pieChartScreen.assertTotalAmount( + amountInt = "0", + decimalPart = ".00", + currency = "USD" + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/AppModuleDI.kt b/app/src/main/java/com/ivy/wallet/AppModuleDI.kt index 502a42c43d..d204a1baf9 100644 --- a/app/src/main/java/com/ivy/wallet/AppModuleDI.kt +++ b/app/src/main/java/com/ivy/wallet/AppModuleDI.kt @@ -6,23 +6,19 @@ import com.google.gson.GsonBuilder import com.ivy.design.navigation.Navigation import com.ivy.wallet.android.billing.IvyBilling import com.ivy.wallet.android.notification.NotificationService -import com.ivy.wallet.domain.fp.data.WalletDAOs -import com.ivy.wallet.domain.logic.* -import com.ivy.wallet.domain.logic.bankintegrations.BankIntegrationsLogic -import com.ivy.wallet.domain.logic.bankintegrations.SaltEdgeAccountMapper -import com.ivy.wallet.domain.logic.bankintegrations.SaltEdgeCategoryMapper -import com.ivy.wallet.domain.logic.bankintegrations.SaltEdgeTransactionMapper -import com.ivy.wallet.domain.logic.csv.* -import com.ivy.wallet.domain.logic.currency.ExchangeRatesLogic -import com.ivy.wallet.domain.logic.loantrasactions.LTLoanMapper -import com.ivy.wallet.domain.logic.loantrasactions.LTLoanRecordMapper -import com.ivy.wallet.domain.logic.loantrasactions.LoanTransactionsCore -import com.ivy.wallet.domain.logic.loantrasactions.LoanTransactionsLogic -import com.ivy.wallet.domain.logic.notification.TransactionReminderLogic -import com.ivy.wallet.domain.logic.zip.ExportZipLogic -import com.ivy.wallet.domain.sync.IvySync -import com.ivy.wallet.domain.sync.item.* -import com.ivy.wallet.domain.sync.uploader.* +import com.ivy.wallet.domain.deprecated.logic.* +import com.ivy.wallet.domain.deprecated.logic.csv.* +import com.ivy.wallet.domain.deprecated.logic.currency.ExchangeRatesLogic +import com.ivy.wallet.domain.deprecated.logic.loantrasactions.LTLoanMapper +import com.ivy.wallet.domain.deprecated.logic.loantrasactions.LTLoanRecordMapper +import com.ivy.wallet.domain.deprecated.logic.loantrasactions.LoanTransactionsCore +import com.ivy.wallet.domain.deprecated.logic.loantrasactions.LoanTransactionsLogic +import com.ivy.wallet.domain.deprecated.logic.notification.TransactionReminderLogic +import com.ivy.wallet.domain.deprecated.logic.zip.ExportZipLogic +import com.ivy.wallet.domain.deprecated.sync.IvySync +import com.ivy.wallet.domain.deprecated.sync.item.* +import com.ivy.wallet.domain.deprecated.sync.uploader.* +import com.ivy.wallet.domain.pure.data.WalletDAOs import com.ivy.wallet.io.network.* import com.ivy.wallet.io.network.error.ErrorCode import com.ivy.wallet.io.persistence.IvyRoomDatabase @@ -117,9 +113,6 @@ object AppModuleDI { @Provides fun provideBudgetDao(db: IvyRoomDatabase): BudgetDao = db.budgetDao() - @Provides - fun provideWishlistItemDao(db: IvyRoomDatabase): WishlistItemDao = db.wishlistItemDao() - @Provides fun provideSettingsDao(db: IvyRoomDatabase): SettingsDao = db.settingsDao() @@ -133,18 +126,6 @@ object AppModuleDI { fun provideTrnRecurringRuleDao(db: IvyRoomDatabase): PlannedPaymentRuleDao = db.plannedPaymentRuleDao() - @Provides - fun provideWalletLogic( - accountDao: AccountDao, - transactionDao: TransactionDao, - settingsDao: SettingsDao, - exchangeRatesLogic: ExchangeRatesLogic, - ): WalletLogic = WalletLogic( - accountDao = accountDao, - transactionDao = transactionDao, - settingsDao = settingsDao, - exchangeRatesLogic = exchangeRatesLogic, - ) @Provides fun provideWalletAccountLogic( @@ -658,56 +639,6 @@ object AppModuleDI { ) } - @Provides - fun provideSaltEdgeLogic( - restClient: RestClient, - seTransactionsMapper: SaltEdgeTransactionMapper, - ivySession: IvySession, - sharedPrefs: SharedPrefs - ): BankIntegrationsLogic { - return BankIntegrationsLogic( - restClient = restClient, - seTransactionMapper = seTransactionsMapper, - ivySession = ivySession, - sharedPrefs = sharedPrefs - ) - } - - @Provides - fun provideSeTransactionMapper( - transactionDao: TransactionDao, - seAccountMapper: SaltEdgeAccountMapper, - seCategoryMapper: SaltEdgeCategoryMapper, - accountDao: AccountDao, - walletAccountLogic: WalletAccountLogic - ): SaltEdgeTransactionMapper { - return SaltEdgeTransactionMapper( - transactionDao = transactionDao, - seAccountMapper = seAccountMapper, - seCategoryMapper = seCategoryMapper, - accountDao = accountDao, - walletAccountLogic = walletAccountLogic - ) - } - - @Provides - fun provideSeAccountMapper( - accountDao: AccountDao - ): SaltEdgeAccountMapper { - return SaltEdgeAccountMapper( - accountDao = accountDao - ) - } - - @Provides - fun provideSeCategoryMapper( - categoryDao: CategoryDao - ): SaltEdgeCategoryMapper { - return SaltEdgeCategoryMapper( - categoryDao = categoryDao - ) - } - @Provides fun provideCustomerJourneyLogic( transactionDao: TransactionDao, diff --git a/app/src/main/java/com/ivy/wallet/IvyAndroidApp.kt b/app/src/main/java/com/ivy/wallet/IvyAndroidApp.kt index 19056f9671..276abff9ea 100644 --- a/app/src/main/java/com/ivy/wallet/IvyAndroidApp.kt +++ b/app/src/main/java/com/ivy/wallet/IvyAndroidApp.kt @@ -1,6 +1,9 @@ package com.ivy.wallet +import android.annotation.SuppressLint import android.app.Application +import android.content.Context +import androidx.annotation.StringRes import androidx.hilt.work.HiltWorkerFactory import androidx.work.Configuration import dagger.hilt.android.HiltAndroidApp @@ -13,6 +16,11 @@ import javax.inject.Inject */ @HiltAndroidApp class IvyAndroidApp : Application(), Configuration.Provider { + companion object { + @SuppressLint("StaticFieldLeak") + lateinit var appContext: Context + } + @Inject lateinit var workerFactory: HiltWorkerFactory @@ -25,8 +33,18 @@ class IvyAndroidApp : Application(), Configuration.Provider { override fun onCreate() { super.onCreate() + appContext = this + if (BuildConfig.DEBUG) { Timber.plant(DebugTree()) } } +} + +fun stringRes( + @StringRes id: Int, + vararg args: String +): String { + //I don't want strings.xml to handle something different than String at this point + return IvyAndroidApp.appContext.getString(id, *args) } \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/IvyWalletCompleteData.kt b/app/src/main/java/com/ivy/wallet/domain/IvyWalletCompleteData.kt deleted file mode 100644 index 98ce5b1ee4..0000000000 --- a/app/src/main/java/com/ivy/wallet/domain/IvyWalletCompleteData.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.ivy.wallet.domain - -import com.ivy.wallet.domain.data.entity.* - -data class IvyWalletCompleteData( - val accounts: List = emptyList(), - val budgets: List = emptyList(), - val categories: List = emptyList(), - val loanRecords: List = emptyList(), - val loans: List = emptyList(), - val plannedPaymentRules: List = emptyList(), - val settings: List = emptyList(), - val transactions: List = emptyList(), - val sharedPrefs: HashMap = HashMap() -) \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/action/CalcOverdueAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/CalcOverdueAct.kt deleted file mode 100644 index 858b24f7f8..0000000000 --- a/app/src/main/java/com/ivy/wallet/domain/action/CalcOverdueAct.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.ivy.wallet.domain.action - -import com.ivy.wallet.domain.data.entity.Transaction -import com.ivy.wallet.domain.fp.data.IncomeExpensePair -import com.ivy.wallet.domain.logic.WalletLogic -import com.ivy.wallet.ui.onboarding.model.FromToTimeRange -import javax.inject.Inject - -class CalcOverdueAct @Inject constructor( - private val walletLogic: WalletLogic -) : Action() { - - override suspend fun FromToTimeRange.willDo(): Output = io { - //TODO: Rework & optimize this - Output( - overdue = IncomeExpensePair( - income = walletLogic.calculateOverdueIncome(this).toBigDecimal(), - expense = walletLogic.calculateOverdueExpenses(this).toBigDecimal() - ), - overdueTrns = walletLogic.overdueTransactions(this) - ) - } - - data class Output( - val overdue: IncomeExpensePair, - val overdueTrns: List - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/action/CalcUpcomingAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/CalcUpcomingAct.kt deleted file mode 100644 index 5c6c92fbc1..0000000000 --- a/app/src/main/java/com/ivy/wallet/domain/action/CalcUpcomingAct.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.ivy.wallet.domain.action - -import com.ivy.wallet.domain.data.entity.Transaction -import com.ivy.wallet.domain.fp.data.IncomeExpensePair -import com.ivy.wallet.domain.logic.WalletLogic -import com.ivy.wallet.ui.onboarding.model.FromToTimeRange -import javax.inject.Inject - -class CalcUpcomingAct @Inject constructor( - private val walletLogic: WalletLogic -) : Action() { - - override suspend fun FromToTimeRange.willDo(): Output = io { - //TODO: Rework & optimize this - Output( - upcoming = IncomeExpensePair( - income = walletLogic.calculateUpcomingIncome(this).toBigDecimal(), - expense = walletLogic.calculateUpcomingExpenses(this).toBigDecimal() - ), - upcomingTrns = walletLogic.upcomingTransactions(this) - ) - } - - data class Output( - val upcoming: IncomeExpensePair, - val upcomingTrns: List - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/action/CalcWalletBalanceAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/CalcWalletBalanceAct.kt deleted file mode 100644 index 1a1a2b1e03..0000000000 --- a/app/src/main/java/com/ivy/wallet/domain/action/CalcWalletBalanceAct.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.ivy.wallet.domain.action - -import com.ivy.wallet.domain.fp.data.WalletDAOs -import com.ivy.wallet.domain.fp.wallet.calculateWalletBalance -import java.math.BigDecimal -import javax.inject.Inject - -class CalcWalletBalanceAct @Inject constructor( - private val walletDAOs: WalletDAOs, -) : Action() { - override suspend fun String.willDo(): BigDecimal = io { - val baseCurrency = this - calculateWalletBalance( - walletDAOs = walletDAOs, - baseCurrencyCode = baseCurrency - ).value - } -} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/action/GetBaseCurrencyAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/GetBaseCurrencyAct.kt deleted file mode 100644 index 99773fb324..0000000000 --- a/app/src/main/java/com/ivy/wallet/domain/action/GetBaseCurrencyAct.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.ivy.wallet.domain.action - -import com.ivy.wallet.domain.fp.wallet.baseCurrencyCode -import com.ivy.wallet.io.persistence.dao.SettingsDao -import javax.inject.Inject - -class GetBaseCurrencyAct @Inject constructor( - private val settingsDao: SettingsDao -) : Action() { - override suspend fun Unit.willDo(): String = io { - baseCurrencyCode(settingsDao) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/action/HistoryWithDateDivAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/HistoryWithDateDivAct.kt deleted file mode 100644 index c3cb7a4673..0000000000 --- a/app/src/main/java/com/ivy/wallet/domain/action/HistoryWithDateDivAct.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.ivy.wallet.domain.action - -import com.ivy.wallet.domain.data.TransactionHistoryItem -import com.ivy.wallet.domain.fp.data.ClosedTimeRange -import com.ivy.wallet.domain.fp.data.WalletDAOs -import com.ivy.wallet.domain.fp.wallet.historyWithDateDividers -import javax.inject.Inject - -class HistoryWithDateDivAct @Inject constructor( - private val walletDAOs: WalletDAOs -) : Action>() { - - override suspend fun Input.willDo(): List = io { - historyWithDateDividers( - walletDAOs = walletDAOs, - baseCurrencyCode = baseCurrencyCode, - range = timeRange - ) - } - - data class Input( - val timeRange: ClosedTimeRange, - val baseCurrencyCode: String - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/action/account/AccTrnsAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/account/AccTrnsAct.kt new file mode 100644 index 0000000000..97c86a0972 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/action/account/AccTrnsAct.kt @@ -0,0 +1,35 @@ +package com.ivy.wallet.domain.action.account + +import com.ivy.fp.action.FPAction +import com.ivy.fp.action.thenMap +import com.ivy.wallet.domain.data.core.Transaction +import com.ivy.wallet.domain.pure.data.ClosedTimeRange +import com.ivy.wallet.io.persistence.dao.TransactionDao +import java.util.* +import javax.inject.Inject + +class AccTrnsAct @Inject constructor( + private val transactionDao: TransactionDao +) : FPAction>() { + override suspend fun Input.compose(): suspend () -> List = suspend { + io { + transactionDao.findAllByAccountAndBetween( + accountId = accountId, + startDate = range.from, + endDate = range.to + ) + transactionDao.findAllToAccountAndBetween( + toAccountId = accountId, + startDate = range.from, + endDate = range.to + ) + } + } thenMap { + it.toDomain() + } + + class Input( + val accountId: UUID, + val range: ClosedTimeRange + ) +} + diff --git a/app/src/main/java/com/ivy/wallet/domain/action/account/AccountByIdAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/account/AccountByIdAct.kt new file mode 100644 index 0000000000..da343e503a --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/action/account/AccountByIdAct.kt @@ -0,0 +1,18 @@ +package com.ivy.wallet.domain.action.account + +import com.ivy.fp.action.FPAction +import com.ivy.fp.action.then +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.io.persistence.dao.AccountDao +import java.util.* +import javax.inject.Inject + +class AccountByIdAct @Inject constructor( + private val accountDao: AccountDao +) : FPAction() { + override suspend fun UUID.compose(): suspend () -> Account? = suspend { + this //accountId + } then accountDao::findById then { + it?.toDomain() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/action/account/AccountsAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/account/AccountsAct.kt new file mode 100644 index 0000000000..f199a896ba --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/action/account/AccountsAct.kt @@ -0,0 +1,15 @@ +package com.ivy.wallet.domain.action.account + +import com.ivy.fp.action.FPAction +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.io.persistence.dao.AccountDao +import javax.inject.Inject + +class AccountsAct @Inject constructor( + private val accountDao: AccountDao +) : FPAction>() { + + override suspend fun Unit.compose(): suspend () -> List = suspend { + io { accountDao.findAll().map { it.toDomain() } } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/action/account/CalcAccBalanceAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/account/CalcAccBalanceAct.kt new file mode 100644 index 0000000000..04dc6c1112 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/action/account/CalcAccBalanceAct.kt @@ -0,0 +1,44 @@ +package com.ivy.wallet.domain.action.account + +import arrow.core.nonEmptyListOf +import com.ivy.fp.action.FPAction +import com.ivy.fp.action.then +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.pure.data.ClosedTimeRange +import com.ivy.wallet.domain.pure.transaction.AccountValueFunctions +import com.ivy.wallet.domain.pure.transaction.foldTransactions +import java.math.BigDecimal +import javax.inject.Inject + +class CalcAccBalanceAct @Inject constructor( + private val accTrnsAct: AccTrnsAct +) : FPAction() { + + override suspend fun Input.compose(): suspend () -> Output = suspend { + AccTrnsAct.Input( + accountId = account.id, + range = range + ) + } then accTrnsAct then { accTrns -> + foldTransactions( + transactions = accTrns, + arg = account.id, + valueFunctions = nonEmptyListOf(AccountValueFunctions::balance) + ).head + } then { balance -> + Output( + account = account, + balance = balance + ) + } + + data class Input( + val account: Account, + val range: ClosedTimeRange = ClosedTimeRange.allTimeIvy() + ) + + data class Output( + val account: Account, + val balance: BigDecimal, + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/action/account/CalcAccIncomeExpenseAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/account/CalcAccIncomeExpenseAct.kt new file mode 100644 index 0000000000..7071e968a4 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/action/account/CalcAccIncomeExpenseAct.kt @@ -0,0 +1,54 @@ +package com.ivy.wallet.domain.action.account + +import arrow.core.nonEmptyListOf +import com.ivy.fp.action.FPAction +import com.ivy.fp.action.then +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.pure.data.ClosedTimeRange +import com.ivy.wallet.domain.pure.data.IncomeExpensePair +import com.ivy.wallet.domain.pure.transaction.AccountValueFunctions +import com.ivy.wallet.domain.pure.transaction.foldTransactions +import java.math.BigDecimal +import javax.inject.Inject + +class CalcAccIncomeExpenseAct @Inject constructor( + private val accTrnsAct: AccTrnsAct +) : FPAction() { + + override suspend fun Input.compose(): suspend () -> Output = suspend { + AccTrnsAct.Input( + accountId = account.id, + range = range + ) + } then accTrnsAct then { accTrns -> + foldTransactions( + transactions = accTrns, + arg = account.id, + valueFunctions = nonEmptyListOf( + AccountValueFunctions::income, + AccountValueFunctions::expense, + AccountValueFunctions::transferIncome, + AccountValueFunctions::transferExpense + ) + ) + } then { values -> + Output( + account = account, + incomeExpensePair = IncomeExpensePair( + income = values[0] + if (includeTransfersInCalc) values[2] else BigDecimal.ZERO, + expense = values[1] + if (includeTransfersInCalc) values[3] else BigDecimal.ZERO + ) + ) + } + + data class Input( + val account: Account, + val range: ClosedTimeRange = ClosedTimeRange.allTimeIvy(), + val includeTransfersInCalc: Boolean = false + ) + + data class Output( + val account: Account, + val incomeExpensePair: IncomeExpensePair + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/action/budget/BudgetsAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/budget/BudgetsAct.kt new file mode 100644 index 0000000000..46c713f841 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/action/budget/BudgetsAct.kt @@ -0,0 +1,15 @@ +package com.ivy.wallet.domain.action.budget + +import com.ivy.fp.action.FPAction +import com.ivy.fp.action.thenMap +import com.ivy.wallet.domain.data.core.Budget +import com.ivy.wallet.io.persistence.dao.BudgetDao +import javax.inject.Inject + +class BudgetsAct @Inject constructor( + private val budgetDao: BudgetDao +) : FPAction>() { + override suspend fun Unit.compose(): suspend () -> List = suspend { + budgetDao.findAll() + } thenMap { it.toDomain() } +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/action/category/CategoriesAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/category/CategoriesAct.kt new file mode 100644 index 0000000000..f0c5869cdd --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/action/category/CategoriesAct.kt @@ -0,0 +1,17 @@ +package com.ivy.wallet.domain.action.category + +import com.ivy.fp.action.FPAction +import com.ivy.fp.action.thenMap +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.io.persistence.dao.CategoryDao +import javax.inject.Inject + +class CategoriesAct @Inject constructor( + private val categoryDao: CategoryDao +) : FPAction>() { + override suspend fun Unit.compose(): suspend () -> List = suspend { + io { + categoryDao.findAll() + } + } thenMap { it.toDomain() } +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/action/category/CategoryByIdAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/category/CategoryByIdAct.kt new file mode 100644 index 0000000000..00beafbf43 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/action/category/CategoryByIdAct.kt @@ -0,0 +1,15 @@ +package com.ivy.wallet.domain.action.category + +import com.ivy.fp.action.FPAction +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.io.persistence.dao.CategoryDao +import java.util.* +import javax.inject.Inject + +class CategoryByIdAct @Inject constructor( + private val categoryDao: CategoryDao +) : FPAction() { + override suspend fun UUID.compose(): suspend () -> Category? = suspend { + categoryDao.findById(this)?.toDomain() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/action/category/CategoryIncomeWithAccountFiltersAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/category/CategoryIncomeWithAccountFiltersAct.kt new file mode 100644 index 0000000000..6cb4af6afc --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/action/category/CategoryIncomeWithAccountFiltersAct.kt @@ -0,0 +1,40 @@ +package com.ivy.wallet.domain.action.category + +import com.ivy.fp.action.FPAction +import com.ivy.fp.action.then +import com.ivy.wallet.domain.action.transaction.CalcTrnsIncomeExpenseAct +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.data.core.Transaction +import com.ivy.wallet.domain.pure.data.IncomeExpenseTransferPair +import javax.inject.Inject + +class CategoryIncomeWithAccountFiltersAct @Inject constructor( + private val calcTrnsIncomeExpenseAct: CalcTrnsIncomeExpenseAct +) : FPAction() { + + override suspend fun Input.compose(): suspend () -> IncomeExpenseTransferPair = { + val accountFilterSet = accountFilterList.map { it.id }.toHashSet() + transactions.filter { + it.categoryId == category?.id + }.filter { + if (accountFilterSet.isEmpty()) + true + else + accountFilterSet.contains(it.accountId) + } + } then { + CalcTrnsIncomeExpenseAct.Input( + transactions = it, + baseCurrency = baseCurrency, + accounts = accountFilterList + ) + } then calcTrnsIncomeExpenseAct + + data class Input( + val transactions: List, + val accountFilterList: List, + val category: Category?, + val baseCurrency: String + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/action/category/CategoryTrnsBetweenAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/category/CategoryTrnsBetweenAct.kt new file mode 100644 index 0000000000..1303090ac9 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/action/category/CategoryTrnsBetweenAct.kt @@ -0,0 +1,37 @@ +package com.ivy.wallet.domain.action.category + +import com.ivy.fp.action.FPAction +import com.ivy.fp.action.thenMap +import com.ivy.wallet.domain.data.core.Transaction +import com.ivy.wallet.domain.pure.data.ClosedTimeRange +import com.ivy.wallet.io.persistence.dao.TransactionDao +import java.util.* +import javax.inject.Inject + +class CategoryTrnsBetweenAct @Inject constructor( + private val transactionDao: TransactionDao +) : FPAction>() { + + override suspend fun Input.compose(): suspend () -> List = suspend { + io { + transactionDao.findAllByCategoryAndBetween( + startDate = between.from, + endDate = between.to, + categoryId = categoryId + ) + } + } thenMap { it.toDomain() } + + data class Input( + val categoryId: UUID, + val between: ClosedTimeRange + ) +} + +fun actInput( + categoryId: UUID, + between: ClosedTimeRange +) = CategoryTrnsBetweenAct.Input( + categoryId = categoryId, + between = between +) \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/action/charts/BalanceChartAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/charts/BalanceChartAct.kt new file mode 100644 index 0000000000..9f5a570121 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/action/charts/BalanceChartAct.kt @@ -0,0 +1,34 @@ +package com.ivy.wallet.domain.action.charts + +import com.ivy.fp.action.FPAction +import com.ivy.wallet.domain.action.wallet.CalcWalletBalanceAct +import com.ivy.wallet.domain.pure.charts.ChartPeriod +import com.ivy.wallet.domain.pure.charts.SingleChartPoint +import com.ivy.wallet.domain.pure.charts.balanceChart +import javax.inject.Inject + +class BalanceChartAct @Inject constructor( + private val calcWalletBalanceAct: CalcWalletBalanceAct +) : FPAction>() { + + override suspend fun Input.compose(): suspend () -> List = suspend { + io { + balanceChart( + period = period, + calcWalletBalance = { range -> + calcWalletBalanceAct( + CalcWalletBalanceAct.Input( + baseCurrency = baseCurrency, + range = range + ) + ) + } + ) + } + } + + data class Input( + val baseCurrency: String, + val period: ChartPeriod + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/action/charts/PieChartAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/charts/PieChartAct.kt new file mode 100644 index 0000000000..eef14e364c --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/action/charts/PieChartAct.kt @@ -0,0 +1,241 @@ +package com.ivy.wallet.domain.action.charts + +import androidx.compose.ui.graphics.toArgb +import com.ivy.fp.Pure +import com.ivy.fp.SideEffect +import com.ivy.fp.action.FPAction +import com.ivy.fp.action.then +import com.ivy.fp.action.thenFilter +import com.ivy.fp.action.thenMap +import com.ivy.wallet.R +import com.ivy.wallet.domain.action.account.AccountsAct +import com.ivy.wallet.domain.action.category.CategoriesAct +import com.ivy.wallet.domain.action.category.CategoryIncomeWithAccountFiltersAct +import com.ivy.wallet.domain.action.transaction.CalcTrnsIncomeExpenseAct +import com.ivy.wallet.domain.action.transaction.TrnsWithRangeAndAccFiltersAct +import com.ivy.wallet.domain.data.TransactionType +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.data.core.Transaction +import com.ivy.wallet.domain.pure.account.filterExcluded +import com.ivy.wallet.domain.pure.data.IncomeExpenseTransferPair +import com.ivy.wallet.stringRes +import com.ivy.wallet.ui.onboarding.model.FromToTimeRange +import com.ivy.wallet.ui.statistic.level1.CategoryAmount +import com.ivy.wallet.ui.theme.RedLight +import java.math.BigDecimal +import java.util.* +import javax.inject.Inject + +class PieChartAct @Inject constructor( + private val accountsAct: AccountsAct, + private val trnsWithRangeAndAccFiltersAct: TrnsWithRangeAndAccFiltersAct, + private val calcTrnsIncomeExpenseAct: CalcTrnsIncomeExpenseAct, + private val categoriesAct: CategoriesAct, + private val categoryIncomeWithAccountFiltersAct: CategoryIncomeWithAccountFiltersAct +) : FPAction() { + + private val accountTransfersCategory = + Category(stringRes(R.string.account_transfers), RedLight.toArgb(), "transfer") + + override suspend fun Input.compose(): suspend () -> Output = suspend { + getUsableAccounts( + accountIdFilterList = accountIdFilterList, + allAccounts = suspend { accountsAct(Unit) } + ) + } then { + val accountsUsed = it.first + val accountIdFilterSet = it.second + + val transactions = trnsWithRangeAndAccFiltersAct( + TrnsWithRangeAndAccFiltersAct.Input( + range = range, + accountIdFilterSet = accountIdFilterSet + ) + ) + + Pair(accountsUsed, transactions) + } then { + val accountsUsed = it.first + val transactions = it.second + + val incomeExpenseTransfer = calcTrnsIncomeExpenseAct( + CalcTrnsIncomeExpenseAct.Input( + transactions = transactions, + accounts = accountsUsed, + baseCurrency = baseCurrency + ) + ) + + val categoryAmounts = calculateCategoryAmounts( + type = type, + baseCurrency = baseCurrency, + allCategories = suspend { + categoriesAct(Unit).plus(null) //for unspecified + }, + transactions = suspend { transactions }, + accountsUsed = suspend { accountsUsed } + ) + + Pair(incomeExpenseTransfer, categoryAmounts) + } then { + + val totalAmount = calculateTotalAmount( + type = type, + treatTransferAsIncExp = treatTransferAsIncExp, + incomeExpenseTransfer = suspend { it.first } + ) + + val catAmountList = addAccountTransfersCategory( + treatTransferAsIncExp = treatTransferAsIncExp, + type = type, + incomeExpenseTransfer = suspend { it.first }, + accountTransfersCategory = accountTransfersCategory, + categoryAmounts = suspend { it.second } + ) + + Pair(totalAmount, catAmountList) + } then { + Output(it.first.toDouble(), it.second) + } + + @Pure + private suspend fun getUsableAccounts( + accountIdFilterList: List, + + @SideEffect + allAccounts: suspend () -> List + ): Pair, Set> { + + val accountsUsed = if (accountIdFilterList.isEmpty()) + allAccounts then ::filterExcluded + else + allAccounts thenFilter { + accountIdFilterList.contains(it.id) + } + + val accountsUsedIDSet = accountsUsed thenMap { it.id } then { it.toHashSet() } + + return Pair(accountsUsed(), accountsUsedIDSet()) + } + + @Pure + private suspend fun calculateCategoryAmounts( + type: TransactionType, + baseCurrency: String, + + @SideEffect + allCategories: suspend () -> List, + + @SideEffect + transactions: suspend () -> List, + + @SideEffect + accountsUsed: suspend () -> List, + ): List { + val trans = transactions() + val accUsed = accountsUsed() + + val catAmtList = allCategories thenMap { category -> + val catIncomeExpense = categoryIncomeWithAccountFiltersAct( + CategoryIncomeWithAccountFiltersAct.Input( + transactions = trans, + accountFilterList = accUsed, + category = category, + baseCurrency = baseCurrency + ) + ) + + CategoryAmount( + category = category, + amount = when (type) { + TransactionType.INCOME -> catIncomeExpense.income.toDouble() + TransactionType.EXPENSE -> catIncomeExpense.expense.toDouble() + else -> error("not supported transactionType - $type") + } + ) + } thenFilter { catAmt -> + catAmt.amount != 0.0 + } then { + it.sortedByDescending { ca -> ca.amount } + } + + return catAmtList() + } + + @Pure + private suspend fun calculateTotalAmount( + type: TransactionType, + treatTransferAsIncExp: Boolean, + + @SideEffect + incomeExpenseTransfer: suspend () -> IncomeExpenseTransferPair + ): BigDecimal { + val incExpQuad = incomeExpenseTransfer() + return when (type) { + TransactionType.INCOME -> { + incExpQuad.income + + if (treatTransferAsIncExp) + incExpQuad.transferIncome + else + BigDecimal.ZERO + } + TransactionType.EXPENSE -> { + incExpQuad.expense + + if (treatTransferAsIncExp) + incExpQuad.transferExpense + else + BigDecimal.ZERO + } + else -> BigDecimal.ZERO + } + } + + @Pure + private suspend fun addAccountTransfersCategory( + treatTransferAsIncExp: Boolean, + type: TransactionType, + accountTransfersCategory: Category, + + @SideEffect + incomeExpenseTransfer: suspend () -> IncomeExpenseTransferPair, + + @SideEffect + categoryAmounts: suspend () -> List + ): List { + + val incExpQuad = incomeExpenseTransfer() + + val catAmtList = + if (!treatTransferAsIncExp || incExpQuad.transferIncome == BigDecimal.ZERO && incExpQuad.transferExpense == BigDecimal.ZERO) + categoryAmounts then { it.sortedByDescending { ca -> ca.amount } } + else { + + val amt = if (type == TransactionType.INCOME) + incExpQuad.transferIncome.toDouble() + else + incExpQuad.transferExpense.toDouble() + + + categoryAmounts then { + it.plus( + CategoryAmount(accountTransfersCategory, amt) + ) + } then { + it.sortedByDescending { ca -> ca.amount } + } + } + + return catAmtList() + } + + data class Input( + val baseCurrency: String, + val range: FromToTimeRange, + val type: TransactionType, + val accountIdFilterList: List, + val treatTransferAsIncExp: Boolean = false + ) + + data class Output(val totalAmount: Double, val categoryAmounts: List) +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/action/exchange/ExchangeAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/exchange/ExchangeAct.kt new file mode 100644 index 0000000000..7906d11a7b --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/action/exchange/ExchangeAct.kt @@ -0,0 +1,39 @@ +package com.ivy.wallet.domain.action.exchange + +import arrow.core.Option +import com.ivy.fp.action.FPAction +import com.ivy.fp.then +import com.ivy.wallet.domain.pure.exchange.ExchangeData +import com.ivy.wallet.domain.pure.exchange.exchange +import com.ivy.wallet.io.persistence.dao.ExchangeRateDao +import java.math.BigDecimal +import javax.inject.Inject + +class ExchangeAct @Inject constructor( + private val exchangeRateDao: ExchangeRateDao, +) : FPAction>() { + override suspend fun Input.compose(): suspend () -> Option = suspend { + io { + exchange( + data = data, + amount = amount, + getExchangeRate = exchangeRateDao::findByBaseCurrencyAndCurrency then { + it?.toDomain() + } + ) + } + } + + data class Input( + val data: ExchangeData, + val amount: BigDecimal + ) +} + +fun actInput( + data: ExchangeData, + amount: BigDecimal +): ExchangeAct.Input = ExchangeAct.Input( + data = data, + amount = amount +) diff --git a/app/src/main/java/com/ivy/wallet/domain/action/loan/LoanByIdAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/loan/LoanByIdAct.kt new file mode 100644 index 0000000000..9e771cc9cd --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/action/loan/LoanByIdAct.kt @@ -0,0 +1,15 @@ +package com.ivy.wallet.domain.action.loan + +import com.ivy.fp.action.FPAction +import com.ivy.wallet.domain.data.core.Loan +import com.ivy.wallet.io.persistence.dao.LoanDao +import java.util.* +import javax.inject.Inject + +class LoanByIdAct @Inject constructor( + private val loanDao: LoanDao +) : FPAction() { + override suspend fun UUID.compose(): suspend () -> Loan? = suspend { + loanDao.findById(this)?.toDomain() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/action/loan/LoansAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/loan/LoansAct.kt new file mode 100644 index 0000000000..86c2c8fc23 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/action/loan/LoansAct.kt @@ -0,0 +1,15 @@ +package com.ivy.wallet.domain.action.loan + +import com.ivy.fp.action.FPAction +import com.ivy.fp.action.thenMap +import com.ivy.wallet.domain.data.core.Loan +import com.ivy.wallet.io.persistence.dao.LoanDao +import javax.inject.Inject + +class LoansAct @Inject constructor( + private val loanDao: LoanDao +) : FPAction>() { + override suspend fun Unit.compose(): suspend () -> List = suspend { + loanDao.findAll() + } thenMap { it.toDomain() } +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/action/settings/BaseCurrencyAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/settings/BaseCurrencyAct.kt new file mode 100644 index 0000000000..6d989e8ff4 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/action/settings/BaseCurrencyAct.kt @@ -0,0 +1,13 @@ +package com.ivy.wallet.domain.action.settings + +import com.ivy.fp.action.FPAction +import com.ivy.wallet.io.persistence.dao.SettingsDao +import javax.inject.Inject + +class BaseCurrencyAct @Inject constructor( + private val settingsDao: SettingsDao +) : FPAction() { + override suspend fun Unit.compose(): suspend () -> String = suspend { + io { settingsDao.findFirst().currency } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/action/settings/CalcBufferDiffAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/settings/CalcBufferDiffAct.kt new file mode 100644 index 0000000000..845362449d --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/action/settings/CalcBufferDiffAct.kt @@ -0,0 +1,17 @@ +package com.ivy.wallet.domain.action.settings + +import com.ivy.fp.action.FPAction +import java.math.BigDecimal +import javax.inject.Inject + +class CalcBufferDiffAct @Inject constructor() : FPAction() { + + override suspend fun Input.compose(): suspend () -> BigDecimal = { + balance - buffer + } + + data class Input( + val balance: BigDecimal, + val buffer: BigDecimal + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/action/settings/SettingsAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/settings/SettingsAct.kt new file mode 100644 index 0000000000..e354ffb166 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/action/settings/SettingsAct.kt @@ -0,0 +1,15 @@ +package com.ivy.wallet.domain.action.settings + +import com.ivy.fp.action.FPAction +import com.ivy.fp.action.then +import com.ivy.wallet.domain.data.core.Settings +import com.ivy.wallet.io.persistence.dao.SettingsDao +import javax.inject.Inject + +class SettingsAct @Inject constructor( + private val settingsDao: SettingsDao +) : FPAction() { + override suspend fun Unit.compose(): suspend () -> Settings = suspend { + io { settingsDao.findFirst() } + } then { it.toDomain() } +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/action/transaction/AllTrnsAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/transaction/AllTrnsAct.kt new file mode 100644 index 0000000000..0ce113479e --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/action/transaction/AllTrnsAct.kt @@ -0,0 +1,15 @@ +package com.ivy.wallet.domain.action.transaction + +import com.ivy.fp.action.FPAction +import com.ivy.fp.action.thenMap +import com.ivy.wallet.domain.data.core.Transaction +import com.ivy.wallet.io.persistence.dao.TransactionDao +import javax.inject.Inject + +class AllTrnsAct @Inject constructor( + private val transactionDao: TransactionDao +) : FPAction>() { + override suspend fun Unit.compose(): suspend () -> List = suspend { + transactionDao.findAll() + } thenMap { it.toDomain() } +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/action/transaction/CalcTrnsIncomeExpenseAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/transaction/CalcTrnsIncomeExpenseAct.kt new file mode 100644 index 0000000000..4b7d6d3979 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/action/transaction/CalcTrnsIncomeExpenseAct.kt @@ -0,0 +1,48 @@ +package com.ivy.wallet.domain.action.transaction + +import arrow.core.nonEmptyListOf +import com.ivy.fp.action.FPAction +import com.ivy.fp.action.then +import com.ivy.fp.then +import com.ivy.wallet.domain.action.exchange.ExchangeAct +import com.ivy.wallet.domain.action.exchange.actInput +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Transaction +import com.ivy.wallet.domain.pure.data.IncomeExpenseTransferPair +import com.ivy.wallet.domain.pure.transaction.WalletValueFunctions +import com.ivy.wallet.domain.pure.transaction.foldTransactionsSuspend +import javax.inject.Inject + +class CalcTrnsIncomeExpenseAct @Inject constructor( + private val exchangeAct: ExchangeAct +) : FPAction() { + override suspend fun Input.compose(): suspend () -> IncomeExpenseTransferPair = suspend { + foldTransactionsSuspend( + transactions = transactions, + valueFunctions = nonEmptyListOf( + WalletValueFunctions::income, + WalletValueFunctions::expense, + WalletValueFunctions::transferIncome, + WalletValueFunctions::transferExpenses + ), + arg = WalletValueFunctions.Argument( + accounts = accounts, + baseCurrency = baseCurrency, + exchange = ::actInput then exchangeAct + ) + ) + } then { values -> + IncomeExpenseTransferPair( + income = values[0], + expense = values[1], + transferIncome = values[2], + transferExpense = values[3] + ) + } + + data class Input( + val transactions: List, + val baseCurrency: String, + val accounts: List + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/action/transaction/DueTrnsAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/transaction/DueTrnsAct.kt new file mode 100644 index 0000000000..f61f349354 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/action/transaction/DueTrnsAct.kt @@ -0,0 +1,22 @@ +package com.ivy.wallet.domain.action.transaction + +import com.ivy.fp.action.FPAction +import com.ivy.fp.action.thenMap +import com.ivy.wallet.domain.data.core.Transaction +import com.ivy.wallet.domain.pure.data.ClosedTimeRange +import com.ivy.wallet.io.persistence.dao.TransactionDao +import javax.inject.Inject + +class DueTrnsAct @Inject constructor( + private val transactionDao: TransactionDao +) : FPAction>() { + + override suspend fun ClosedTimeRange.compose(): suspend () -> List = suspend { + io { + transactionDao.findAllDueToBetween( + startDate = from, + endDate = to + ) + } + } thenMap { it.toDomain() } +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/action/transaction/HistoryTrnsAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/transaction/HistoryTrnsAct.kt new file mode 100644 index 0000000000..66442f80f3 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/action/transaction/HistoryTrnsAct.kt @@ -0,0 +1,22 @@ +package com.ivy.wallet.domain.action.transaction + +import com.ivy.fp.action.FPAction +import com.ivy.fp.action.thenMap +import com.ivy.wallet.domain.data.core.Transaction +import com.ivy.wallet.domain.pure.data.ClosedTimeRange +import com.ivy.wallet.io.persistence.dao.TransactionDao +import javax.inject.Inject + +class HistoryTrnsAct @Inject constructor( + private val transactionDao: TransactionDao +) : FPAction>() { + + override suspend fun ClosedTimeRange.compose(): suspend () -> List = suspend { + io { + transactionDao.findAllBetween( + startDate = from, + endDate = to + ) + } + } thenMap { it.toDomain() } +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/action/transaction/HistoryWithDateDivsAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/transaction/HistoryWithDateDivsAct.kt new file mode 100644 index 0000000000..2449faf0fc --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/action/transaction/HistoryWithDateDivsAct.kt @@ -0,0 +1,27 @@ +package com.ivy.wallet.domain.action.transaction + +import com.ivy.fp.action.FPAction +import com.ivy.fp.action.then +import com.ivy.wallet.domain.data.TransactionHistoryItem +import com.ivy.wallet.domain.pure.data.ClosedTimeRange +import javax.inject.Inject + +class HistoryWithDateDivsAct @Inject constructor( + private val historyTrnsAct: HistoryTrnsAct, + private val trnsWithDateDivsAct: TrnsWithDateDivsAct +) : FPAction>() { + + override suspend fun Input.compose(): suspend () -> List = suspend { + range + } then historyTrnsAct then { trns -> + TrnsWithDateDivsAct.Input( + baseCurrency = baseCurrency, + transactions = trns + ) + } then trnsWithDateDivsAct + + data class Input( + val range: ClosedTimeRange, + val baseCurrency: String + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/action/transaction/TrnByIdAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/transaction/TrnByIdAct.kt new file mode 100644 index 0000000000..d075b66dd6 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/action/transaction/TrnByIdAct.kt @@ -0,0 +1,18 @@ +package com.ivy.wallet.domain.action.transaction + +import com.ivy.fp.action.FPAction +import com.ivy.fp.action.then +import com.ivy.wallet.domain.data.core.Transaction +import com.ivy.wallet.io.persistence.dao.TransactionDao +import java.util.* +import javax.inject.Inject + +class TrnByIdAct @Inject constructor( + private val transactionDao: TransactionDao +) : FPAction() { + override suspend fun UUID.compose(): suspend () -> Transaction? = suspend { + this //transactionId + } then transactionDao::findById then { + it?.toDomain() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/action/transaction/TrnsWithDateDivsAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/transaction/TrnsWithDateDivsAct.kt new file mode 100644 index 0000000000..08d09ad07c --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/action/transaction/TrnsWithDateDivsAct.kt @@ -0,0 +1,32 @@ +package com.ivy.wallet.domain.action.transaction + +import com.ivy.fp.action.FPAction +import com.ivy.fp.then +import com.ivy.wallet.domain.action.exchange.ExchangeAct +import com.ivy.wallet.domain.action.exchange.actInput +import com.ivy.wallet.domain.data.TransactionHistoryItem +import com.ivy.wallet.domain.data.core.Transaction +import com.ivy.wallet.domain.pure.transaction.transactionsWithDateDividers +import com.ivy.wallet.io.persistence.dao.AccountDao +import javax.inject.Inject + +class TrnsWithDateDivsAct @Inject constructor( + private val accountDao: AccountDao, + private val exchangeAct: ExchangeAct +) : FPAction>() { + + override suspend fun Input.compose(): suspend () -> List = suspend { + transactionsWithDateDividers( + transactions = transactions, + baseCurrencyCode = baseCurrency, + + getAccount = accountDao::findById then { it?.toDomain() }, + exchange = ::actInput then exchangeAct + ) + } + + data class Input( + val baseCurrency: String, + val transactions: List + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/action/transaction/TrnsWithRangeAndAccFiltersAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/transaction/TrnsWithRangeAndAccFiltersAct.kt new file mode 100644 index 0000000000..c67db5900c --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/action/transaction/TrnsWithRangeAndAccFiltersAct.kt @@ -0,0 +1,26 @@ +package com.ivy.wallet.domain.action.transaction + +import com.ivy.fp.action.FPAction +import com.ivy.fp.action.thenFilter +import com.ivy.wallet.domain.data.core.Transaction +import com.ivy.wallet.io.persistence.dao.TransactionDao +import com.ivy.wallet.ui.onboarding.model.FromToTimeRange +import java.util.* +import javax.inject.Inject + +class TrnsWithRangeAndAccFiltersAct @Inject constructor( + private val transactionDao: TransactionDao +) : FPAction>() { + + override suspend fun Input.compose(): suspend () -> List = suspend { + transactionDao.findAllBetween(range.from(), range.to()) + .map { it.toDomain() } + } thenFilter { + accountIdFilterSet.contains(it.accountId) || accountIdFilterSet.contains(it.toAccountId) + } + + data class Input( + val range: FromToTimeRange, + val accountIdFilterSet: Set + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/action/viewmodel/account/AccountDataAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/viewmodel/account/AccountDataAct.kt new file mode 100644 index 0000000000..b51a505700 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/action/viewmodel/account/AccountDataAct.kt @@ -0,0 +1,67 @@ +package com.ivy.wallet.domain.action.viewmodel.account + +import arrow.core.toOption +import com.ivy.fp.action.FPAction +import com.ivy.fp.action.thenMap +import com.ivy.wallet.domain.action.account.CalcAccBalanceAct +import com.ivy.wallet.domain.action.account.CalcAccIncomeExpenseAct +import com.ivy.wallet.domain.action.exchange.ExchangeAct +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.pure.data.ClosedTimeRange +import com.ivy.wallet.domain.pure.exchange.ExchangeData +import com.ivy.wallet.ui.accounts.AccountData +import javax.inject.Inject + +class AccountDataAct @Inject constructor( + private val exchangeAct: ExchangeAct, + private val calcAccBalanceAct: CalcAccBalanceAct, + private val calcAccIncomeExpenseAct: CalcAccIncomeExpenseAct +) : FPAction>() { + + override suspend fun Input.compose(): suspend () -> List = suspend { + accounts + } thenMap { acc -> + val balance = calcAccBalanceAct( + CalcAccBalanceAct.Input( + account = acc + ) + ).balance + + val balanceBaseCurrency = if (acc.currency != baseCurrency) { + exchangeAct( + ExchangeAct.Input( + data = ExchangeData( + baseCurrency = baseCurrency, + fromCurrency = acc.currency.toOption() + ), + amount = balance + ) + ).orNull() + } else { + null + } + + val incomeExpensePair = calcAccIncomeExpenseAct( + CalcAccIncomeExpenseAct.Input( + account = acc, + range = range, + includeTransfersInCalc = includeTransfersInCalc + ) + ).incomeExpensePair + + AccountData( + account = acc, + balance = balance.toDouble(), + balanceBaseCurrency = balanceBaseCurrency?.toDouble(), + monthlyIncome = incomeExpensePair.income.toDouble(), + monthlyExpenses = incomeExpensePair.expense.toDouble(), + ) + } + + data class Input( + val accounts: List, + val baseCurrency: String, + val range: ClosedTimeRange, + val includeTransfersInCalc: Boolean = false + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/action/viewmodel/home/DueTrnsInfoAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/viewmodel/home/DueTrnsInfoAct.kt new file mode 100644 index 0000000000..1cf898d960 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/action/viewmodel/home/DueTrnsInfoAct.kt @@ -0,0 +1,73 @@ +package com.ivy.wallet.domain.action.viewmodel.home + +import com.ivy.fp.action.FPAction +import com.ivy.fp.action.lambda +import com.ivy.fp.action.then +import com.ivy.fp.then +import com.ivy.wallet.domain.action.account.AccountByIdAct +import com.ivy.wallet.domain.action.exchange.ExchangeAct +import com.ivy.wallet.domain.action.exchange.actInput +import com.ivy.wallet.domain.action.transaction.DueTrnsAct +import com.ivy.wallet.domain.data.core.Transaction +import com.ivy.wallet.domain.pure.data.ClosedTimeRange +import com.ivy.wallet.domain.pure.data.IncomeExpensePair +import com.ivy.wallet.domain.pure.exchange.ExchangeTrnArgument +import com.ivy.wallet.domain.pure.exchange.exchangeInBaseCurrency +import com.ivy.wallet.domain.pure.transaction.expenses +import com.ivy.wallet.domain.pure.transaction.incomes +import com.ivy.wallet.domain.pure.transaction.sumTrns +import com.ivy.wallet.utils.dateNowUTC +import java.time.LocalDate +import javax.inject.Inject + +class DueTrnsInfoAct @Inject constructor( + private val dueTrnsAct: DueTrnsAct, + private val accountByIdAct: AccountByIdAct, + private val exchangeAct: ExchangeAct +) : FPAction() { + + override suspend fun Input.compose(): suspend () -> Output = suspend { + range + } then dueTrnsAct then { trns -> + val dateNow = dateNowUTC() + trns.filter { + this.dueFilter(it, dateNow) + } + } then { dueTrns -> + //We have due transactions in different currencies + val exchangeArg = ExchangeTrnArgument( + baseCurrency = baseCurrency, + exchange = ::actInput then exchangeAct, + getAccount = accountByIdAct.lambda() + ) + + io { + Output( + dueIncomeExpense = IncomeExpensePair( + income = sumTrns( + incomes(dueTrns), + ::exchangeInBaseCurrency, + exchangeArg + ), + expense = sumTrns( + expenses(dueTrns), + ::exchangeInBaseCurrency, + exchangeArg + ) + ), + dueTrns = dueTrns + ) + } + } + + data class Input( + val range: ClosedTimeRange, + val baseCurrency: String, + val dueFilter: (Transaction, LocalDate) -> Boolean + ) + + data class Output( + val dueIncomeExpense: IncomeExpensePair, + val dueTrns: List + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/action/viewmodel/home/HasTrnsAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/viewmodel/home/HasTrnsAct.kt new file mode 100644 index 0000000000..9f11f4911c --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/action/viewmodel/home/HasTrnsAct.kt @@ -0,0 +1,15 @@ +package com.ivy.wallet.domain.action.viewmodel.home + +import com.ivy.fp.action.FPAction +import com.ivy.wallet.io.persistence.dao.TransactionDao +import javax.inject.Inject + +class HasTrnsAct @Inject constructor( + private val transactionDao: TransactionDao +) : FPAction() { + override suspend fun Unit.compose(): suspend () -> Boolean = suspend { + io { + transactionDao.findAll_LIMIT_1().isNotEmpty() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/action/viewmodel/home/OverdueAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/viewmodel/home/OverdueAct.kt new file mode 100644 index 0000000000..b1f5075d57 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/action/viewmodel/home/OverdueAct.kt @@ -0,0 +1,37 @@ +package com.ivy.wallet.domain.action.viewmodel.home + +import com.ivy.fp.action.FPAction +import com.ivy.fp.action.then +import com.ivy.wallet.domain.data.core.Transaction +import com.ivy.wallet.domain.pure.data.ClosedTimeRange +import com.ivy.wallet.domain.pure.data.IncomeExpensePair +import com.ivy.wallet.domain.pure.transaction.isOverdue +import javax.inject.Inject + +class OverdueAct @Inject constructor( + private val dueTrnsInfoAct: DueTrnsInfoAct +) : FPAction() { + + override suspend fun Input.compose(): suspend () -> Output = suspend { + DueTrnsInfoAct.Input( + range = range, + baseCurrency = baseCurrency, + dueFilter = ::isOverdue + ) + } then dueTrnsInfoAct then { + Output( + overdue = it.dueIncomeExpense, + overdueTrns = it.dueTrns + ) + } + + data class Input( + val range: ClosedTimeRange, + val baseCurrency: String + ) + + data class Output( + val overdue: IncomeExpensePair, + val overdueTrns: List + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/action/viewmodel/home/UpcomingAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/viewmodel/home/UpcomingAct.kt new file mode 100644 index 0000000000..35536be366 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/action/viewmodel/home/UpcomingAct.kt @@ -0,0 +1,37 @@ +package com.ivy.wallet.domain.action.viewmodel.home + +import com.ivy.fp.action.FPAction +import com.ivy.fp.action.then +import com.ivy.wallet.domain.data.core.Transaction +import com.ivy.wallet.domain.pure.data.ClosedTimeRange +import com.ivy.wallet.domain.pure.data.IncomeExpensePair +import com.ivy.wallet.domain.pure.transaction.isUpcoming +import javax.inject.Inject + +class UpcomingAct @Inject constructor( + private val dueTrnsInfoAct: DueTrnsInfoAct +) : FPAction() { + + override suspend fun Input.compose(): suspend () -> Output = suspend { + DueTrnsInfoAct.Input( + range = range, + baseCurrency = baseCurrency, + dueFilter = ::isUpcoming + ) + } then dueTrnsInfoAct then { + Output( + upcoming = it.dueIncomeExpense, + upcomingTrns = it.dueTrns + ) + } + + data class Input( + val range: ClosedTimeRange, + val baseCurrency: String + ) + + data class Output( + val upcoming: IncomeExpensePair, + val upcomingTrns: List + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/action/wallet/CalcIncomeExpenseAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/wallet/CalcIncomeExpenseAct.kt new file mode 100644 index 0000000000..d22a13912d --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/action/wallet/CalcIncomeExpenseAct.kt @@ -0,0 +1,76 @@ +package com.ivy.wallet.domain.action.wallet + +import arrow.core.nonEmptyListOf +import arrow.core.toOption +import com.ivy.fp.action.FPAction +import com.ivy.fp.action.then +import com.ivy.fp.action.thenMap +import com.ivy.wallet.domain.action.account.AccTrnsAct +import com.ivy.wallet.domain.action.exchange.ExchangeAct +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.pure.account.filterExcluded +import com.ivy.wallet.domain.pure.data.ClosedTimeRange +import com.ivy.wallet.domain.pure.data.IncomeExpensePair +import com.ivy.wallet.domain.pure.exchange.ExchangeData +import com.ivy.wallet.domain.pure.transaction.AccountValueFunctions +import com.ivy.wallet.domain.pure.transaction.foldTransactions +import com.ivy.wallet.domain.pure.util.orZero +import timber.log.Timber +import javax.inject.Inject + +class CalcIncomeExpenseAct @Inject constructor( + private val accTrnsAct: AccTrnsAct, + private val exchangeAct: ExchangeAct +) : FPAction() { + + override suspend fun Input.compose(): suspend () -> IncomeExpensePair = suspend { + filterExcluded(accounts) + } thenMap { acc -> + Pair( + acc, + accTrnsAct( + AccTrnsAct.Input( + accountId = acc.id, + range = range + ) + ) + ) + } thenMap { (acc, trns) -> + Timber.i("acc: $acc, trns = ${trns.size}") + Pair( + acc, + foldTransactions( + transactions = trns, + valueFunctions = nonEmptyListOf( + AccountValueFunctions::income, + AccountValueFunctions::expense + ), + arg = acc.id + ) + ) + } thenMap { (acc, stats) -> + Timber.i("acc_stats: $acc - $stats") + stats.map { + exchangeAct( + ExchangeAct.Input( + data = ExchangeData( + baseCurrency = baseCurrency, + fromCurrency = (acc.currency ?: baseCurrency).toOption() + ), + amount = it + ), + ).orZero() + } + } then { statsList -> + IncomeExpensePair( + income = statsList.sumOf { it[0] }, + expense = statsList.sumOf { it[1] } + ) + } + + data class Input( + val baseCurrency: String, + val accounts: List, + val range: ClosedTimeRange, + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/action/wallet/CalcWalletBalanceAct.kt b/app/src/main/java/com/ivy/wallet/domain/action/wallet/CalcWalletBalanceAct.kt new file mode 100644 index 0000000000..22bfa2abf1 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/action/wallet/CalcWalletBalanceAct.kt @@ -0,0 +1,52 @@ +package com.ivy.wallet.domain.action.wallet + +import arrow.core.toOption +import com.ivy.fp.action.* +import com.ivy.wallet.domain.action.account.AccountsAct +import com.ivy.wallet.domain.action.account.CalcAccBalanceAct +import com.ivy.wallet.domain.action.exchange.ExchangeAct +import com.ivy.wallet.domain.pure.data.ClosedTimeRange +import com.ivy.wallet.domain.pure.exchange.ExchangeData +import java.math.BigDecimal +import javax.inject.Inject + +class CalcWalletBalanceAct @Inject constructor( + private val accountsAct: AccountsAct, + private val calcAccBalanceAct: CalcAccBalanceAct, + private val exchangeAct: ExchangeAct, +) : FPAction() { + + override suspend fun Input.compose(): suspend () -> BigDecimal = recipe().fixUnit() + + private suspend fun Input.recipe(): suspend (Unit) -> BigDecimal = + accountsAct thenFilter { + withExcluded || it.includeInBalance + } thenMap { + calcAccBalanceAct( + CalcAccBalanceAct.Input( + account = it, + range = range + ) + ) + } thenMap { + exchangeAct( + ExchangeAct.Input( + data = ExchangeData( + baseCurrency = baseCurrency, + fromCurrency = (it.account.currency ?: baseCurrency).toOption(), + toCurrency = balanceCurrency + ), + amount = it.balance + ) + ) + } thenSum { + it.orNull() ?: BigDecimal.ZERO + } + + data class Input( + val baseCurrency: String, + val balanceCurrency: String = baseCurrency, + val range: ClosedTimeRange = ClosedTimeRange.allTimeIvy(), + val withExcluded: Boolean = false + ) +} diff --git a/app/src/main/java/com/ivy/wallet/domain/data/IntervalType.kt b/app/src/main/java/com/ivy/wallet/domain/data/IntervalType.kt index 85063fcb63..c3653223aa 100644 --- a/app/src/main/java/com/ivy/wallet/domain/data/IntervalType.kt +++ b/app/src/main/java/com/ivy/wallet/domain/data/IntervalType.kt @@ -1,5 +1,7 @@ package com.ivy.wallet.domain.data +import com.ivy.wallet.R +import com.ivy.wallet.stringRes import java.time.LocalDateTime enum class IntervalType { @@ -8,10 +10,10 @@ enum class IntervalType { fun forDisplay(intervalN: Int): String { val plural = intervalN > 1 || intervalN == 0 return when (this) { - DAY -> if (plural) "days" else "day" - WEEK -> if (plural) "weeks" else "week" - MONTH -> if (plural) "months" else "month" - YEAR -> if (plural) "years" else "year" + DAY -> if (plural) stringRes(R.string.days) else stringRes(R.string.day) + WEEK -> if (plural) stringRes(R.string.weeks) else stringRes(R.string.week) + MONTH -> if (plural) stringRes(R.string.months) else stringRes(R.string.month) + YEAR -> if (plural) stringRes(R.string.years) else stringRes(R.string.year) } } diff --git a/app/src/main/java/com/ivy/wallet/domain/data/IvyCurrency.kt b/app/src/main/java/com/ivy/wallet/domain/data/IvyCurrency.kt index b27ef15d91..7433419270 100644 --- a/app/src/main/java/com/ivy/wallet/domain/data/IvyCurrency.kt +++ b/app/src/main/java/com/ivy/wallet/domain/data/IvyCurrency.kt @@ -160,6 +160,11 @@ data class IvyCurrency( name = "Ethereum Classic", isCrypto = true ), + IvyCurrency( + code = "DASH", + name = "Dash", + isCrypto = true + ), ) fun getAvailable(): List { diff --git a/app/src/main/java/com/ivy/wallet/domain/data/IvyWalletCompleteData.kt b/app/src/main/java/com/ivy/wallet/domain/data/IvyWalletCompleteData.kt new file mode 100644 index 0000000000..efe0b95e52 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/data/IvyWalletCompleteData.kt @@ -0,0 +1,15 @@ +package com.ivy.wallet.domain.data + +import com.ivy.wallet.io.persistence.data.* + +data class IvyWalletCompleteData( + val accounts: List = emptyList(), + val budgets: List = emptyList(), + val categories: List = emptyList(), + val loanRecords: List = emptyList(), + val loans: List = emptyList(), + val plannedPaymentRules: List = emptyList(), + val settings: List = emptyList(), + val transactions: List = emptyList(), + val sharedPrefs: HashMap = HashMap() +) \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/data/bankintegrations/SEAccount.kt b/app/src/main/java/com/ivy/wallet/domain/data/bankintegrations/SEAccount.kt deleted file mode 100644 index 08c68fc6bd..0000000000 --- a/app/src/main/java/com/ivy/wallet/domain/data/bankintegrations/SEAccount.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.ivy.wallet.domain.data.bankintegrations - -import com.google.gson.annotations.SerializedName - -data class SEAccount( - @SerializedName("id") - val id: String, - - @SerializedName("name") - val name: String, - - @SerializedName("nature") - val nature: String, - - @SerializedName("balance") - val balance: Double, - - @SerializedName("currency_code") - val currency_code: String, - - @SerializedName("connection_id") - val connection_id: String, -) \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/data/bankintegrations/SEAccountConnection.kt b/app/src/main/java/com/ivy/wallet/domain/data/bankintegrations/SEAccountConnection.kt deleted file mode 100644 index 48f50e5552..0000000000 --- a/app/src/main/java/com/ivy/wallet/domain/data/bankintegrations/SEAccountConnection.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.ivy.wallet.domain.data.bankintegrations - -import com.google.gson.annotations.SerializedName - -data class SEAccountConnection( - @SerializedName("account") - val account: SEAccount, - @SerializedName("connection") - val connection: SEConnection -) \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/data/bankintegrations/SEConnection.kt b/app/src/main/java/com/ivy/wallet/domain/data/bankintegrations/SEConnection.kt deleted file mode 100644 index f47ddcf08d..0000000000 --- a/app/src/main/java/com/ivy/wallet/domain/data/bankintegrations/SEConnection.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.ivy.wallet.domain.data.bankintegrations - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import com.google.gson.annotations.SerializedName -import java.time.LocalDateTime - -@Entity(tableName = "se_connections") -data class SEConnection( - @SerializedName("id") - @PrimaryKey @ColumnInfo(name = "id") - val id: String = "", - - @SerializedName("provider_code") - @ColumnInfo(name = "provider_code") - val provider_code: String = "", - - @SerializedName("provider_name") - @ColumnInfo(name = "provider_name") - val provider_name: String = "", - - @SerializedName("customer_id") - @ColumnInfo(name = "customer_id") - val customer_id: String = "", - - @SerializedName("next_refresh_possible_at") - @ColumnInfo(name = "next_refresh_possible_at") - var next_refresh_possible_at: LocalDateTime? = null, -) \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/data/bankintegrations/SECustomer.kt b/app/src/main/java/com/ivy/wallet/domain/data/bankintegrations/SECustomer.kt deleted file mode 100644 index c67c98136a..0000000000 --- a/app/src/main/java/com/ivy/wallet/domain/data/bankintegrations/SECustomer.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.ivy.wallet.domain.data.bankintegrations - -import com.google.gson.annotations.SerializedName - -data class SECustomer( - @SerializedName("id") - val id: String = "", - - @SerializedName("identifier") - val identifier: String = "", - - @SerializedName("secret") - val secret: String = "" -) \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/data/bankintegrations/SETransaction.kt b/app/src/main/java/com/ivy/wallet/domain/data/bankintegrations/SETransaction.kt deleted file mode 100644 index 480f1e821f..0000000000 --- a/app/src/main/java/com/ivy/wallet/domain/data/bankintegrations/SETransaction.kt +++ /dev/null @@ -1,44 +0,0 @@ -package com.ivy.wallet.domain.data.bankintegrations - -import com.google.gson.annotations.SerializedName - -data class SETransaction( - @SerializedName("id") - val id: String, - - @SerializedName("duplicated") - val duplicated: Boolean, - - @SerializedName("mode") - val mode: String, - - @SerializedName("status") - val status: String, - - @SerializedName("made_on") - val made_on: String, - - @SerializedName("amount") - val amount: Double, - - @SerializedName("currency_code") - val currency_code: String, - - @SerializedName("description") - val description: String, - - @SerializedName("category") - val category: String, - - @SerializedName("account_id") - val account_id: String, - - @SerializedName("created_at") - val created_at: String, - - @SerializedName("updated_at") - val updated_at: String, - - @SerializedName("extra") - val extra: Map? -) \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/data/core/Account.kt b/app/src/main/java/com/ivy/wallet/domain/data/core/Account.kt new file mode 100644 index 0000000000..e11d51d9b5 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/data/core/Account.kt @@ -0,0 +1,43 @@ +package com.ivy.wallet.domain.data.core + +import androidx.compose.ui.graphics.toArgb +import com.ivy.wallet.io.network.data.AccountDTO +import com.ivy.wallet.io.persistence.data.AccountEntity +import com.ivy.wallet.ui.theme.Green +import java.util.* + +data class Account( + val name: String, + val currency: String? = null, + val color: Int = Green.toArgb(), + val icon: String? = null, + val orderNum: Double = 0.0, + val includeInBalance: Boolean = true, + + val isSynced: Boolean = false, + val isDeleted: Boolean = false, + + val id: UUID = UUID.randomUUID() +) { + fun toEntity(): AccountEntity = AccountEntity( + name = name, + currency = currency, + color = color, + icon = icon, + orderNum = orderNum, + includeInBalance = includeInBalance, + isSynced = isSynced, + isDeleted = isDeleted, + id = id + ) + + fun toDTO(): AccountDTO = AccountDTO( + name = name, + currency = currency, + color = color, + icon = icon, + orderNum = orderNum, + includeInBalance = includeInBalance, + id = id + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/data/core/Budget.kt b/app/src/main/java/com/ivy/wallet/domain/data/core/Budget.kt new file mode 100644 index 0000000000..8cafaaabe4 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/data/core/Budget.kt @@ -0,0 +1,81 @@ +package com.ivy.wallet.domain.data.core + +import com.ivy.wallet.R +import com.ivy.wallet.io.network.data.BudgetDTO +import com.ivy.wallet.io.persistence.data.BudgetEntity +import com.ivy.wallet.stringRes +import java.util.* + +data class Budget( + val name: String, + val amount: Double, + + val categoryIdsSerialized: String?, + val accountIdsSerialized: String?, + + val isSynced: Boolean = false, + val isDeleted: Boolean = false, + + val orderId: Double, + val id: UUID = UUID.randomUUID() +) { + fun toEntity(): BudgetEntity = BudgetEntity( + name = name, + amount = amount, + categoryIdsSerialized = categoryIdsSerialized, + accountIdsSerialized = accountIdsSerialized, + isSynced = isSynced, + isDeleted = isDeleted, + orderId = orderId, + id = id, + ) + + fun toDTO(): BudgetDTO = BudgetDTO( + name = name, + amount = amount, + categoryIdsSerialized = categoryIdsSerialized, + accountIdsSerialized = accountIdsSerialized, + orderId = orderId, + id = id + ) + + companion object { + fun serialize(ids: List): String { + return ids.joinToString(separator = ",") + } + + fun type(categoriesCount: Int): String { + return when (categoriesCount) { + 0 -> stringRes(R.string.total_budget) + 1 -> stringRes(R.string.category_budget) + else -> stringRes(R.string.multi_category_budget, categoriesCount.toString()) + } + } + } + + fun parseCategoryIds(): List { + return parseIdsString(categoryIdsSerialized) + } + + fun parseAccountIds(): List { + return parseIdsString(accountIdsSerialized) + } + + private fun parseIdsString(idsString: String?): List { + return try { + if (idsString == null) return emptyList() + + idsString + .split(",") + .map { UUID.fromString(it) } + } catch (e: Exception) { + e.printStackTrace() + emptyList() + } + } + + + fun validate(): Boolean { + return name.isNotEmpty() && amount > 0.0 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/data/core/Category.kt b/app/src/main/java/com/ivy/wallet/domain/data/core/Category.kt new file mode 100644 index 0000000000..49ad3a650b --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/data/core/Category.kt @@ -0,0 +1,37 @@ +package com.ivy.wallet.domain.data.core + +import androidx.compose.ui.graphics.toArgb +import com.ivy.wallet.io.network.data.CategoryDTO +import com.ivy.wallet.io.persistence.data.CategoryEntity +import com.ivy.wallet.ui.theme.Ivy +import java.util.* + +data class Category( + val name: String, + val color: Int = Ivy.toArgb(), + val icon: String? = null, + val orderNum: Double = 0.0, + + val isSynced: Boolean = false, + val isDeleted: Boolean = false, + + val id: UUID = UUID.randomUUID() +) { + fun toEntity(): CategoryEntity = CategoryEntity( + name = name, + color = color, + icon = icon, + orderNum = orderNum, + isSynced = isSynced, + isDeleted = isDeleted, + id = id + ) + + fun toDTO(): CategoryDTO = CategoryDTO( + name = name, + color = color, + icon = icon, + orderNum = orderNum, + id = id + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/data/core/ExchangeRate.kt b/app/src/main/java/com/ivy/wallet/domain/data/core/ExchangeRate.kt new file mode 100644 index 0000000000..7f2240e66b --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/data/core/ExchangeRate.kt @@ -0,0 +1,22 @@ +package com.ivy.wallet.domain.data.core + +import com.ivy.wallet.io.network.data.ExchangeRateDTO +import com.ivy.wallet.io.persistence.data.ExchangeRateEntity + +data class ExchangeRate( + val baseCurrency: String, + val currency: String, + val rate: Double, +) { + fun toEntity(): ExchangeRateEntity = ExchangeRateEntity( + baseCurrency = baseCurrency, + currency = currency, + rate = rate + ) + + fun toDTO(): ExchangeRateDTO = ExchangeRateDTO( + baseCurrency = baseCurrency, + currency = currency, + rate = rate + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/data/core/Loan.kt b/app/src/main/java/com/ivy/wallet/domain/data/core/Loan.kt new file mode 100644 index 0000000000..c7b61efca6 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/data/core/Loan.kt @@ -0,0 +1,52 @@ +package com.ivy.wallet.domain.data.core + +import com.ivy.wallet.R +import com.ivy.wallet.domain.data.LoanType +import com.ivy.wallet.io.network.data.LoanDTO +import com.ivy.wallet.io.persistence.data.LoanEntity +import com.ivy.wallet.stringRes +import java.util.* + +data class Loan( + val name: String, + val amount: Double, + val type: LoanType, + val color: Int = 0, + val icon: String? = null, + val orderNum: Double = 0.0, + val accountId: UUID? = null, + + val isSynced: Boolean = false, + val isDeleted: Boolean = false, + + val id: UUID = UUID.randomUUID() +) { + fun toEntity(): LoanEntity = LoanEntity( + name = name, + amount = amount, + type = type, + color = color, + icon = icon, + orderNum = orderNum, + accountId = accountId, + isSynced = isSynced, + isDeleted = isDeleted, + id = id + ) + + fun toDTO(): LoanDTO = LoanDTO( + name = name, + amount = amount, + type = type, + color = color, + icon = icon, + orderNum = orderNum, + accountId = accountId, + id = id + ) + + fun humanReadableType(): String { + return if (type == LoanType.BORROW) stringRes(R.string.borrowed_uppercase) else stringRes( + R.string.lent_uppercase) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/data/core/LoanRecord.kt b/app/src/main/java/com/ivy/wallet/domain/data/core/LoanRecord.kt new file mode 100644 index 0000000000..c0b72b7e4f --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/data/core/LoanRecord.kt @@ -0,0 +1,46 @@ +package com.ivy.wallet.domain.data.core + +import com.ivy.wallet.io.network.data.LoanRecordDTO +import com.ivy.wallet.io.persistence.data.LoanRecordEntity +import java.time.LocalDateTime +import java.util.* + +data class LoanRecord( + val loanId: UUID, + val amount: Double, + val note: String? = null, + val dateTime: LocalDateTime, + val interest: Boolean = false, + val accountId: UUID? = null, + //This is used store the converted amount for currencies which are different from the loan account currency + val convertedAmount: Double? = null, + + val isSynced: Boolean = false, + val isDeleted: Boolean = false, + + val id: UUID = UUID.randomUUID() +) { + fun toEntity(): LoanRecordEntity = LoanRecordEntity( + loanId = loanId, + amount = amount, + note = note, + dateTime = dateTime, + interest = interest, + accountId = accountId, + convertedAmount = convertedAmount, + isSynced = isSynced, + isDeleted = isDeleted, + id = id + ) + + fun toDTO(): LoanRecordDTO = LoanRecordDTO( + loanId = loanId, + amount = amount, + note = note, + dateTime = dateTime, + interest = interest, + accountId = accountId, + convertedAmount = convertedAmount, + id = id + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/data/core/PlannedPaymentRule.kt b/app/src/main/java/com/ivy/wallet/domain/data/core/PlannedPaymentRule.kt new file mode 100644 index 0000000000..dceb2a3a59 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/data/core/PlannedPaymentRule.kt @@ -0,0 +1,57 @@ +package com.ivy.wallet.domain.data.core + +import com.ivy.wallet.domain.data.IntervalType +import com.ivy.wallet.domain.data.TransactionType +import com.ivy.wallet.io.network.data.PlannedPaymentRuleDTO +import com.ivy.wallet.io.persistence.data.PlannedPaymentRuleEntity +import java.time.LocalDateTime +import java.util.* + +data class PlannedPaymentRule( + val startDate: LocalDateTime?, + val intervalN: Int?, + val intervalType: IntervalType?, + val oneTime: Boolean, + + val type: TransactionType, + val accountId: UUID, + val amount: Double = 0.0, + val categoryId: UUID? = null, + val title: String? = null, + val description: String? = null, + + val isSynced: Boolean = false, + val isDeleted: Boolean = false, + + val id: UUID = UUID.randomUUID() +) { + fun toEntity(): PlannedPaymentRuleEntity = PlannedPaymentRuleEntity( + startDate = startDate, + intervalN = intervalN, + intervalType = intervalType, + oneTime = oneTime, + type = type, + accountId = accountId, + amount = amount, + categoryId = categoryId, + title = title, + description = description, + isSynced = isSynced, + isDeleted = isDeleted, + id = id + ) + + fun toDTO(): PlannedPaymentRuleDTO = PlannedPaymentRuleDTO( + startDate = startDate, + intervalN = intervalN, + intervalType = intervalType, + oneTime = oneTime, + type = type, + accountId = accountId, + amount = amount, + categoryId = categoryId, + title = title, + description = description, + id = id + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/data/core/Settings.kt b/app/src/main/java/com/ivy/wallet/domain/data/core/Settings.kt new file mode 100644 index 0000000000..3970eadd55 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/data/core/Settings.kt @@ -0,0 +1,32 @@ +package com.ivy.wallet.domain.data.core + +import com.ivy.design.l0_system.Theme +import com.ivy.wallet.io.network.data.SettingsDTO +import com.ivy.wallet.io.persistence.data.SettingsEntity +import java.math.BigDecimal +import java.util.* + +data class Settings( + val theme: Theme, + val baseCurrency: String, + val bufferAmount: BigDecimal, + val name: String, + + val id: UUID = UUID.randomUUID() +) { + fun toEntity(): SettingsEntity = SettingsEntity( + theme = theme, + currency = baseCurrency, + bufferAmount = bufferAmount.toDouble(), + name = name, + id = id + ) + + fun toDTO(): SettingsDTO = SettingsDTO( + theme = theme, + currency = baseCurrency, + bufferAmount = bufferAmount.toDouble(), + name = name, + id = id + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/data/core/Transaction.kt b/app/src/main/java/com/ivy/wallet/domain/data/core/Transaction.kt new file mode 100644 index 0000000000..28b810756c --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/data/core/Transaction.kt @@ -0,0 +1,77 @@ +package com.ivy.wallet.domain.data.core + +import com.ivy.wallet.domain.data.TransactionHistoryItem +import com.ivy.wallet.domain.data.TransactionType +import com.ivy.wallet.io.network.data.TransactionDTO +import com.ivy.wallet.io.persistence.data.TransactionEntity +import java.math.BigDecimal +import java.time.LocalDateTime +import java.util.* + +data class Transaction( + //TODO: Remove default values & introduce Transaction#dummy() method + val accountId: UUID, + val type: TransactionType, + val amount: BigDecimal, + val toAccountId: UUID? = null, + val toAmount: BigDecimal = amount, + val title: String? = null, + val description: String? = null, + val dateTime: LocalDateTime? = null, + val categoryId: UUID? = null, + val dueDate: LocalDateTime? = null, + + val recurringRuleId: UUID? = null, + + val attachmentUrl: String? = null, + + //This refers to the loan id that is linked with a transaction + val loanId: UUID? = null, + + //This refers to the loan record id that is linked with a transaction + val loanRecordId: UUID? = null, + + val isSynced: Boolean = false, + val isDeleted: Boolean = false, + + + val id: UUID = UUID.randomUUID() +) : TransactionHistoryItem { + fun toEntity(): TransactionEntity = TransactionEntity( + accountId = accountId, + type = type, + amount = amount.toDouble(), + toAccountId = toAccountId, + toAmount = toAmount.toDouble(), + title = title, + description = description, + dateTime = dateTime, + categoryId = categoryId, + dueDate = dueDate, + recurringRuleId = recurringRuleId, + attachmentUrl = attachmentUrl, + loanId = loanId, + loanRecordId = loanRecordId, + id = id, + isSynced = isSynced, + isDeleted = isDeleted + ) + + fun toDTO(): TransactionDTO = TransactionDTO( + accountId = accountId, + type = type, + amount = amount.toDouble(), + toAccountId = toAccountId, + toAmount = toAmount.toDouble(), + title = title, + description = description, + dateTime = dateTime, + categoryId = categoryId, + dueDate = dueDate, + recurringRuleId = recurringRuleId, + attachmentUrl = attachmentUrl, + loanId = loanId, + loanRecordId = loanRecordId, + id = id + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/data/core/User.kt b/app/src/main/java/com/ivy/wallet/domain/data/core/User.kt new file mode 100644 index 0000000000..031b6ca12d --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/data/core/User.kt @@ -0,0 +1,42 @@ +package com.ivy.wallet.domain.data.core + +import com.ivy.wallet.domain.data.AuthProviderType +import com.ivy.wallet.io.network.data.UserDTO +import com.ivy.wallet.io.persistence.data.UserEntity +import java.util.* + +data class User( + val email: String, + val authProviderType: AuthProviderType, + var firstName: String, + val lastName: String?, + val profilePicture: String?, + val color: Int, + + val testUser: Boolean = false, + var id: UUID +) { + fun toEntity(): UserEntity = UserEntity( + email = email, + authProviderType = authProviderType, + firstName = firstName, + lastName = lastName, + profilePicture = profilePicture, + color = color, + testUser = testUser, + id = id + ) + + fun toDTO(): UserDTO = UserDTO( + email = email, + authProviderType = authProviderType, + firstName = firstName, + lastName = lastName, + profilePicture = profilePicture, + color = color, + testUser = testUser, + id = id + ) + + fun names(): String = firstName + if (lastName != null) " $lastName" else "" +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/data/entity/ExchangeRate.kt b/app/src/main/java/com/ivy/wallet/domain/data/entity/ExchangeRate.kt deleted file mode 100644 index d8689b9e4f..0000000000 --- a/app/src/main/java/com/ivy/wallet/domain/data/entity/ExchangeRate.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.ivy.wallet.domain.data.entity - -import androidx.room.Entity - -@Entity(tableName = "exchange_rates", primaryKeys = ["baseCurrency", "currency"]) -data class ExchangeRate( - val baseCurrency: String, - val currency: String, - val rate: Double, -) \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/data/entity/LoanRecord.kt b/app/src/main/java/com/ivy/wallet/domain/data/entity/LoanRecord.kt deleted file mode 100644 index f30df981f5..0000000000 --- a/app/src/main/java/com/ivy/wallet/domain/data/entity/LoanRecord.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.ivy.wallet.domain.data.entity - -import androidx.room.Entity -import androidx.room.PrimaryKey -import java.time.LocalDateTime -import java.util.* - -@Entity(tableName = "loan_records") -data class LoanRecord( - val loanId: UUID, - val amount: Double, - val note: String? = null, - val dateTime: LocalDateTime, - val interest:Boolean = false, - val accountId : UUID? = null, - //This is used store the converted amount for currencies which are different from the loan account currency - val convertedAmount :Double? = null, - - val isSynced: Boolean = false, - val isDeleted: Boolean = false, - - @PrimaryKey - val id: UUID = UUID.randomUUID() -) \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/data/entity/WishlistItem.kt b/app/src/main/java/com/ivy/wallet/domain/data/entity/WishlistItem.kt deleted file mode 100644 index 9eb6b6cba5..0000000000 --- a/app/src/main/java/com/ivy/wallet/domain/data/entity/WishlistItem.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.ivy.wallet.domain.data.entity - -import androidx.room.Entity -import androidx.room.PrimaryKey -import java.time.LocalDateTime -import java.util.* - -@Entity(tableName = "wishlist_items") -data class WishlistItem( - val name: String, - val price: Double, - val accountId: UUID, - val categoryId: UUID? = null, - val description: String?, - val plannedDateTime: LocalDateTime? = null, - val orderNum: Double = 0.0, - @PrimaryKey - val id: UUID = UUID.randomUUID() -) \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/AccountCreator.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/AccountCreator.kt similarity index 79% rename from app/src/main/java/com/ivy/wallet/domain/logic/AccountCreator.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/AccountCreator.kt index 87e84bd58f..d744d003bf 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/AccountCreator.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/AccountCreator.kt @@ -1,10 +1,11 @@ -package com.ivy.wallet.domain.logic +package com.ivy.wallet.domain.deprecated.logic import androidx.compose.ui.graphics.toArgb -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.logic.model.CreateAccountData -import com.ivy.wallet.domain.sync.item.TransactionSync -import com.ivy.wallet.domain.sync.uploader.AccountUploader +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.deprecated.logic.model.CreateAccountData +import com.ivy.wallet.domain.deprecated.sync.item.TransactionSync +import com.ivy.wallet.domain.deprecated.sync.uploader.AccountUploader +import com.ivy.wallet.domain.pure.util.nextOrderNum import com.ivy.wallet.io.persistence.dao.AccountDao import com.ivy.wallet.utils.ioThread @@ -33,10 +34,10 @@ class AccountCreator( color = data.color.toArgb(), icon = data.icon, includeInBalance = data.includeBalance, - orderNum = accountDao.findMaxOrderNum() + 1, + orderNum = accountDao.findMaxOrderNum().nextOrderNum(), isSynced = false ) - accountDao.save(account) + accountDao.save(account.toEntity()) accountLogic.adjustBalance( account = account, @@ -65,7 +66,7 @@ class AccountCreator( ) ioThread { - accountDao.save(updatedAccount) + accountDao.save(updatedAccount.toEntity()) accountLogic.adjustBalance( account = updatedAccount, actualBalance = accountLogic.calculateAccountBalance(updatedAccount), diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/BudgetCreator.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/BudgetCreator.kt similarity index 82% rename from app/src/main/java/com/ivy/wallet/domain/logic/BudgetCreator.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/BudgetCreator.kt index 0042a802bb..18c52faac1 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/BudgetCreator.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/BudgetCreator.kt @@ -1,8 +1,9 @@ -package com.ivy.wallet.domain.logic +package com.ivy.wallet.domain.deprecated.logic -import com.ivy.wallet.domain.data.entity.Budget -import com.ivy.wallet.domain.logic.model.CreateBudgetData -import com.ivy.wallet.domain.sync.uploader.BudgetUploader +import com.ivy.wallet.domain.data.core.Budget +import com.ivy.wallet.domain.deprecated.logic.model.CreateBudgetData +import com.ivy.wallet.domain.deprecated.sync.uploader.BudgetUploader +import com.ivy.wallet.domain.pure.util.nextOrderNum import com.ivy.wallet.io.persistence.dao.BudgetDao import com.ivy.wallet.utils.ioThread @@ -29,11 +30,11 @@ class BudgetCreator( amount = data.amount, categoryIdsSerialized = data.categoryIdsSerialized, accountIdsSerialized = data.accountIdsSerialized, - orderId = budgetDao.findMaxOrderNum() + 1, + orderId = budgetDao.findMaxOrderNum().nextOrderNum(), isSynced = false ) - budgetDao.save(budget) + budgetDao.save(budget.toEntity()) budget } @@ -59,7 +60,7 @@ class BudgetCreator( try { ioThread { budgetDao.save( - updatedBudget.copy( + updatedBudget.toEntity().copy( isSynced = false ) ) diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/CategoryCreator.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/CategoryCreator.kt similarity index 81% rename from app/src/main/java/com/ivy/wallet/domain/logic/CategoryCreator.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/CategoryCreator.kt index 0f74897ada..0f356c4da8 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/CategoryCreator.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/CategoryCreator.kt @@ -1,9 +1,10 @@ -package com.ivy.wallet.domain.logic +package com.ivy.wallet.domain.deprecated.logic import androidx.compose.ui.graphics.toArgb -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.logic.model.CreateCategoryData -import com.ivy.wallet.domain.sync.uploader.CategoryUploader +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.deprecated.logic.model.CreateCategoryData +import com.ivy.wallet.domain.deprecated.sync.uploader.CategoryUploader +import com.ivy.wallet.domain.pure.util.nextOrderNum import com.ivy.wallet.io.persistence.dao.CategoryDao import com.ivy.wallet.utils.ioThread @@ -28,12 +29,11 @@ class CategoryCreator( name = name.trim(), color = data.color.toArgb(), icon = data.icon, - orderNum = categoryDao.findMaxOrderNum() + 1, + orderNum = categoryDao.findMaxOrderNum().nextOrderNum(), isSynced = false ) - categoryDao. - save(newCategory) + categoryDao.save(newCategory.toEntity()) newCategory } @@ -58,7 +58,7 @@ class CategoryCreator( try { ioThread { categoryDao.save( - updatedCategory.copy( + updatedCategory.toEntity().copy( isSynced = false ) ) diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/CustomerJourneyLogic.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/CustomerJourneyLogic.kt similarity index 74% rename from app/src/main/java/com/ivy/wallet/domain/logic/CustomerJourneyLogic.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/CustomerJourneyLogic.kt index ccfd42158e..c989a63fba 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/CustomerJourneyLogic.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/CustomerJourneyLogic.kt @@ -1,20 +1,22 @@ -package com.ivy.wallet.domain.logic +package com.ivy.wallet.domain.deprecated.logic import androidx.compose.runtime.Composable import androidx.compose.ui.tooling.preview.Preview import com.ivy.wallet.Constants import com.ivy.wallet.R import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.logic.model.CustomerJourneyCardData +import com.ivy.wallet.domain.deprecated.logic.model.CustomerJourneyCardData import com.ivy.wallet.io.persistence.SharedPrefs import com.ivy.wallet.io.persistence.dao.PlannedPaymentRuleDao import com.ivy.wallet.io.persistence.dao.TransactionDao +import com.ivy.wallet.stringRes import com.ivy.wallet.ui.* import com.ivy.wallet.ui.home.CustomerJourneyCard import com.ivy.wallet.ui.main.MainTab import com.ivy.wallet.ui.theme.* import com.ivy.wallet.ui.widget.AddTransactionWidgetCompact +@Deprecated("Use FP style, look into `domain.fp` package") class CustomerJourneyLogic( private val transactionDao: TransactionDao, private val plannedPaymentRuleDao: PlannedPaymentRuleDao, @@ -22,7 +24,7 @@ class CustomerJourneyLogic( private val ivyContext: IvyWalletCtx ) { - fun loadCards(): List { + suspend fun loadCards(): List { val trnCount = transactionDao.countHappenedTransactions() val plannedPaymentsCount = plannedPaymentRuleDao.countPlannedPayments() @@ -65,9 +67,9 @@ class CustomerJourneyLogic( condition = { trnCount, _, _ -> trnCount == 0L }, - title = "Adjust your initial balance", - description = "Let's get started. Go to \"Accounts\" -> Tap an account -> Tap its balance -> Enter current balance. That's it!", - cta = "To accounts", + title = stringRes(R.string.adjust_initial_balance), + description = stringRes(R.string.adjust_initial_balance_description), + cta = stringRes(R.string.to_accounts), ctaIcon = R.drawable.ic_custom_account_s, backgroundColor = Ivy, hasDismiss = false, @@ -81,10 +83,9 @@ class CustomerJourneyLogic( condition = { trnCount, plannedPaymentCount, _ -> trnCount >= 1 && plannedPaymentCount == 0L }, - title = "Create your first planned payment", - description = "Automate the tracking of recurring transactions like your subscriptions, rent, salary, etc." + - " Stay ahead of your finances by knowing how much you have to pay/get in advance.", - cta = "Add planned payment", + title = stringRes(R.string.create_first_planned_payment), + description = stringRes(R.string.create_first_planned_payment_description), + cta = stringRes(R.string.add_planned_payment), ctaIcon = R.drawable.ic_planned_payments, backgroundColor = Orange, hasDismiss = true, @@ -103,10 +104,9 @@ class CustomerJourneyLogic( condition = { trnCount, _, _ -> trnCount >= 3 }, - title = "Did you know?", - description = "Ivy Wallet has a cool widget that lets you add INCOME/EXPENSES/TRANSFER transactions with 1-click from your home " + - "\n\nNote: If the \"Add widget\" button doesn't work, please add it manually from your launcher's widgets menu.", - cta = "Add widget", + title = stringRes(R.string.did_you_know), + description = stringRes(R.string.widget_description), + cta = stringRes(R.string.add_widget), ctaIcon = R.drawable.ic_custom_atom_s, backgroundColor = GreenLight, hasDismiss = true, @@ -120,11 +120,9 @@ class CustomerJourneyLogic( condition = { trnCount, _, _ -> trnCount >= 5 }, - title = "Set a budget", - description = "Ivy Wallet not only helps you to passively track your expenses" + - " but also proactively create your financial future by setting budgets" + - " and sticking to them.", - cta = "Add budget", + title = stringRes(R.string.set_a_budget), + description = stringRes(R.string.set_a_budget_description), + cta = stringRes(R.string.add_budget), ctaIcon = R.drawable.ic_budget_xs, backgroundColor = Green2, hasDismiss = true, @@ -138,9 +136,9 @@ class CustomerJourneyLogic( condition = { trnCount, _, _ -> trnCount >= 7 }, - title = "Did you know?", - description = "You can see your expenses structure by categories! Try it, tap the gray/black \"Expenses\" button just below your balance.", - cta = "Expenses PieChart", + title = stringRes(R.string.did_you_know), + description = stringRes(R.string.expenses_piechart_description), + cta = stringRes(R.string.expenses_piechart), ctaIcon = R.drawable.ic_custom_bills_s, backgroundColor = Red, hasDismiss = true, @@ -154,11 +152,9 @@ class CustomerJourneyLogic( condition = { trnCount, _, _ -> trnCount >= 10 }, - title = "Review Ivy Wallet", - description = "Give us your feedback! Help Ivy Wallet become better and grow by writing us a review." + - " Compliments, ideas, and critics are all welcome!" + - " We do our best.\n\nCheers,\nIvy Team", - cta = "Rate us on Google Play", + title = stringRes(R.string.review_ivy_wallet), + description = stringRes(R.string.review_ivy_wallet_description), + cta = stringRes(R.string.rate_us_on_google_play), ctaIcon = R.drawable.ic_custom_star_s, backgroundColor = Green, hasDismiss = true, @@ -172,10 +168,9 @@ class CustomerJourneyLogic( condition = { trnCount, _, _ -> trnCount >= 14 }, - title = "Share Ivy Wallet", - description = "Help us grow so we can invest more in development and make the app better for you." + - " By sharing Ivy Wallet you'll make two developers happy and also help a friend to take control of their finances.", - cta = "Share with friends", + title = stringRes(R.string.share_ivy_wallet), + description = stringRes(R.string.help_us_grow), + cta = stringRes(R.string.share_with_friends), ctaIcon = R.drawable.ic_custom_family_s, backgroundColor = Red3, hasDismiss = true, @@ -206,11 +201,9 @@ class CustomerJourneyLogic( condition = { trnCount, _, _ -> trnCount >= 18 }, - title = "Did you know?", - description = "You can generate reports to get deep insights about your income and spending." + - " Filter your transactions by type, time period, category, accounts, amount, keywords and more" + - " to gain better view on your finances.", - cta = "Make a report", + title = stringRes(R.string.did_you_know), + description = stringRes(R.string.make_a_report_description), + cta = stringRes(R.string.make_a_report), ctaIcon = R.drawable.ic_statistics_xs, backgroundColor = Green2, hasDismiss = true, @@ -224,12 +217,9 @@ class CustomerJourneyLogic( condition = { trnCount, _, _ -> trnCount >= 22 }, - title = "Review Ivy Wallet", - description = "Want to make Ivy Wallet better? Write us a review." + - " That's the only way for us to develop what you want and need." + - " Also it help us rank higher in the PlayStore so we can spend money on the product rather than marketing." + - "\n\nWe do our best.\nIvy Team", - cta = "Rate us on Google Play", + title = stringRes(R.string.review_ivy_wallet), + description = stringRes(R.string.make_ivy_wallet_better_description), + cta = stringRes(R.string.rate_us_on_google_play), ctaIcon = R.drawable.ic_custom_star_s, backgroundColor = GreenLight, hasDismiss = true, @@ -243,13 +233,9 @@ class CustomerJourneyLogic( condition = { trnCount, _, _ -> trnCount >= 24 }, - title = "We need your help!", - description = "We're just a designer and a developer" + - " working on the app after our 9-5 jobs. Currently, we invest a lot of time and money" + - " to generate only losses and exhaustion." + - " If you want us to keep developing Ivy Wallet please share it with friends and family." + - "\n\nP.S. Google PlayStore reviews also helps a lot!", - cta = "Share Ivy Wallet", + title = stringRes(R.string.we_need_your_help), + description = stringRes(R.string.we_need_your_help_description), + cta = stringRes(R.string.share_ivy_wallet), ctaIcon = R.drawable.ic_custom_family_s, backgroundColor = Purple2, hasDismiss = true, @@ -263,11 +249,9 @@ class CustomerJourneyLogic( condition = { trnCount, _, _ -> trnCount >= 28 }, - title = "Ivy Wallet is open-source!", - description = "Ivy Wallet's code is open and everyone can see it." + - " We believe that transparency and ethics are must for every software product." + - " If you like our work and want to make the app better you can contribute in our public Github repository.", - cta = "Contribute", + title = stringRes(R.string.ivy_wallet_is_opensource), + description = stringRes(R.string.ivy_wallet_is_opensource_description), + cta = stringRes(R.string.contribute), ctaIcon = R.drawable.github_logo, backgroundColor = Blue3, hasDismiss = true, diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/LoanCreator.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/LoanCreator.kt similarity index 83% rename from app/src/main/java/com/ivy/wallet/domain/logic/LoanCreator.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/LoanCreator.kt index 23b6c4ee08..1d65906833 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/LoanCreator.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/LoanCreator.kt @@ -1,9 +1,10 @@ -package com.ivy.wallet.domain.logic +package com.ivy.wallet.domain.deprecated.logic import androidx.compose.ui.graphics.toArgb -import com.ivy.wallet.domain.data.entity.Loan -import com.ivy.wallet.domain.logic.model.CreateLoanData -import com.ivy.wallet.domain.sync.uploader.LoanUploader +import com.ivy.wallet.domain.data.core.Loan +import com.ivy.wallet.domain.deprecated.logic.model.CreateLoanData +import com.ivy.wallet.domain.deprecated.sync.uploader.LoanUploader +import com.ivy.wallet.domain.pure.util.nextOrderNum import com.ivy.wallet.io.persistence.dao.LoanDao import com.ivy.wallet.utils.ioThread import java.util.* @@ -34,12 +35,12 @@ class LoanCreator( type = data.type, color = data.color.toArgb(), icon = data.icon, - orderNum = dao.findMaxOrderNum() + 1, + orderNum = dao.findMaxOrderNum().nextOrderNum(), isSynced = false, accountId = data.account?.id ) loanId = item.id - dao.save(item) + dao.save(item.toEntity()) item } @@ -67,7 +68,7 @@ class LoanCreator( try { ioThread { dao.save( - updatedItem.copy( + updatedItem.toEntity().copy( isSynced = false ) ) diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/LoanRecordCreator.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/LoanRecordCreator.kt similarity index 85% rename from app/src/main/java/com/ivy/wallet/domain/logic/LoanRecordCreator.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/LoanRecordCreator.kt index f1ee6071c4..29edb414bf 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/LoanRecordCreator.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/LoanRecordCreator.kt @@ -1,12 +1,13 @@ -package com.ivy.wallet.domain.logic +package com.ivy.wallet.domain.deprecated.logic -import com.ivy.wallet.domain.data.entity.LoanRecord -import com.ivy.wallet.domain.logic.model.CreateLoanRecordData -import com.ivy.wallet.domain.sync.uploader.LoanRecordUploader +import com.ivy.wallet.domain.data.core.LoanRecord +import com.ivy.wallet.domain.deprecated.logic.model.CreateLoanRecordData +import com.ivy.wallet.domain.deprecated.sync.uploader.LoanRecordUploader import com.ivy.wallet.io.persistence.dao.LoanRecordDao import com.ivy.wallet.utils.ioThread import java.util.* +@Deprecated("Use FP style, look into `domain.fp` package") class LoanRecordCreator( private val paywallLogic: PaywallLogic, private val dao: LoanRecordDao, @@ -35,7 +36,7 @@ class LoanRecordCreator( convertedAmount = data.convertedAmount ) - dao.save(item) + dao.save(item.toEntity()) item } @@ -62,7 +63,7 @@ class LoanRecordCreator( try { ioThread { dao.save( - updatedItem.copy( + updatedItem.toEntity().copy( isSynced = false ) ) diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/LogoutLogic.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/LogoutLogic.kt similarity index 93% rename from app/src/main/java/com/ivy/wallet/domain/logic/LogoutLogic.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/LogoutLogic.kt index 8a83a3804f..92fa95f17d 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/LogoutLogic.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/LogoutLogic.kt @@ -1,4 +1,4 @@ -package com.ivy.wallet.domain.logic +package com.ivy.wallet.domain.deprecated.logic import com.ivy.design.navigation.Navigation import com.ivy.wallet.io.network.IvySession diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/PaywallLogic.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/PaywallLogic.kt similarity index 99% rename from app/src/main/java/com/ivy/wallet/domain/logic/PaywallLogic.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/PaywallLogic.kt index e0b3e82126..fbbeaaecf4 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/PaywallLogic.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/PaywallLogic.kt @@ -1,4 +1,4 @@ -package com.ivy.wallet.domain.logic +package com.ivy.wallet.domain.deprecated.logic import com.android.billingclient.api.Purchase import com.ivy.design.navigation.Navigation diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/PlannedPaymentsGenerator.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/PlannedPaymentsGenerator.kt similarity index 81% rename from app/src/main/java/com/ivy/wallet/domain/logic/PlannedPaymentsGenerator.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/PlannedPaymentsGenerator.kt index 918341158d..fefb81070c 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/PlannedPaymentsGenerator.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/PlannedPaymentsGenerator.kt @@ -1,7 +1,7 @@ -package com.ivy.wallet.domain.logic +package com.ivy.wallet.domain.deprecated.logic -import com.ivy.wallet.domain.data.entity.PlannedPaymentRule -import com.ivy.wallet.domain.data.entity.Transaction +import com.ivy.wallet.domain.data.core.PlannedPaymentRule +import com.ivy.wallet.domain.data.core.Transaction import com.ivy.wallet.io.persistence.dao.TransactionDao import java.time.LocalDateTime @@ -13,7 +13,7 @@ class PlannedPaymentsGenerator( private const val GENERATED_INSTANCES_LIMIT = 72 } - fun generate(rule: PlannedPaymentRule) { + suspend fun generate(rule: PlannedPaymentRule) { //delete all not happened transactions transactionDao.flagDeletedByRecurringRuleIdAndNoDateTime( recurringRuleId = rule.id @@ -26,7 +26,7 @@ class PlannedPaymentsGenerator( } } - private fun generateOneTime(rule: PlannedPaymentRule) { + private suspend fun generateOneTime(rule: PlannedPaymentRule) { val trns = transactionDao.findAllByRecurringRuleId(recurringRuleId = rule.id) if (trns.isEmpty()) { @@ -34,7 +34,7 @@ class PlannedPaymentsGenerator( } } - private fun generateRecurring(rule: PlannedPaymentRule) { + private suspend fun generateRecurring(rule: PlannedPaymentRule) { val startDate = rule.startDate!! val endDate = startDate.plusYears(3) @@ -69,14 +69,14 @@ class PlannedPaymentsGenerator( } } - private fun generateTransaction(rule: PlannedPaymentRule, dueDate: LocalDateTime) { + private suspend fun generateTransaction(rule: PlannedPaymentRule, dueDate: LocalDateTime) { transactionDao.save( Transaction( type = rule.type, accountId = rule.accountId, recurringRuleId = rule.id, categoryId = rule.categoryId, - amount = rule.amount, + amount = rule.amount.toBigDecimal(), title = rule.title, description = rule.description, dueDate = dueDate, @@ -84,7 +84,7 @@ class PlannedPaymentsGenerator( toAccountId = null, isSynced = false - ) + ).toEntity() ) } diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/PlannedPaymentsLogic.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/PlannedPaymentsLogic.kt similarity index 79% rename from app/src/main/java/com/ivy/wallet/domain/logic/PlannedPaymentsLogic.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/PlannedPaymentsLogic.kt index 80ae5301f9..dbd8be39bf 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/PlannedPaymentsLogic.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/PlannedPaymentsLogic.kt @@ -1,14 +1,14 @@ -package com.ivy.wallet.domain.logic +package com.ivy.wallet.domain.deprecated.logic import com.ivy.wallet.domain.data.IntervalType import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.PlannedPaymentRule -import com.ivy.wallet.domain.data.entity.Transaction -import com.ivy.wallet.domain.logic.currency.ExchangeRatesLogic -import com.ivy.wallet.domain.logic.currency.sumByDoublePlannedInBaseCurrency -import com.ivy.wallet.domain.sync.uploader.PlannedPaymentRuleUploader -import com.ivy.wallet.domain.sync.uploader.TransactionUploader +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.PlannedPaymentRule +import com.ivy.wallet.domain.data.core.Transaction +import com.ivy.wallet.domain.deprecated.logic.currency.ExchangeRatesLogic +import com.ivy.wallet.domain.deprecated.logic.currency.sumByDoublePlannedInBaseCurrency +import com.ivy.wallet.domain.deprecated.sync.uploader.PlannedPaymentRuleUploader +import com.ivy.wallet.domain.deprecated.sync.uploader.TransactionUploader import com.ivy.wallet.io.persistence.dao.AccountDao import com.ivy.wallet.io.persistence.dao.PlannedPaymentRuleDao import com.ivy.wallet.io.persistence.dao.SettingsDao @@ -31,7 +31,7 @@ class PlannedPaymentsLogic( private const val AVG_DAYS_IN_MONTH = 30.436875 } - fun plannedPaymentsAmountFor(range: FromToTimeRange): Double { + suspend fun plannedPaymentsAmountFor(range: FromToTimeRange): Double { val baseCurrency = settingsDao.findFirst().currency val accounts = accountDao.findAll() @@ -40,9 +40,9 @@ class PlannedPaymentsLogic( endDate = range.to() ).sumOf { val amount = exchangeRatesLogic.amountBaseCurrency( - transaction = it, + transaction = it.toDomain(), baseCurrency = baseCurrency, - accounts = accounts + accounts = accounts.map { it.toDomain() } ) when (it.type) { @@ -53,11 +53,11 @@ class PlannedPaymentsLogic( } } - fun oneTime(): List { - return plannedPaymentRuleDao.findAllByOneTime(oneTime = true) + suspend fun oneTime(): List { + return plannedPaymentRuleDao.findAllByOneTime(oneTime = true).map { it.toDomain() } } - fun oneTimeIncome(): Double { + suspend fun oneTimeIncome(): Double { return oneTime() .filter { it.type == TransactionType.INCOME } .sumByDoublePlannedInBaseCurrency( @@ -67,7 +67,7 @@ class PlannedPaymentsLogic( ) } - fun oneTimeExpenses(): Double { + suspend fun oneTimeExpenses(): Double { return oneTime() .filter { it.type == TransactionType.EXPENSE } .sumByDoublePlannedInBaseCurrency( @@ -77,22 +77,22 @@ class PlannedPaymentsLogic( ) } - fun recurring(): List = - plannedPaymentRuleDao.findAllByOneTime(oneTime = false) + suspend fun recurring(): List = + plannedPaymentRuleDao.findAllByOneTime(oneTime = false).map { it.toDomain() } - fun recurringIncome(): Double { + suspend fun recurringIncome(): Double { return recurring() .filter { it.type == TransactionType.INCOME } .sumByDoubleRecurringForMonthInBaseCurrency() } - fun recurringExpenses(): Double { + suspend fun recurringExpenses(): Double { return recurring() .filter { it.type == TransactionType.EXPENSE } .sumByDoubleRecurringForMonthInBaseCurrency() } - private fun Iterable.sumByDoubleRecurringForMonthInBaseCurrency(): Double { + private suspend fun Iterable.sumByDoubleRecurringForMonthInBaseCurrency(): Double { val accounts = accountDao.findAll() val baseCurrency = settingsDao.findFirst().currency @@ -100,12 +100,12 @@ class PlannedPaymentsLogic( amountForMonthInBaseCurrency( plannedPayment = it, baseCurrency = baseCurrency, - accounts = accounts + accounts = accounts.map { it.toDomain() } ) } } - private fun amountForMonthInBaseCurrency( + private suspend fun amountForMonthInBaseCurrency( plannedPayment: PlannedPaymentRule, baseCurrency: String, accounts: List @@ -166,7 +166,7 @@ class PlannedPaymentsLogic( } ioThread { - transactionDao.save(paidTransaction) + transactionDao.save(paidTransaction.toEntity()) if (plannedPaymentRule != null && plannedPaymentRule.oneTime) { //delete paid oneTime planned payment rules diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/PreloadDataLogic.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/PreloadDataLogic.kt similarity index 70% rename from app/src/main/java/com/ivy/wallet/domain/logic/PreloadDataLogic.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/PreloadDataLogic.kt index 73d328f5fe..0008da514c 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/PreloadDataLogic.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/PreloadDataLogic.kt @@ -1,12 +1,14 @@ -package com.ivy.wallet.domain.logic +package com.ivy.wallet.domain.deprecated.logic import androidx.compose.ui.graphics.toArgb -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.logic.model.CreateAccountData -import com.ivy.wallet.domain.logic.model.CreateCategoryData +import com.ivy.wallet.R +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.deprecated.logic.model.CreateAccountData +import com.ivy.wallet.domain.deprecated.logic.model.CreateCategoryData import com.ivy.wallet.io.persistence.dao.AccountDao import com.ivy.wallet.io.persistence.dao.CategoryDao +import com.ivy.wallet.stringRes import com.ivy.wallet.ui.onboarding.model.AccountBalance import com.ivy.wallet.ui.theme.* @@ -22,9 +24,9 @@ class PreloadDataLogic( return accounts.size < 2 } - fun preloadAccounts() { + suspend fun preloadAccounts() { val cash = Account( - name = "Cash", + name = stringRes(R.string.cash), currency = null, color = Green.toArgb(), icon = "cash", @@ -33,7 +35,7 @@ class PreloadDataLogic( ) val bank = Account( - name = "Bank", + name = stringRes(R.string.bank), currency = null, color = IvyDark.toArgb(), icon = "bank", @@ -41,27 +43,27 @@ class PreloadDataLogic( isSynced = false ) - accountsDao.save(cash) - accountsDao.save(bank) + accountsDao.save(cash.toEntity()) + accountsDao.save(bank.toEntity()) } fun accountSuggestions(baseCurrency: String): List = listOf( CreateAccountData( - name = "Cash", + name = stringRes(R.string.cash), currency = baseCurrency, color = Green, icon = "cash", balance = 0.0 ), CreateAccountData( - name = "Bank", + name = stringRes(R.string.bank), currency = baseCurrency, color = IvyDark, icon = "bank", balance = 0.0 ), CreateAccountData( - name = "Revolut", + name = stringRes(R.string.revoult), currency = baseCurrency, color = Blue, icon = "revolut", @@ -69,7 +71,7 @@ class PreloadDataLogic( ), ) - fun preloadCategories() { + suspend fun preloadCategories() { categoryOrderNum = 0.0 val categoriesToPreload = preloadCategoriesCreateData() @@ -81,67 +83,67 @@ class PreloadDataLogic( private fun preloadCategoriesCreateData() = listOf( CreateCategoryData( - name = "Food & Drinks", + name = stringRes(R.string.food_drinks), color = Green, icon = "fooddrink" ), CreateCategoryData( - name = "Bills & Fees", + name = stringRes(R.string.bills_fees), color = Red, icon = "bills" ), CreateCategoryData( - name = "Transport", + name = stringRes(R.string.transport), color = YellowLight, icon = "transport" ), CreateCategoryData( - name = "Groceries", + name = stringRes(R.string.groceries), color = GreenLight, icon = "groceries" ), CreateCategoryData( - name = "Entertainment", + name = stringRes(R.string.entertainment), color = Orange, icon = "game" ), CreateCategoryData( - name = "Shopping", + name = stringRes(R.string.shopping), color = Ivy, icon = "shopping" ), CreateCategoryData( - name = "Gifts", + name = stringRes(R.string.gifts), color = RedLight, icon = "gift" ), CreateCategoryData( - name = "Health", + name = stringRes(R.string.health), color = IvyLight, icon = "health" ), CreateCategoryData( - name = "Investments", + name = stringRes(R.string.investments), color = IvyDark, icon = "leaf" ), CreateCategoryData( - name = "Loans", + name = stringRes(R.string.loans), color = BlueDark, icon = "loan" ), ) - private fun preloadCategory( + private suspend fun preloadCategory( data: CreateCategoryData ) { val category = Category( @@ -152,98 +154,98 @@ class PreloadDataLogic( isSynced = false ) - categoryDao.save(category) + categoryDao.save(category.toEntity()) } fun categorySuggestions(): List = preloadCategoriesCreateData() .plus( listOf( CreateCategoryData( - name = "Car", + name = stringRes(R.string.car), color = Blue3, icon = "vehicle" ), CreateCategoryData( - name = "Work", + name = stringRes(R.string.work), color = Blue2Light, icon = "work" ), CreateCategoryData( - name = "Home", + name = stringRes(R.string.home_category), color = Green2, icon = "house" ), CreateCategoryData( - name = "Restaurant", + name = stringRes(R.string.restaurant), color = Orange3, icon = "restaurant" ), CreateCategoryData( - name = "Family", + name = stringRes(R.string.family), color = Red3Light, icon = "family" ), CreateCategoryData( - name = "Social Life", + name = stringRes(R.string.social_life), color = Blue2, icon = "people" ), CreateCategoryData( - name = "Order food", + name = stringRes(R.string.order_food), color = Orange2, icon = "orderfood2" ), CreateCategoryData( - name = "Travel", + name = stringRes(R.string.travel), color = BlueLight, icon = "travel" ), CreateCategoryData( - name = "Fitness", + name = stringRes(R.string.fitness), color = Purple2, icon = "fitness" ), CreateCategoryData( - name = "Self-development", + name = stringRes(R.string.self_development), color = Yellow, icon = "selfdevelopment" ), CreateCategoryData( - name = "Clothes", + name = stringRes(R.string.clothes), color = Green2Light, icon = "clothes2" ), CreateCategoryData( - name = "Beauty", + name = stringRes(R.string.beauty), color = Red3, icon = "makeup" ), CreateCategoryData( - name = "Education", + name = stringRes(R.string.education), color = Blue, icon = "education" ), CreateCategoryData( - name = "Pet", + name = stringRes(R.string.pet), color = Orange3Light, icon = "pet" ), CreateCategoryData( - name = "Sports", + name = stringRes(R.string.sports), color = Purple1, icon = "sports" ), diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/SmartTitleSuggestionsLogic.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/SmartTitleSuggestionsLogic.kt similarity index 86% rename from app/src/main/java/com/ivy/wallet/domain/logic/SmartTitleSuggestionsLogic.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/SmartTitleSuggestionsLogic.kt index 3a1c87c33b..10e5aab77a 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/SmartTitleSuggestionsLogic.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/SmartTitleSuggestionsLogic.kt @@ -1,13 +1,13 @@ -package com.ivy.wallet.domain.logic +package com.ivy.wallet.domain.deprecated.logic -import com.ivy.wallet.domain.data.entity.Transaction +import com.ivy.wallet.domain.data.core.Transaction import com.ivy.wallet.io.persistence.dao.TransactionDao import com.ivy.wallet.ui.edit.core.SUGGESTIONS_LIMIT import com.ivy.wallet.utils.capitalizeWords import com.ivy.wallet.utils.isNotNullOrBlank import java.util.* -@Deprecated("Migrate to FP Style") +@Deprecated("Use FP style, look into `domain.fp` package") class SmartTitleSuggestionsLogic( private val transactionDao: TransactionDao ) { @@ -18,7 +18,7 @@ class SmartTitleSuggestionsLogic( * - most used titles for categories * - if suggestions.size < SUGGESTIONS_LIMIT most used titles for accounts */ - fun suggest( + suspend fun suggest( title: String?, categoryId: UUID?, accountId: UUID? @@ -28,6 +28,7 @@ class SmartTitleSuggestionsLogic( if (title != null && title.isNotEmpty()) { //suggest by title val suggestionsByTitle = transactionDao.findAllByTitleMatchingPattern("${title}%") + .map { it.toDomain() } .extractUniqueTitles() .sortedByMostUsedFirst { transactionDao.countByTitleMatchingPattern("${it}%") @@ -45,6 +46,7 @@ class SmartTitleSuggestionsLogic( .findAllByCategory( categoryId = categoryId ) + .map { it.toDomain() } //exclude already suggested suggestions so they're ordered by priority at the end .extractUniqueTitles(excludeSuggestions = suggestions) .sortedByMostUsedFirst { @@ -67,6 +69,7 @@ class SmartTitleSuggestionsLogic( .findAllByAccount( accountId = accountId ) + .map { it.toDomain() } //exclude already suggested suggestions so they're ordered by priority at the end .extractUniqueTitles(excludeSuggestions = suggestions) .sortedByMostUsedFirst { @@ -85,6 +88,7 @@ class SmartTitleSuggestionsLogic( } } +@Deprecated("Use FP style, look into `domain.fp` package") private fun List.extractUniqueTitles( excludeSuggestions: Set? = null ): Set { @@ -95,7 +99,8 @@ private fun List.extractUniqueTitles( .toSet() } -private fun Set.sortedByMostUsedFirst(countUses: (String) -> Long): Set { +@Deprecated("Use FP style, look into `domain.fp` package") +private suspend fun Set.sortedByMostUsedFirst(countUses: suspend (String) -> Long): Set { val titleCountMap = this .map { it to countUses(it) diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/WalletAccountLogic.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/WalletAccountLogic.kt similarity index 66% rename from app/src/main/java/com/ivy/wallet/domain/logic/WalletAccountLogic.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/WalletAccountLogic.kt index 0fa61b151a..e3dc661147 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/WalletAccountLogic.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/WalletAccountLogic.kt @@ -1,10 +1,9 @@ -package com.ivy.wallet.domain.logic +package com.ivy.wallet.domain.deprecated.logic -import com.ivy.wallet.domain.data.TransactionHistoryItem import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.Transaction -import com.ivy.wallet.domain.logic.currency.ExchangeRatesLogic +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Transaction +import com.ivy.wallet.domain.deprecated.logic.currency.ExchangeRatesLogic import com.ivy.wallet.io.persistence.dao.AccountDao import com.ivy.wallet.io.persistence.dao.SettingsDao import com.ivy.wallet.io.persistence.dao.TransactionDao @@ -24,9 +23,9 @@ class WalletAccountLogic( private val settingsDao: SettingsDao ) { - fun adjustBalance( + suspend fun adjustBalance( account: Account, - actualBalance: Double = calculateAccountBalance(account), + actualBalance: Double? = null, newBalance: Double, adjustTransactionTitle: String = "Adjust balance", @@ -34,7 +33,8 @@ class WalletAccountLogic( isFiat: Boolean? = null, trnIsSyncedFlag: Boolean = false, //TODO: Remove this once Bank Integration trn sync is properly implemented ) { - val diff = actualBalance - newBalance + val ab = actualBalance ?: calculateAccountBalance(account) + val diff = ab - newBalance val finalDiff = if (isFiat == true && abs(diff) < 0.009) 0.0 else diff when { @@ -44,11 +44,12 @@ class WalletAccountLogic( Transaction( type = TransactionType.INCOME, title = adjustTransactionTitle, - amount = diff.absoluteValue, + amount = diff.absoluteValue.toBigDecimal(), + toAmount = diff.absoluteValue.toBigDecimal(), dateTime = timeNowUTC(), accountId = account.id, isSynced = trnIsSyncedFlag - ) + ).toEntity() ) } finalDiff > 0 -> { @@ -57,42 +58,18 @@ class WalletAccountLogic( Transaction( type = TransactionType.EXPENSE, title = adjustTransactionTitle, - amount = diff.absoluteValue, + amount = diff.absoluteValue.toBigDecimal(), + toAmount = diff.absoluteValue.toBigDecimal(), dateTime = timeNowUTC(), accountId = account.id, isSynced = trnIsSyncedFlag - ) + ).toEntity() ) } } } - fun historyForAccount(account: Account, range: FromToTimeRange): List { - val startDate = range.from() - val endDate = range.to() - - return transactionDao - .findAllByAccountAndBetween( - accountId = account.id, - startDate = startDate, - endDate = endDate - ) - .plus( - transactionDao.findAllToAccountAndBetween( - toAccountId = account.id, - startDate = startDate, - endDate = endDate - ) - ) - .sortedByDescending { it.dateTime } - .withDateDividers( - exchangeRatesLogic = exchangeRatesLogic, - accountDao = accountDao, - settingsDao = settingsDao - ) - } - - fun calculateAccountBalance( + suspend fun calculateAccountBalance( account: Account, before: LocalDateTime? = null ): Double { @@ -105,44 +82,48 @@ class WalletAccountLogic( ) } - private fun calculateIncomeWithTransfers( + private suspend fun calculateIncomeWithTransfers( account: Account, before: LocalDateTime? ): Double { return transactionDao.findAllByTypeAndAccount(TransactionType.INCOME, account.id) + .map { it.toDomain() } .filterHappenedTransactions( before = before ) - .sumOf { it.amount } + .sumOf { it.amount.toDouble() } .plus( //transfers in transactionDao.findAllTransfersToAccount(account.id) + .map { it.toDomain() } .filterHappenedTransactions( before = before ) - .sumOf { it.toAmount ?: it.amount } + .sumOf { it.toAmount.toDouble() } ) } - private fun calculateExpensesWithTransfers( + private suspend fun calculateExpensesWithTransfers( account: Account, before: LocalDateTime? ): Double { return transactionDao.findAllByTypeAndAccount(TransactionType.EXPENSE, account.id) + .map { it.toDomain() } .filterHappenedTransactions( before = before ) - .sumOf { it.amount } + .sumOf { it.amount.toDouble() } .plus( //transfer out transactionDao.findAllByTypeAndAccount( type = TransactionType.TRANSFER, accountId = account.id ) + .map { it.toDomain() } .filterHappenedTransactions( before = before ) - .sumOf { it.amount } + .sumOf { it.amount.toDouble() } ) } @@ -155,7 +136,7 @@ class WalletAccountLogic( } } - fun calculateAccountIncome(account: Account, range: FromToTimeRange): Double = + suspend fun calculateAccountIncome(account: Account, range: FromToTimeRange): Double = transactionDao .findAllByTypeAndAccountBetween( type = TransactionType.INCOME, @@ -166,7 +147,7 @@ class WalletAccountLogic( .filter { it.dateTime != null } .sumOf { it.amount } - fun calculateAccountExpenses(account: Account, range: FromToTimeRange): Double = + suspend fun calculateAccountExpenses(account: Account, range: FromToTimeRange): Double = transactionDao .findAllByTypeAndAccountBetween( type = TransactionType.EXPENSE, @@ -177,40 +158,44 @@ class WalletAccountLogic( .filter { it.dateTime != null } .sumOf { it.amount } - fun calculateUpcomingIncome(account: Account, range: FromToTimeRange): Double = + suspend fun calculateUpcomingIncome(account: Account, range: FromToTimeRange): Double = upcoming(account, range = range) .filter { it.type == TransactionType.INCOME } - .sumOf { it.amount } + .sumOf { it.amount.toDouble() } - fun calculateUpcomingExpenses(account: Account, range: FromToTimeRange): Double = + suspend fun calculateUpcomingExpenses(account: Account, range: FromToTimeRange): Double = upcoming(account = account, range = range) .filter { it.type == TransactionType.EXPENSE } - .sumOf { it.amount } + .sumOf { it.amount.toDouble() } - fun calculateOverdueIncome(account: Account, range: FromToTimeRange): Double = + suspend fun calculateOverdueIncome(account: Account, range: FromToTimeRange): Double = overdue(account, range = range) .filter { it.type == TransactionType.INCOME } - .sumOf { it.amount } + .sumOf { it.amount.toDouble() } - fun calculateOverdueExpenses(account: Account, range: FromToTimeRange): Double = + suspend fun calculateOverdueExpenses(account: Account, range: FromToTimeRange): Double = overdue(account, range = range) .filter { it.type == TransactionType.EXPENSE } - .sumOf { it.amount } + .sumOf { it.amount.toDouble() } - fun upcoming(account: Account, range: FromToTimeRange): List { + suspend fun upcoming(account: Account, range: FromToTimeRange): List { return transactionDao.findAllDueToBetweenByAccount( accountId = account.id, startDate = range.upcomingFrom(), endDate = range.to() - ).filterUpcoming() + ) + .map { it.toDomain() } + .filterUpcoming() } - fun overdue(account: Account, range: FromToTimeRange): List { + suspend fun overdue(account: Account, range: FromToTimeRange): List { return transactionDao.findAllDueToBetweenByAccount( accountId = account.id, startDate = range.from(), endDate = range.overdueTo() - ).filterOverdue() + ) + .map { it.toDomain() } + .filterOverdue() } } \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/WalletCategoryLogic.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/WalletCategoryLogic.kt similarity index 78% rename from app/src/main/java/com/ivy/wallet/domain/logic/WalletCategoryLogic.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/WalletCategoryLogic.kt index 86eb786913..94d5d8ec89 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/WalletCategoryLogic.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/WalletCategoryLogic.kt @@ -1,11 +1,12 @@ -package com.ivy.wallet.domain.logic +package com.ivy.wallet.domain.deprecated.logic import com.ivy.wallet.domain.data.TransactionHistoryItem import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.data.entity.Transaction -import com.ivy.wallet.domain.logic.currency.ExchangeRatesLogic -import com.ivy.wallet.domain.logic.currency.sumInBaseCurrency +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.data.core.Transaction +import com.ivy.wallet.domain.deprecated.logic.currency.ExchangeRatesLogic +import com.ivy.wallet.domain.deprecated.logic.currency.sumInBaseCurrency +import com.ivy.wallet.domain.pure.transaction.withDateDividers import com.ivy.wallet.io.persistence.dao.AccountDao import com.ivy.wallet.io.persistence.dao.SettingsDao import com.ivy.wallet.io.persistence.dao.TransactionDao @@ -22,14 +23,14 @@ class WalletCategoryLogic( private val transactionDao: TransactionDao ) { - fun calculateCategoryBalance( + suspend fun calculateCategoryBalance( category: Category, range: FromToTimeRange, accountFilterSet: Set = emptySet(), transactions: List = emptyList() ): Double { val baseCurrency = settingsDao.findFirst().currency - val accounts = accountDao.findAll() + val accounts = accountDao.findAll().map { it.toDomain() } return historyByCategory( category, @@ -52,7 +53,7 @@ class WalletCategoryLogic( } } - fun calculateCategoryIncome( + suspend fun calculateCategoryIncome( category: Category, range: FromToTimeRange, accountFilterSet: Set = emptySet(), @@ -63,7 +64,7 @@ class WalletCategoryLogic( type = TransactionType.INCOME, startDate = range.from(), endDate = range.to() - ) + ).map { it.toDomain() } .filter { accountFilterSet.isEmpty() || accountFilterSet.contains(it.accountId) } @@ -74,7 +75,7 @@ class WalletCategoryLogic( ) } - fun calculateCategoryIncome( + suspend fun calculateCategoryIncome( incomeTransaction: List, accountFilterSet: Set = emptySet() ): Double { @@ -89,7 +90,7 @@ class WalletCategoryLogic( ) } - fun calculateCategoryExpenses( + suspend fun calculateCategoryExpenses( category: Category, range: FromToTimeRange, accountFilterSet: Set = emptySet(), @@ -103,7 +104,7 @@ class WalletCategoryLogic( ) .filter { accountFilterSet.isEmpty() || accountFilterSet.contains(it.accountId) - } + }.map { it.toDomain() } .sumInBaseCurrency( exchangeRatesLogic = exchangeRatesLogic, settingsDao = settingsDao, @@ -112,7 +113,7 @@ class WalletCategoryLogic( } - fun calculateCategoryExpenses( + suspend fun calculateCategoryExpenses( expenseTransactions: List, accountFilterSet: Set = emptySet() ): Double { @@ -127,17 +128,17 @@ class WalletCategoryLogic( ) } - fun calculateUnspecifiedBalance(range: FromToTimeRange): Double { + suspend fun calculateUnspecifiedBalance(range: FromToTimeRange): Double { return calculateUnspecifiedIncome(range) - calculateUnspecifiedExpenses(range) } - fun calculateUnspecifiedIncome(range: FromToTimeRange): Double { + suspend fun calculateUnspecifiedIncome(range: FromToTimeRange): Double { return transactionDao .findAllUnspecifiedAndTypeAndBetween( type = TransactionType.INCOME, startDate = range.from(), endDate = range.to() - ) + ).map { it.toDomain() } .sumInBaseCurrency( exchangeRatesLogic = exchangeRatesLogic, settingsDao = settingsDao, @@ -145,13 +146,13 @@ class WalletCategoryLogic( ) } - fun calculateUnspecifiedExpenses(range: FromToTimeRange): Double { + suspend fun calculateUnspecifiedExpenses(range: FromToTimeRange): Double { return transactionDao .findAllUnspecifiedAndTypeAndBetween( type = TransactionType.EXPENSE, startDate = range.from(), endDate = range.to() - ) + ).map { it.toDomain() } .sumInBaseCurrency( exchangeRatesLogic = exchangeRatesLogic, settingsDao = settingsDao, @@ -159,19 +160,8 @@ class WalletCategoryLogic( ) } - fun historyByCategoryWithDateDividers( - category: Category, - range: FromToTimeRange - ): List { - return historyByCategory(category, range) - .withDateDividers( - exchangeRatesLogic = exchangeRatesLogic, - settingsDao = settingsDao, - accountDao = accountDao - ) - } - fun historyByCategoryAccountWithDateDividers( + suspend fun historyByCategoryAccountWithDateDividers( category: Category, range: FromToTimeRange, accountFilterSet: Set, @@ -188,7 +178,7 @@ class WalletCategoryLogic( ) } - fun historyByCategory( + suspend fun historyByCategory( category: Category, range: FromToTimeRange, accountFilterSet: Set = emptySet(), @@ -201,7 +191,7 @@ class WalletCategoryLogic( categoryId = category.id, startDate = range.from(), endDate = range.to() - ) + ).map { it.toDomain() } } return trans.filter { @@ -209,12 +199,12 @@ class WalletCategoryLogic( } } - fun historyUnspecified(range: FromToTimeRange): List { + suspend fun historyUnspecified(range: FromToTimeRange): List { return transactionDao .findAllUnspecifiedAndBetween( startDate = range.from(), endDate = range.to() - ) + ).map { it.toDomain() } .withDateDividers( exchangeRatesLogic = exchangeRatesLogic, settingsDao = settingsDao, @@ -223,7 +213,7 @@ class WalletCategoryLogic( } - fun calculateUpcomingIncomeByCategory(category: Category, range: FromToTimeRange): Double { + suspend fun calculateUpcomingIncomeByCategory(category: Category, range: FromToTimeRange): Double { return upcomingByCategory(category = category, range = range) .filter { it.type == TransactionType.INCOME } .sumInBaseCurrency( @@ -233,7 +223,7 @@ class WalletCategoryLogic( ) } - fun calculateUpcomingExpensesByCategory(category: Category, range: FromToTimeRange): Double { + suspend fun calculateUpcomingExpensesByCategory(category: Category, range: FromToTimeRange): Double { return upcomingByCategory(category = category, range = range) .filter { it.type == TransactionType.EXPENSE } .sumInBaseCurrency( @@ -243,7 +233,7 @@ class WalletCategoryLogic( ) } - fun calculateUpcomingIncomeUnspecified(range: FromToTimeRange): Double { + suspend fun calculateUpcomingIncomeUnspecified(range: FromToTimeRange): Double { return upcomingUnspecified(range = range) .filter { it.type == TransactionType.INCOME } .sumInBaseCurrency( @@ -253,7 +243,7 @@ class WalletCategoryLogic( ) } - fun calculateUpcomingExpensesUnspecified(range: FromToTimeRange): Double { + suspend fun calculateUpcomingExpensesUnspecified(range: FromToTimeRange): Double { return upcomingUnspecified(range = range) .filter { it.type == TransactionType.EXPENSE } .sumInBaseCurrency( @@ -263,22 +253,26 @@ class WalletCategoryLogic( ) } - fun upcomingByCategory(category: Category, range: FromToTimeRange): List { + suspend fun upcomingByCategory(category: Category, range: FromToTimeRange): List { return transactionDao.findAllDueToBetweenByCategory( categoryId = category.id, startDate = range.upcomingFrom(), endDate = range.to() - ).filterUpcoming() + ) + .map { it.toDomain() } + .filterUpcoming() } - fun upcomingUnspecified(range: FromToTimeRange): List { + suspend fun upcomingUnspecified(range: FromToTimeRange): List { return transactionDao.findAllDueToBetweenByCategoryUnspecified( startDate = range.upcomingFrom(), endDate = range.to() - ).filterUpcoming() + ) + .map { it.toDomain() } + .filterUpcoming() } - fun calculateOverdueIncomeByCategory(category: Category, range: FromToTimeRange): Double { + suspend fun calculateOverdueIncomeByCategory(category: Category, range: FromToTimeRange): Double { return overdueByCategory(category, range = range) .filter { it.type == TransactionType.INCOME } .sumInBaseCurrency( @@ -288,7 +282,7 @@ class WalletCategoryLogic( ) } - fun calculateOverdueExpensesByCategory(category: Category, range: FromToTimeRange): Double { + suspend fun calculateOverdueExpensesByCategory(category: Category, range: FromToTimeRange): Double { return overdueByCategory(category, range = range) .filter { it.type == TransactionType.EXPENSE } .sumInBaseCurrency( @@ -298,7 +292,7 @@ class WalletCategoryLogic( ) } - fun calculateOverdueIncomeUnspecified(range: FromToTimeRange): Double { + suspend fun calculateOverdueIncomeUnspecified(range: FromToTimeRange): Double { return overdueUnspecified(range = range) .filter { it.type == TransactionType.INCOME } .sumInBaseCurrency( @@ -308,7 +302,7 @@ class WalletCategoryLogic( ) } - fun calculateOverdueExpensesUnspecified(range: FromToTimeRange): Double { + suspend fun calculateOverdueExpensesUnspecified(range: FromToTimeRange): Double { return overdueUnspecified(range = range) .filter { it.type == TransactionType.EXPENSE } .sumInBaseCurrency( @@ -319,19 +313,23 @@ class WalletCategoryLogic( } - fun overdueByCategory(category: Category, range: FromToTimeRange): List { + suspend fun overdueByCategory(category: Category, range: FromToTimeRange): List { return transactionDao.findAllDueToBetweenByCategory( categoryId = category.id, startDate = range.from(), endDate = range.overdueTo() - ).filterOverdue() + ) + .map { it.toDomain() } + .filterOverdue() } - fun overdueUnspecified(range: FromToTimeRange): List { + suspend fun overdueUnspecified(range: FromToTimeRange): List { return transactionDao.findAllDueToBetweenByCategoryUnspecified( startDate = range.from(), endDate = range.overdueTo() - ).filterOverdue() + ) + .map { it.toDomain() } + .filterOverdue() } } \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/csv/CSVImporter.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/csv/CSVImporter.kt similarity index 93% rename from app/src/main/java/com/ivy/wallet/domain/logic/csv/CSVImporter.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/csv/CSVImporter.kt index edc55bb848..9389b9dff5 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/csv/CSVImporter.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/csv/CSVImporter.kt @@ -1,14 +1,15 @@ -package com.ivy.wallet.domain.logic.csv +package com.ivy.wallet.domain.deprecated.logic.csv import androidx.compose.ui.graphics.toArgb import com.ivy.wallet.domain.data.IvyCurrency import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.data.entity.Transaction -import com.ivy.wallet.domain.logic.csv.model.CSVRow -import com.ivy.wallet.domain.logic.csv.model.ImportResult -import com.ivy.wallet.domain.logic.csv.model.RowMapping +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.data.core.Transaction +import com.ivy.wallet.domain.deprecated.logic.csv.model.CSVRow +import com.ivy.wallet.domain.deprecated.logic.csv.model.ImportResult +import com.ivy.wallet.domain.deprecated.logic.csv.model.RowMapping +import com.ivy.wallet.domain.pure.util.nextOrderNum import com.ivy.wallet.io.persistence.dao.AccountDao import com.ivy.wallet.io.persistence.dao.CategoryDao import com.ivy.wallet.io.persistence.dao.SettingsDao @@ -78,10 +79,10 @@ class CSVImporter( newCategoryColorIndex = 0 newAccountColorIndex = 0 - accounts = accountDao.findAll() + accounts = accountDao.findAll().map { it.toDomain() } val initialAccountsCount = accounts.size - categories = categoryDao.findAll() + categories = categoryDao.findAll().map { it.toDomain() } val initialCategoriesCount = categories.size val baseCurrency = settingsDao.findFirst().currency @@ -120,7 +121,7 @@ class CSVImporter( val progressPercent = if (rowsCount > 0) index / transactions.size.toDouble() else 0.0 onProgress(0.5 + progressPercent / 2) - transactionDao.save(transaction) + transactionDao.save(transaction.toEntity()) } return ImportResult( @@ -132,7 +133,7 @@ class CSVImporter( ) } - private fun mapToTransaction( + private suspend fun mapToTransaction( baseCurrency: String, row: List, rowMapping: RowMapping @@ -212,10 +213,10 @@ class CSVImporter( Transaction( id = id, type = type, - amount = amount, + amount = amount.toBigDecimal(), accountId = account.id, toAccountId = toAccount?.id, - toAmount = toAmount, + toAmount = toAmount?.toBigDecimal() ?: amount.toBigDecimal(), dateTime = dateTime, dueDate = dueDate, categoryId = category?.id, @@ -403,7 +404,7 @@ class CSVImporter( ).convertLocalToUTC() } - private fun mapAccount( + private suspend fun mapAccount( baseCurrency: String, accountNameString: String?, color: Int?, @@ -442,10 +443,10 @@ class CSVImporter( ), color = colorArgb, icon = icon, - orderNum = orderNum ?: accountDao.findMaxOrderNum() + 1 + orderNum = orderNum ?: accountDao.findMaxOrderNum().nextOrderNum() ) - accountDao.save(newAccount) - accounts = accountDao.findAll() + accountDao.save(newAccount.toEntity()) + accounts = accountDao.findAll().map { it.toDomain() } return newAccount } @@ -466,7 +467,7 @@ class CSVImporter( } - private fun mapCategory( + private suspend fun mapCategory( categoryNameString: String?, color: Int?, icon: String?, @@ -491,10 +492,10 @@ class CSVImporter( name = categoryNameString, color = colorArgb, icon = icon, - orderNum = orderNum ?: categoryDao.findMaxOrderNum() + 1 + orderNum = orderNum ?: categoryDao.findMaxOrderNum().nextOrderNum() ) - categoryDao.save(newCategory) - categories = categoryDao.findAll() + categoryDao.save(newCategory.toEntity()) + categories = categoryDao.findAll().map { it.toDomain() } return newCategory } diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/csv/CSVMapper.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/csv/CSVMapper.kt similarity index 95% rename from app/src/main/java/com/ivy/wallet/domain/logic/csv/CSVMapper.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/csv/CSVMapper.kt index 7101669908..98c35a0015 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/csv/CSVMapper.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/csv/CSVMapper.kt @@ -1,10 +1,10 @@ -package com.ivy.wallet.domain.logic.csv +package com.ivy.wallet.domain.deprecated.logic.csv import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.data.entity.Transaction -import com.ivy.wallet.domain.logic.csv.model.ImportType -import com.ivy.wallet.domain.logic.csv.model.JoinResult -import com.ivy.wallet.domain.logic.csv.model.RowMapping +import com.ivy.wallet.domain.data.core.Transaction +import com.ivy.wallet.domain.deprecated.logic.csv.model.ImportType +import com.ivy.wallet.domain.deprecated.logic.csv.model.JoinResult +import com.ivy.wallet.domain.deprecated.logic.csv.model.RowMapping import com.ivy.wallet.utils.toLowerCaseLocal class CSVMapper { diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/csv/CSVNormalizer.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/csv/CSVNormalizer.kt similarity index 77% rename from app/src/main/java/com/ivy/wallet/domain/logic/csv/CSVNormalizer.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/csv/CSVNormalizer.kt index 29d19d1a8e..4f1b4389c3 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/csv/CSVNormalizer.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/csv/CSVNormalizer.kt @@ -1,6 +1,6 @@ -package com.ivy.wallet.domain.logic.csv +package com.ivy.wallet.domain.deprecated.logic.csv -import com.ivy.wallet.domain.logic.csv.model.ImportType +import com.ivy.wallet.domain.deprecated.logic.csv.model.ImportType class CSVNormalizer { diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/csv/ExportCSVLogic.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/csv/ExportCSVLogic.kt similarity index 89% rename from app/src/main/java/com/ivy/wallet/domain/logic/csv/ExportCSVLogic.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/csv/ExportCSVLogic.kt index 6df704811f..2c80a97bcc 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/csv/ExportCSVLogic.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/csv/ExportCSVLogic.kt @@ -1,11 +1,11 @@ -package com.ivy.wallet.domain.logic.csv +package com.ivy.wallet.domain.deprecated.logic.csv import android.content.Context import android.net.Uri import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.data.entity.Transaction +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.data.core.Transaction import com.ivy.wallet.io.persistence.dao.AccountDao import com.ivy.wallet.io.persistence.dao.CategoryDao import com.ivy.wallet.io.persistence.dao.SettingsDao @@ -31,8 +31,8 @@ class ExportCSVLogic( suspend fun exportToFile( context: Context, fileUri: Uri, - exportScope: () -> List = { - transactionDao.findAll() + exportScope: suspend () -> List = { + transactionDao.findAll().map { it.toDomain() } } ) { val csv = generateCSV( @@ -49,7 +49,7 @@ class ExportCSVLogic( } private suspend fun generateCSV( - exportScope: () -> List + exportScope: suspend () -> List ): String { return ioThread { val accountMap = accountDao @@ -66,8 +66,8 @@ class ExportCSVLogic( .joinToString("\n") { it.toCSV( baseCurrency = baseCurrency, - accountMap = accountMap, - categoryMap = categoryMap + accountMap = accountMap.mapValues { it.value.toDomain() }, + categoryMap = categoryMap.mapValues { it.value.toDomain() } ) } @@ -116,7 +116,7 @@ class ExportCSVLogic( TransactionType.INCOME -> it TransactionType.EXPENSE -> -it TransactionType.TRANSFER -> 0.0 - }.formatAmountCSV(currency) + }.toDouble().formatAmountCSV(currency) append(amountFormatted) } @@ -132,7 +132,7 @@ class ExportCSVLogic( //Transfer Amount csv.appendValue(if (type == TransactionType.TRANSFER) amount else null) { - append(it.formatAmountCSV(currency)) + append(it.toDouble().formatAmountCSV(currency)) } //Transfer Currency @@ -148,7 +148,7 @@ class ExportCSVLogic( val receiveCurrency = toAccountId?.let { accountMap[it]?.currency ?: baseCurrency } //Receive Amount csv.appendValue(toAmount) { - append(it.formatAmountCSV(receiveCurrency ?: baseCurrency)) + append(it.toDouble().formatAmountCSV(receiveCurrency ?: baseCurrency)) } //Receive Currency diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/csv/IvyFileReader.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/csv/IvyFileReader.kt similarity index 88% rename from app/src/main/java/com/ivy/wallet/domain/logic/csv/IvyFileReader.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/csv/IvyFileReader.kt index 24734883cb..0ad0b9c05a 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/csv/IvyFileReader.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/csv/IvyFileReader.kt @@ -1,4 +1,4 @@ -package com.ivy.wallet.domain.logic.csv +package com.ivy.wallet.domain.deprecated.logic.csv import android.content.Context import android.net.Uri diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/csv/model/CSVRow.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/csv/model/CSVRow.kt similarity index 55% rename from app/src/main/java/com/ivy/wallet/domain/logic/csv/model/CSVRow.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/csv/model/CSVRow.kt index 3fa61d3aea..1b926a8f93 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/csv/model/CSVRow.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/csv/model/CSVRow.kt @@ -1,4 +1,4 @@ -package com.ivy.wallet.domain.logic.csv.model +package com.ivy.wallet.domain.deprecated.logic.csv.model data class CSVRow( val index: Int, diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/csv/model/ImportResult.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/csv/model/ImportResult.kt similarity index 76% rename from app/src/main/java/com/ivy/wallet/domain/logic/csv/model/ImportResult.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/csv/model/ImportResult.kt index 833026c420..681e3a2f37 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/csv/model/ImportResult.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/csv/model/ImportResult.kt @@ -1,4 +1,4 @@ -package com.ivy.wallet.domain.logic.csv.model +package com.ivy.wallet.domain.deprecated.logic.csv.model data class ImportResult( val rowsFound: Int, diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/csv/model/ImportType.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/csv/model/ImportType.kt similarity index 98% rename from app/src/main/java/com/ivy/wallet/domain/logic/csv/model/ImportType.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/csv/model/ImportType.kt index d9028d0287..ebfb836b06 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/csv/model/ImportType.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/csv/model/ImportType.kt @@ -1,4 +1,4 @@ -package com.ivy.wallet.domain.logic.csv.model +package com.ivy.wallet.domain.deprecated.logic.csv.model import androidx.annotation.DrawableRes import androidx.compose.runtime.Composable diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/csv/model/RowMapping.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/csv/model/RowMapping.kt similarity index 90% rename from app/src/main/java/com/ivy/wallet/domain/logic/csv/model/RowMapping.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/csv/model/RowMapping.kt index 03c67fbdca..5bd01acb35 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/csv/model/RowMapping.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/csv/model/RowMapping.kt @@ -1,7 +1,7 @@ -package com.ivy.wallet.domain.logic.csv.model +package com.ivy.wallet.domain.deprecated.logic.csv.model -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.data.entity.Transaction +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.data.core.Transaction data class RowMapping( val type: Int? = null, diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/currency/ExchangeRatesLogic.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/currency/ExchangeRatesLogic.kt similarity index 80% rename from app/src/main/java/com/ivy/wallet/domain/logic/currency/ExchangeRatesLogic.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/currency/ExchangeRatesLogic.kt index 3ca79b58b0..d5187c7111 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/currency/ExchangeRatesLogic.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/currency/ExchangeRatesLogic.kt @@ -1,9 +1,9 @@ -package com.ivy.wallet.domain.logic.currency +package com.ivy.wallet.domain.deprecated.logic.currency -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.ExchangeRate -import com.ivy.wallet.domain.data.entity.PlannedPaymentRule -import com.ivy.wallet.domain.data.entity.Transaction +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.ExchangeRate +import com.ivy.wallet.domain.data.core.PlannedPaymentRule +import com.ivy.wallet.domain.data.core.Transaction import com.ivy.wallet.io.network.RestClient import com.ivy.wallet.io.network.service.CoinbaseService import com.ivy.wallet.io.persistence.dao.AccountDao @@ -12,6 +12,7 @@ import com.ivy.wallet.io.persistence.dao.SettingsDao import com.ivy.wallet.utils.sendToCrashlytics import java.util.* +@Deprecated("Use FP style, look into `domain.fp` package") class ExchangeRatesLogic( restClient: RestClient, private val exchangeRateDao: ExchangeRateDao @@ -36,7 +37,7 @@ class ExchangeRatesLogic( baseCurrency = baseCurrency, currency = currency, rate = rate - ) + ).toEntity() ) } } catch (e: Exception) { @@ -45,7 +46,7 @@ class ExchangeRatesLogic( } } - fun amountBaseCurrency( + suspend fun amountBaseCurrency( plannedPayment: PlannedPaymentRule, baseCurrency: String, accounts: List //helper @@ -58,36 +59,36 @@ class ExchangeRatesLogic( ) } - fun amountBaseCurrency( + suspend fun amountBaseCurrency( transaction: Transaction, baseCurrency: String, accounts: List //helper ): Double { return amountBaseCurrency( - amount = transaction.amount, + amount = transaction.amount.toDouble(), accountId = transaction.accountId, baseCurrency = baseCurrency, accounts = accounts ) } - fun toAmountBaseCurrency( + suspend fun toAmountBaseCurrency( transaction: Transaction, baseCurrency: String, accounts: List //helper ): Double { val amount = transaction.toAmount ?: transaction.amount val toCurrency = accounts.find { it.id == transaction.toAccountId }?.currency - ?: return amount // no conversion + ?: return amount.toDouble() // no conversion return amountBaseCurrency( - amount = amount, + amount = amount.toDouble(), amountCurrency = toCurrency, baseCurrency = baseCurrency ) } - private fun amountBaseCurrency( + private suspend fun amountBaseCurrency( amount: Double, accountId: UUID, baseCurrency: String, @@ -103,7 +104,7 @@ class ExchangeRatesLogic( ) } - fun amountBaseCurrency( + suspend fun amountBaseCurrency( amount: Double, amountCurrency: String, baseCurrency: String @@ -117,7 +118,7 @@ class ExchangeRatesLogic( } } - fun convertAmount( + suspend fun convertAmount( baseCurrency: String, amount: Double, fromCurrency: String, @@ -133,7 +134,7 @@ class ExchangeRatesLogic( /** * base = BGN, currency = EUR => rate = 0.51 */ - private fun exchangeRate( + private suspend fun exchangeRate( baseCurrency: String, currency: String ): Double { @@ -148,7 +149,8 @@ class ExchangeRatesLogic( } } -fun Iterable.sumInBaseCurrency( +@Deprecated("Use FP style, look into `domain.fp` package") +suspend fun Iterable.sumInBaseCurrency( exchangeRatesLogic: ExchangeRatesLogic, settingsDao: SettingsDao, accountDao: AccountDao, @@ -160,12 +162,13 @@ fun Iterable.sumInBaseCurrency( exchangeRatesLogic.amountBaseCurrency( transaction = it, baseCurrency = baseCurrency, - accounts = accounts + accounts = accounts.map { it.toDomain() } ) } } -fun Iterable.sumByDoublePlannedInBaseCurrency( +@Deprecated("Use FP style, look into `domain.fp` package") +suspend fun Iterable.sumByDoublePlannedInBaseCurrency( exchangeRatesLogic: ExchangeRatesLogic, settingsDao: SettingsDao, accountDao: AccountDao, @@ -177,7 +180,7 @@ fun Iterable.sumByDoublePlannedInBaseCurrency( exchangeRatesLogic.amountBaseCurrency( plannedPayment = it, baseCurrency = baseCurrency, - accounts = accounts + accounts = accounts.map { it.toDomain() } ) } } \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/loantrasactions/LTLoanMapper.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/loantrasactions/LTLoanMapper.kt similarity index 71% rename from app/src/main/java/com/ivy/wallet/domain/logic/loantrasactions/LTLoanMapper.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/loantrasactions/LTLoanMapper.kt index dbf9afe159..170336fe36 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/loantrasactions/LTLoanMapper.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/loantrasactions/LTLoanMapper.kt @@ -1,12 +1,12 @@ -package com.ivy.wallet.domain.logic.loantrasactions +package com.ivy.wallet.domain.deprecated.logic.loantrasactions import com.ivy.wallet.domain.data.LoanType import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.Loan -import com.ivy.wallet.domain.data.entity.LoanRecord -import com.ivy.wallet.domain.data.entity.Transaction -import com.ivy.wallet.domain.logic.model.CreateLoanData +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Loan +import com.ivy.wallet.domain.data.core.LoanRecord +import com.ivy.wallet.domain.data.core.Transaction +import com.ivy.wallet.domain.deprecated.logic.model.CreateLoanData import com.ivy.wallet.utils.computationThread import com.ivy.wallet.utils.scopedIOThread import kotlinx.coroutines.async @@ -60,7 +60,7 @@ class LTLoanMapper( newLoanAccountId: UUID?, loanId: UUID ) { - val accounts = ltCore.fetchAccounts() + val accounts = ltCore.fetchAccounts().map { it.toDomain() } computationThread { if (oldLoanAccountId == newLoanAccountId || oldLoanAccountId.fetchAssociatedCurrencyCode( @@ -100,13 +100,13 @@ class LTLoanMapper( } val modifiedLoan = loan.copy( - amount = transaction.amount, + amount = transaction.amount.toDouble(), name = if (transaction.title.isNullOrEmpty()) loan.name else transaction.title, type = if (transaction.type == TransactionType.INCOME) LoanType.BORROW else LoanType.LEND, accountId = transaction.accountId ) - ltCore.saveLoan(modifiedLoan) + ltCore.saveLoan(modifiedLoan.toDomain()) } onBackgroundProcessingEnd() } @@ -117,21 +117,23 @@ class LTLoanMapper( ): List { return scopedIOThread { scope -> val loanRecords = - ltCore.fetchAllLoanRecords(loanId = loanId).map { loanRecord -> - scope.async { - val convertedAmount: Double? = - ltCore.computeConvertedAmount( - oldLoanRecordAccountId = loanRecord.accountId, - oldLonRecordConvertedAmount = loanRecord.convertedAmount, - oldLoanRecordAmount = loanRecord.amount, - newLoanRecordAccountID = loanRecord.accountId, - newLoanRecordAmount = loanRecord.amount, - loanAccountId = newAccountId, - accounts = ltCore.fetchAccounts(), - ) - loanRecord.copy(convertedAmount = convertedAmount) - } - }.awaitAll() + ltCore.fetchAllLoanRecords(loanId = loanId) + .map { it.toDomain() } + .map { loanRecord -> + scope.async { + val convertedAmount: Double? = + ltCore.computeConvertedAmount( + oldLoanRecordAccountId = loanRecord.accountId, + oldLonRecordConvertedAmount = loanRecord.convertedAmount, + oldLoanRecordAmount = loanRecord.amount, + newLoanRecordAccountID = loanRecord.accountId, + newLoanRecordAmount = loanRecord.amount, + loanAccountId = newAccountId, + accounts = ltCore.fetchAccounts().map { it.toDomain() }, + ) + loanRecord.copy(convertedAmount = convertedAmount) + } + }.awaitAll() loanRecords } } diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/loantrasactions/LTLoanRecordMapper.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/loantrasactions/LTLoanRecordMapper.kt similarity index 85% rename from app/src/main/java/com/ivy/wallet/domain/logic/loantrasactions/LTLoanRecordMapper.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/loantrasactions/LTLoanRecordMapper.kt index 78e443409e..88cc9ba82a 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/loantrasactions/LTLoanRecordMapper.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/loantrasactions/LTLoanRecordMapper.kt @@ -1,9 +1,9 @@ -package com.ivy.wallet.domain.logic.loantrasactions +package com.ivy.wallet.domain.deprecated.logic.loantrasactions -import com.ivy.wallet.domain.data.entity.Loan -import com.ivy.wallet.domain.data.entity.LoanRecord -import com.ivy.wallet.domain.data.entity.Transaction -import com.ivy.wallet.domain.logic.model.CreateLoanRecordData +import com.ivy.wallet.domain.data.core.Loan +import com.ivy.wallet.domain.data.core.LoanRecord +import com.ivy.wallet.domain.data.core.Transaction +import com.ivy.wallet.domain.deprecated.logic.model.CreateLoanRecordData import com.ivy.wallet.utils.computationThread import java.util.* @@ -75,19 +75,19 @@ class LTLoanRecordMapper( oldLonRecordConvertedAmount = loanRecord.convertedAmount, oldLoanRecordAmount = loanRecord.amount, newLoanRecordAccountID = transaction.accountId, - newLoanRecordAmount = transaction.amount, + newLoanRecordAmount = transaction.amount.toDouble(), loanAccountId = loan.accountId, - accounts = ltCore.fetchAccounts() + accounts = ltCore.fetchAccounts().map { it.toDomain() } ) val modifiedLoanRecord = loanRecord.copy( - amount = transaction.amount, + amount = transaction.amount.toDouble(), note = transaction.title, dateTime = transaction.dateTime ?: loanRecord.dateTime, accountId = transaction.accountId, convertedAmount = convertedAmount ) - ltCore.saveLoanRecords(modifiedLoanRecord) + ltCore.saveLoanRecords(modifiedLoanRecord.toDomain()) } onBackgroundProcessingEnd() } @@ -105,7 +105,7 @@ class LTLoanRecordMapper( newLoanRecordAccountID = newLoanRecord.accountId, newLoanRecordAmount = newLoanRecord.amount, loanAccountId = loanAccountId, - accounts = ltCore.fetchAccounts(), + accounts = ltCore.fetchAccounts().map { it.toDomain() }, reCalculateLoanAmount = reCalculateLoanAmount ) } @@ -121,7 +121,7 @@ class LTLoanRecordMapper( newLoanRecordAccountID = data.account?.id, newLoanRecordAmount = data.amount, loanAccountId = loanAccountId, - accounts = ltCore.fetchAccounts(), + accounts = ltCore.fetchAccounts().map { it.toDomain() }, ) } } \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/loantrasactions/LoanTransactionsCore.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/loantrasactions/LoanTransactionsCore.kt similarity index 90% rename from app/src/main/java/com/ivy/wallet/domain/logic/loantrasactions/LoanTransactionsCore.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/loantrasactions/LoanTransactionsCore.kt index 317573060c..82d4efe386 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/loantrasactions/LoanTransactionsCore.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/loantrasactions/LoanTransactionsCore.kt @@ -1,12 +1,14 @@ -package com.ivy.wallet.domain.logic.loantrasactions +package com.ivy.wallet.domain.deprecated.logic.loantrasactions import androidx.compose.ui.graphics.toArgb +import com.ivy.wallet.R import com.ivy.wallet.domain.data.LoanType import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.data.entity.* -import com.ivy.wallet.domain.logic.currency.ExchangeRatesLogic -import com.ivy.wallet.domain.sync.uploader.TransactionUploader +import com.ivy.wallet.domain.data.core.* +import com.ivy.wallet.domain.deprecated.logic.currency.ExchangeRatesLogic +import com.ivy.wallet.domain.deprecated.sync.uploader.TransactionUploader import com.ivy.wallet.io.persistence.dao.* +import com.ivy.wallet.stringRes import com.ivy.wallet.ui.IvyWalletCtx import com.ivy.wallet.ui.theme.components.IVY_COLOR_PICKER_COLORS_FREE import com.ivy.wallet.utils.computationThread @@ -46,8 +48,9 @@ class LoanTransactionsCore( ioThread { val transactions: List = - if (loanId != null) transactionDao.findAllByLoanId(loanId = loanId) else - listOf(transactionDao.findLoanRecordTransaction(loanRecordId!!)) + if (loanId != null) transactionDao.findAllByLoanId(loanId = loanId) + .map { it.toDomain() } else + listOf(transactionDao.findLoanRecordTransaction(loanRecordId!!)).map { it?.toDomain() } transactions.forEach { trans -> deleteTransaction(trans) @@ -142,7 +145,7 @@ class LoanTransactionsCore( val modifiedTransaction: Transaction = transaction?.copy( loanId = loanId, loanRecordId = if (isLoanRecord) loanRecordId else null, - amount = amount, + amount = amount.toBigDecimal(), type = transType, accountId = selectedAccountId, title = title, @@ -152,7 +155,7 @@ class LoanTransactionsCore( ?: Transaction( accountId = selectedAccountId, type = transType, - amount = amount, + amount = amount.toBigDecimal(), dateTime = time, categoryId = transCategoryId, title = title, @@ -161,7 +164,7 @@ class LoanTransactionsCore( ) ioThread { - transactionDao.save(modifiedTransaction) + transactionDao.save(modifiedTransaction.toEntity()) } } @@ -182,7 +185,7 @@ class LoanTransactionsCore( return existingCategoryId val categoryList = ioThread { - categoryDao.findAll() + categoryDao.findAll().map { it.toDomain() } } var addCategoryToDb = false @@ -192,7 +195,7 @@ class LoanTransactionsCore( } ?: if (ivyContext.isPremium || categoryList.size < 12) { addCategoryToDb = true Category( - "Loans", + stringRes(R.string.loans), color = IVY_COLOR_PICKER_COLORS_FREE[4].toArgb(), icon = "loan" ) @@ -201,7 +204,7 @@ class LoanTransactionsCore( if (addCategoryToDb) ioThread { loanCategory?.let { - categoryDao.save(it) + categoryDao.save(it.toEntity()) } } @@ -268,15 +271,15 @@ class LoanTransactionsCore( } suspend fun saveLoanRecords(loanRecords: List) = ioThread { - loanRecordDao.save(loanRecords) + loanRecordDao.save(loanRecords.map { it.toEntity() }) } suspend fun saveLoanRecords(loanRecord: LoanRecord) = ioThread { - loanRecordDao.save(loanRecord) + loanRecordDao.save(loanRecord.toEntity()) } suspend fun saveLoan(loan: Loan) = ioThread { - loanDao.save(loan) + loanDao.save(loan.toEntity()) } suspend fun fetchLoanRecord(loanRecordId: UUID) = ioThread { @@ -294,7 +297,7 @@ class LoanTransactionsCore( suspend fun fetchLoanRecordTransaction(loanRecordId: UUID?): Transaction? { return loanRecordId?.let { ioThread { - transactionDao.findLoanRecordTransaction(it) + transactionDao.findLoanRecordTransaction(it)?.toDomain() } } } diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/loantrasactions/LoanTransactionsLogic.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/loantrasactions/LoanTransactionsLogic.kt similarity index 91% rename from app/src/main/java/com/ivy/wallet/domain/logic/loantrasactions/LoanTransactionsLogic.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/loantrasactions/LoanTransactionsLogic.kt index 0ea34c1056..20c431e0a6 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/loantrasactions/LoanTransactionsLogic.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/loantrasactions/LoanTransactionsLogic.kt @@ -1,6 +1,6 @@ -package com.ivy.wallet.domain.logic.loantrasactions +package com.ivy.wallet.domain.deprecated.logic.loantrasactions -import com.ivy.wallet.domain.data.entity.Transaction +import com.ivy.wallet.domain.data.core.Transaction import com.ivy.wallet.utils.computationThread data class LoanTransactionsLogic( diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/model/CreateAccountData.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/model/CreateAccountData.kt similarity index 81% rename from app/src/main/java/com/ivy/wallet/domain/logic/model/CreateAccountData.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/model/CreateAccountData.kt index afd0854982..2def1f6323 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/model/CreateAccountData.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/model/CreateAccountData.kt @@ -1,4 +1,4 @@ -package com.ivy.wallet.domain.logic.model +package com.ivy.wallet.domain.deprecated.logic.model import androidx.compose.ui.graphics.Color diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/model/CreateBudgetData.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/model/CreateBudgetData.kt similarity index 74% rename from app/src/main/java/com/ivy/wallet/domain/logic/model/CreateBudgetData.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/model/CreateBudgetData.kt index 99a6ef502b..db29ec3812 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/model/CreateBudgetData.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/model/CreateBudgetData.kt @@ -1,4 +1,4 @@ -package com.ivy.wallet.domain.logic.model +package com.ivy.wallet.domain.deprecated.logic.model data class CreateBudgetData( val name: String, diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/model/CreateCategoryData.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/model/CreateCategoryData.kt similarity index 72% rename from app/src/main/java/com/ivy/wallet/domain/logic/model/CreateCategoryData.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/model/CreateCategoryData.kt index b9913ba77f..95be242474 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/model/CreateCategoryData.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/model/CreateCategoryData.kt @@ -1,4 +1,4 @@ -package com.ivy.wallet.domain.logic.model +package com.ivy.wallet.domain.deprecated.logic.model import androidx.compose.ui.graphics.Color diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/model/CreateLoanData.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/model/CreateLoanData.kt similarity index 75% rename from app/src/main/java/com/ivy/wallet/domain/logic/model/CreateLoanData.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/model/CreateLoanData.kt index 6bd69df44e..826613c6e8 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/model/CreateLoanData.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/model/CreateLoanData.kt @@ -1,8 +1,8 @@ -package com.ivy.wallet.domain.logic.model +package com.ivy.wallet.domain.deprecated.logic.model import androidx.compose.ui.graphics.Color import com.ivy.wallet.domain.data.LoanType -import com.ivy.wallet.domain.data.entity.Account +import com.ivy.wallet.domain.data.core.Account data class CreateLoanData( val name: String, diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/model/CreateLoanRecordData.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/model/CreateLoanRecordData.kt similarity index 75% rename from app/src/main/java/com/ivy/wallet/domain/logic/model/CreateLoanRecordData.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/model/CreateLoanRecordData.kt index 7392dfde34..df578879ea 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/model/CreateLoanRecordData.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/model/CreateLoanRecordData.kt @@ -1,6 +1,6 @@ -package com.ivy.wallet.domain.logic.model +package com.ivy.wallet.domain.deprecated.logic.model -import com.ivy.wallet.domain.data.entity.Account +import com.ivy.wallet.domain.data.core.Account import java.time.LocalDateTime data class CreateLoanRecordData( diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/model/CustomerJourneyCardData.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/model/CustomerJourneyCardData.kt similarity index 91% rename from app/src/main/java/com/ivy/wallet/domain/logic/model/CustomerJourneyCardData.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/model/CustomerJourneyCardData.kt index 0609932a1b..543c9c739f 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/model/CustomerJourneyCardData.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/model/CustomerJourneyCardData.kt @@ -1,4 +1,4 @@ -package com.ivy.wallet.domain.logic.model +package com.ivy.wallet.domain.deprecated.logic.model import androidx.annotation.DrawableRes import androidx.compose.ui.graphics.Color diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/model/EditLoanRecordData.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/model/EditLoanRecordData.kt similarity index 67% rename from app/src/main/java/com/ivy/wallet/domain/logic/model/EditLoanRecordData.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/model/EditLoanRecordData.kt index 93460dad76..542db83cc9 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/model/EditLoanRecordData.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/model/EditLoanRecordData.kt @@ -1,6 +1,6 @@ -package com.ivy.wallet.domain.logic.model +package com.ivy.wallet.domain.deprecated.logic.model -import com.ivy.wallet.domain.data.entity.LoanRecord +import com.ivy.wallet.domain.data.core.LoanRecord data class EditLoanRecordData( val newLoanRecord: LoanRecord, diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/notification/TransactionReminderLogic.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/notification/TransactionReminderLogic.kt similarity index 95% rename from app/src/main/java/com/ivy/wallet/domain/logic/notification/TransactionReminderLogic.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/notification/TransactionReminderLogic.kt index a6126d5f4c..79b6a5e742 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/notification/TransactionReminderLogic.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/notification/TransactionReminderLogic.kt @@ -1,4 +1,4 @@ -package com.ivy.wallet.domain.logic.notification +package com.ivy.wallet.domain.deprecated.logic.notification import android.content.Context import androidx.work.ExistingPeriodicWorkPolicy @@ -9,6 +9,7 @@ import com.ivy.wallet.utils.timeNowLocal import com.ivy.wallet.utils.toEpochSeconds import java.util.concurrent.TimeUnit +@Deprecated("Use FP style, look into `domain.fp` package") class TransactionReminderLogic( private val appContext: Context, private val sharedPrefs: SharedPrefs, diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/notification/TransactionReminderWorker.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/notification/TransactionReminderWorker.kt similarity index 91% rename from app/src/main/java/com/ivy/wallet/domain/logic/notification/TransactionReminderWorker.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/notification/TransactionReminderWorker.kt index 46e1e0f823..8b9c854d37 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/notification/TransactionReminderWorker.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/notification/TransactionReminderWorker.kt @@ -1,4 +1,4 @@ -package com.ivy.wallet.domain.logic.notification +package com.ivy.wallet.domain.deprecated.logic.notification import android.app.PendingIntent import android.content.Context @@ -6,10 +6,12 @@ import androidx.core.app.NotificationCompat import androidx.hilt.work.HiltWorker import androidx.work.CoroutineWorker import androidx.work.WorkerParameters +import com.ivy.wallet.R import com.ivy.wallet.android.notification.IvyNotificationChannel import com.ivy.wallet.android.notification.NotificationService import com.ivy.wallet.io.persistence.SharedPrefs import com.ivy.wallet.io.persistence.dao.TransactionDao +import com.ivy.wallet.stringRes import com.ivy.wallet.ui.RootActivity import com.ivy.wallet.utils.atEndOfDay import com.ivy.wallet.utils.dateNowUTC @@ -68,9 +70,9 @@ class TransactionReminderWorker @AssistedInject constructor( private fun randomText(): String = listOf( - "Have you made any transactions today? \uD83C\uDFC1", - "Did you track your expenses today? \uD83D\uDCB8", - "Have you recorded your transactions today? \uD83C\uDFC1", + stringRes(R.string.notification_1), + stringRes(R.string.notification_2), + stringRes(R.string.notification_3), ).shuffled().first() private fun fetchShowNotifications(): Boolean = diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/zip/ExportZipLogic.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/zip/ExportZipLogic.kt similarity index 93% rename from app/src/main/java/com/ivy/wallet/domain/logic/zip/ExportZipLogic.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/zip/ExportZipLogic.kt index ad1908cc8c..734d7fbb1d 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/zip/ExportZipLogic.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/zip/ExportZipLogic.kt @@ -1,12 +1,12 @@ -package com.ivy.wallet.domain.logic.zip +package com.ivy.wallet.domain.deprecated.logic.zip import android.content.Context import android.net.Uri import androidx.core.net.toUri import com.google.gson.* import com.google.gson.reflect.TypeToken -import com.ivy.wallet.domain.IvyWalletCompleteData -import com.ivy.wallet.domain.logic.csv.model.ImportResult +import com.ivy.wallet.domain.data.IvyWalletCompleteData +import com.ivy.wallet.domain.deprecated.logic.csv.model.ImportResult import com.ivy.wallet.io.persistence.SharedPrefs import com.ivy.wallet.io.persistence.dao.* import com.ivy.wallet.utils.ioThread @@ -61,7 +61,8 @@ class ExportZipLogic( val categories = it.async { categoryDao.findAll() } val loanRecords = it.async { loanRecordDao.findAll() } val loans = it.async { loanDao.findAll() } - val plannedPaymentRules = it.async { plannedPaymentRuleDao.findAll() } + val plannedPaymentRules = + it.async { plannedPaymentRuleDao.findAll() } val settings = it.async { settingsDao.findAll() } val transactions = it.async { transactionDao.findAll() } val sharedPrefs = it.async { getSharedPrefsData() } @@ -105,6 +106,9 @@ class ExportZipLogic( hashmap[SharedPrefs.HIDE_CURRENT_BALANCE] = sharedPrefs.getBoolean(SharedPrefs.HIDE_CURRENT_BALANCE, false).toString() + hashmap[SharedPrefs.TRANSFERS_AS_INCOME_EXPENSE] = + sharedPrefs.getBoolean(SharedPrefs.TRANSFERS_AS_INCOME_EXPENSE, false).toString() + return hashmap } @@ -221,7 +225,8 @@ class ExportZipLogic( val accounts = it.async { accountDao.save(completeData.accounts) } val budgets = it.async { budgetDao.save(completeData.budgets) } - val categories = it.async { categoryDao.save(completeData.categories) } + val categories = + it.async { categoryDao.save(completeData.categories) } accounts.await() budgets.await() categories.await() @@ -229,7 +234,8 @@ class ExportZipLogic( onProgress(0.7) val loans = it.async { loanDao.save(completeData.loans) } - val loanRecords = it.async { loanRecordDao.save(completeData.loanRecords) } + val loanRecords = + it.async { loanRecordDao.save(completeData.loanRecords) } loans.await() loanRecords.await() @@ -258,6 +264,11 @@ class ExportZipLogic( (completeData.sharedPrefs[SharedPrefs.HIDE_CURRENT_BALANCE] ?: "false").toBoolean() ) + sharedPrefs.putBoolean( + SharedPrefs.TRANSFERS_AS_INCOME_EXPENSE, + (completeData.sharedPrefs[SharedPrefs.TRANSFERS_AS_INCOME_EXPENSE] ?: "false").toBoolean() + ) + plannedPayments.await() settings.await() diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/zip/ZipUtils.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/zip/ZipUtils.kt similarity index 98% rename from app/src/main/java/com/ivy/wallet/domain/logic/zip/ZipUtils.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/logic/zip/ZipUtils.kt index 8139069415..8b35ba1015 100644 --- a/app/src/main/java/com/ivy/wallet/domain/logic/zip/ZipUtils.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/logic/zip/ZipUtils.kt @@ -1,4 +1,4 @@ -package com.ivy.wallet.domain.logic.zip +package com.ivy.wallet.domain.deprecated.logic.zip import android.content.Context import android.net.Uri diff --git a/app/src/main/java/com/ivy/wallet/domain/sync/IvySync.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/sync/IvySync.kt similarity index 89% rename from app/src/main/java/com/ivy/wallet/domain/sync/IvySync.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/sync/IvySync.kt index d2791015d8..6e6d1588c7 100644 --- a/app/src/main/java/com/ivy/wallet/domain/sync/IvySync.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/sync/IvySync.kt @@ -1,6 +1,6 @@ -package com.ivy.wallet.domain.sync +package com.ivy.wallet.domain.deprecated.sync -import com.ivy.wallet.domain.sync.item.* +import com.ivy.wallet.domain.deprecated.sync.item.* import com.ivy.wallet.io.network.IvySession class IvySync( @@ -13,7 +13,7 @@ class IvySync( private val loanRecordSync: LoanRecordSync, private val ivySession: IvySession ) { - fun isSynced(): Boolean { + suspend fun isSynced(): Boolean { return accountSync.isSynced() && categorySync.isSynced() && transactionSync.isSynced() && diff --git a/app/src/main/java/com/ivy/wallet/domain/sync/item/AccountSync.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/sync/item/AccountSync.kt similarity index 88% rename from app/src/main/java/com/ivy/wallet/domain/sync/item/AccountSync.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/sync/item/AccountSync.kt index bf0a4d6dad..0aa9f1184b 100644 --- a/app/src/main/java/com/ivy/wallet/domain/sync/item/AccountSync.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/sync/item/AccountSync.kt @@ -1,6 +1,6 @@ -package com.ivy.wallet.domain.sync.item +package com.ivy.wallet.domain.deprecated.sync.item -import com.ivy.wallet.domain.sync.uploader.AccountUploader +import com.ivy.wallet.domain.deprecated.sync.uploader.AccountUploader import com.ivy.wallet.io.network.IvySession import com.ivy.wallet.io.network.RestClient import com.ivy.wallet.io.persistence.SharedPrefs @@ -17,7 +17,7 @@ class AccountSync( ) { private val service = restClient.accountService - fun isSynced(): Boolean = + suspend fun isSynced(): Boolean = dao.findByIsSyncedAndIsDeleted(synced = false, deleted = false).isEmpty() && dao.findByIsSyncedAndIsDeleted(synced = false, deleted = true).isEmpty() @@ -40,7 +40,7 @@ class AccountSync( ) for (item in toSync) { - uploader.sync(item) + uploader.sync(item.toDomain()) } } @@ -63,7 +63,7 @@ class AccountSync( response.accounts.forEach { item -> dao.save( - item.copy( + item.toEntity().copy( isSynced = true, isDeleted = false ) diff --git a/app/src/main/java/com/ivy/wallet/domain/sync/item/BudgetSync.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/sync/item/BudgetSync.kt similarity index 88% rename from app/src/main/java/com/ivy/wallet/domain/sync/item/BudgetSync.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/sync/item/BudgetSync.kt index ed5158164b..2b6f644cd4 100644 --- a/app/src/main/java/com/ivy/wallet/domain/sync/item/BudgetSync.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/sync/item/BudgetSync.kt @@ -1,6 +1,6 @@ -package com.ivy.wallet.domain.sync.item +package com.ivy.wallet.domain.deprecated.sync.item -import com.ivy.wallet.domain.sync.uploader.BudgetUploader +import com.ivy.wallet.domain.deprecated.sync.uploader.BudgetUploader import com.ivy.wallet.io.network.IvySession import com.ivy.wallet.io.network.RestClient import com.ivy.wallet.io.persistence.SharedPrefs @@ -17,7 +17,7 @@ class BudgetSync( ) { private val service = restClient.budgetService - fun isSynced(): Boolean = + suspend fun isSynced(): Boolean = dao.findByIsSyncedAndIsDeleted(synced = false, deleted = false).isEmpty() && dao.findByIsSyncedAndIsDeleted(synced = false, deleted = true).isEmpty() @@ -40,7 +40,7 @@ class BudgetSync( ) for (item in toSync) { - uploader.sync(item) + uploader.sync(item.toDomain()) } } @@ -63,7 +63,7 @@ class BudgetSync( response.budgets.forEach { item -> dao.save( - item.copy( + item.toEntity().copy( isSynced = true, isDeleted = false ) diff --git a/app/src/main/java/com/ivy/wallet/domain/sync/item/CategorySync.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/sync/item/CategorySync.kt similarity index 88% rename from app/src/main/java/com/ivy/wallet/domain/sync/item/CategorySync.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/sync/item/CategorySync.kt index 26e7553aa2..3d9146b160 100644 --- a/app/src/main/java/com/ivy/wallet/domain/sync/item/CategorySync.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/sync/item/CategorySync.kt @@ -1,6 +1,6 @@ -package com.ivy.wallet.domain.sync.item +package com.ivy.wallet.domain.deprecated.sync.item -import com.ivy.wallet.domain.sync.uploader.CategoryUploader +import com.ivy.wallet.domain.deprecated.sync.uploader.CategoryUploader import com.ivy.wallet.io.network.IvySession import com.ivy.wallet.io.network.RestClient import com.ivy.wallet.io.persistence.SharedPrefs @@ -17,7 +17,7 @@ class CategorySync( ) { private val service = restClient.categoryService - fun isSynced(): Boolean = + suspend fun isSynced(): Boolean = dao.findByIsSyncedAndIsDeleted(synced = false, deleted = false).isEmpty() && dao.findByIsSyncedAndIsDeleted(synced = false, deleted = true).isEmpty() @@ -40,7 +40,7 @@ class CategorySync( ) for (item in toSync) { - uploader.sync(item) + uploader.sync(item.toDomain()) } } @@ -63,7 +63,7 @@ class CategorySync( response.categories.forEach { item -> dao.save( - item.copy( + item.toEntity().copy( isSynced = true, isDeleted = false ) diff --git a/app/src/main/java/com/ivy/wallet/domain/sync/item/LoanRecordSync.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/sync/item/LoanRecordSync.kt similarity index 88% rename from app/src/main/java/com/ivy/wallet/domain/sync/item/LoanRecordSync.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/sync/item/LoanRecordSync.kt index b3209ad56a..38f48e19f5 100644 --- a/app/src/main/java/com/ivy/wallet/domain/sync/item/LoanRecordSync.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/sync/item/LoanRecordSync.kt @@ -1,6 +1,6 @@ -package com.ivy.wallet.domain.sync.item +package com.ivy.wallet.domain.deprecated.sync.item -import com.ivy.wallet.domain.sync.uploader.LoanRecordUploader +import com.ivy.wallet.domain.deprecated.sync.uploader.LoanRecordUploader import com.ivy.wallet.io.network.IvySession import com.ivy.wallet.io.network.RestClient import com.ivy.wallet.io.persistence.SharedPrefs @@ -17,7 +17,7 @@ class LoanRecordSync( ) { private val service = restClient.loanService - fun isSynced(): Boolean = + suspend fun isSynced(): Boolean = dao.findByIsSyncedAndIsDeleted(synced = false, deleted = false).isEmpty() && dao.findByIsSyncedAndIsDeleted(synced = false, deleted = true).isEmpty() @@ -40,7 +40,7 @@ class LoanRecordSync( ) for (item in toSync) { - uploader.sync(item) + uploader.sync(item.toDomain()) } } @@ -64,7 +64,7 @@ class LoanRecordSync( response.loanRecords.forEach { item -> dao.save( - item.copy( + item.toEntity().copy( isSynced = true, isDeleted = false ) diff --git a/app/src/main/java/com/ivy/wallet/domain/sync/item/LoanSync.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/sync/item/LoanSync.kt similarity index 88% rename from app/src/main/java/com/ivy/wallet/domain/sync/item/LoanSync.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/sync/item/LoanSync.kt index c6889ac7fd..243d05c56a 100644 --- a/app/src/main/java/com/ivy/wallet/domain/sync/item/LoanSync.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/sync/item/LoanSync.kt @@ -1,6 +1,6 @@ -package com.ivy.wallet.domain.sync.item +package com.ivy.wallet.domain.deprecated.sync.item -import com.ivy.wallet.domain.sync.uploader.LoanUploader +import com.ivy.wallet.domain.deprecated.sync.uploader.LoanUploader import com.ivy.wallet.io.network.IvySession import com.ivy.wallet.io.network.RestClient import com.ivy.wallet.io.persistence.SharedPrefs @@ -17,7 +17,7 @@ class LoanSync( ) { private val service = restClient.loanService - fun isSynced(): Boolean = + suspend fun isSynced(): Boolean = dao.findByIsSyncedAndIsDeleted(synced = false, deleted = false).isEmpty() && dao.findByIsSyncedAndIsDeleted(synced = false, deleted = true).isEmpty() @@ -40,7 +40,7 @@ class LoanSync( ) for (item in toSync) { - uploader.sync(item) + uploader.sync(item.toDomain()) } } @@ -63,7 +63,7 @@ class LoanSync( response.loans.forEach { item -> dao.save( - item.copy( + item.toEntity().copy( isSynced = true, isDeleted = false ) diff --git a/app/src/main/java/com/ivy/wallet/domain/sync/item/PlannedPaymentSync.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/sync/item/PlannedPaymentSync.kt similarity index 88% rename from app/src/main/java/com/ivy/wallet/domain/sync/item/PlannedPaymentSync.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/sync/item/PlannedPaymentSync.kt index 5d1e49823b..aa2ad770a7 100644 --- a/app/src/main/java/com/ivy/wallet/domain/sync/item/PlannedPaymentSync.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/sync/item/PlannedPaymentSync.kt @@ -1,6 +1,6 @@ -package com.ivy.wallet.domain.sync.item +package com.ivy.wallet.domain.deprecated.sync.item -import com.ivy.wallet.domain.sync.uploader.PlannedPaymentRuleUploader +import com.ivy.wallet.domain.deprecated.sync.uploader.PlannedPaymentRuleUploader import com.ivy.wallet.io.network.IvySession import com.ivy.wallet.io.network.RestClient import com.ivy.wallet.io.persistence.SharedPrefs @@ -17,7 +17,7 @@ class PlannedPaymentSync( ) { private val service = restClient.plannedPaymentRuleService - fun isSynced(): Boolean = + suspend fun isSynced(): Boolean = dao.findByIsSyncedAndIsDeleted(synced = false, deleted = false).isEmpty() && dao.findByIsSyncedAndIsDeleted(synced = false, deleted = true).isEmpty() @@ -41,7 +41,7 @@ class PlannedPaymentSync( ) for (item in toSync) { - uploader.sync(item) + uploader.sync(item.toDomain()) } } @@ -65,7 +65,7 @@ class PlannedPaymentSync( response.rules.forEach { item -> dao.save( - item.copy( + item.toEntity().copy( isSynced = true, isDeleted = false ) diff --git a/app/src/main/java/com/ivy/wallet/domain/sync/item/TransactionSync.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/sync/item/TransactionSync.kt similarity index 88% rename from app/src/main/java/com/ivy/wallet/domain/sync/item/TransactionSync.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/sync/item/TransactionSync.kt index 08924bf2a5..5c65746124 100644 --- a/app/src/main/java/com/ivy/wallet/domain/sync/item/TransactionSync.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/sync/item/TransactionSync.kt @@ -1,6 +1,6 @@ -package com.ivy.wallet.domain.sync.item +package com.ivy.wallet.domain.deprecated.sync.item -import com.ivy.wallet.domain.sync.uploader.TransactionUploader +import com.ivy.wallet.domain.deprecated.sync.uploader.TransactionUploader import com.ivy.wallet.io.network.IvySession import com.ivy.wallet.io.network.RestClient import com.ivy.wallet.io.persistence.SharedPrefs @@ -17,7 +17,7 @@ class TransactionSync( ) { private val service = restClient.transactionService - fun isSynced(): Boolean = + suspend fun isSynced(): Boolean = dao.findByIsSyncedAndIsDeleted(synced = false, deleted = false).isEmpty() && dao.findByIsSyncedAndIsDeleted(synced = false, deleted = true).isEmpty() @@ -40,7 +40,7 @@ class TransactionSync( ) for (item in toSync) { - uploader.sync(item) + uploader.sync(item.toDomain()) } } @@ -64,7 +64,7 @@ class TransactionSync( response.transactions.forEach { item -> dao.save( - item.copy( + item.toEntity().copy( isSynced = true, isDeleted = false ) diff --git a/app/src/main/java/com/ivy/wallet/domain/sync/uploader/AccountUploader.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/sync/uploader/AccountUploader.kt similarity index 91% rename from app/src/main/java/com/ivy/wallet/domain/sync/uploader/AccountUploader.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/sync/uploader/AccountUploader.kt index 9d0db4a07b..a71735cab2 100644 --- a/app/src/main/java/com/ivy/wallet/domain/sync/uploader/AccountUploader.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/sync/uploader/AccountUploader.kt @@ -1,6 +1,6 @@ -package com.ivy.wallet.domain.sync.uploader +package com.ivy.wallet.domain.deprecated.sync.uploader -import com.ivy.wallet.domain.data.entity.Account +import com.ivy.wallet.domain.data.core.Account import com.ivy.wallet.io.network.IvySession import com.ivy.wallet.io.network.RestClient import com.ivy.wallet.io.network.request.account.DeleteAccountRequest @@ -25,7 +25,7 @@ class AccountUploader( //update service.update( UpdateAccountRequest( - account = item + account = item.toDTO() ) ) @@ -33,7 +33,7 @@ class AccountUploader( accountDao.save( item.copy( isSynced = true - ) + ).toEntity() ) Timber.d("Account updated: $item.") } catch (e: Exception) { diff --git a/app/src/main/java/com/ivy/wallet/domain/sync/uploader/BudgetUploader.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/sync/uploader/BudgetUploader.kt similarity index 90% rename from app/src/main/java/com/ivy/wallet/domain/sync/uploader/BudgetUploader.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/sync/uploader/BudgetUploader.kt index 91a0b8b552..17d609d890 100644 --- a/app/src/main/java/com/ivy/wallet/domain/sync/uploader/BudgetUploader.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/sync/uploader/BudgetUploader.kt @@ -1,6 +1,6 @@ -package com.ivy.wallet.domain.sync.uploader +package com.ivy.wallet.domain.deprecated.sync.uploader -import com.ivy.wallet.domain.data.entity.Budget +import com.ivy.wallet.domain.data.core.Budget import com.ivy.wallet.io.network.IvySession import com.ivy.wallet.io.network.RestClient import com.ivy.wallet.io.network.request.budget.CrupdateBudgetRequest @@ -23,7 +23,7 @@ class BudgetUploader( //update service.update( CrupdateBudgetRequest( - budget = item + budget = item.toDTO() ) ) @@ -31,7 +31,7 @@ class BudgetUploader( dao.save( item.copy( isSynced = true - ) + ).toEntity() ) Timber.d("Budget updated: $item.") } catch (e: Exception) { diff --git a/app/src/main/java/com/ivy/wallet/domain/sync/uploader/CategoryUploader.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/sync/uploader/CategoryUploader.kt similarity index 90% rename from app/src/main/java/com/ivy/wallet/domain/sync/uploader/CategoryUploader.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/sync/uploader/CategoryUploader.kt index 43dab1c576..c0d452bdd1 100644 --- a/app/src/main/java/com/ivy/wallet/domain/sync/uploader/CategoryUploader.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/sync/uploader/CategoryUploader.kt @@ -1,6 +1,6 @@ -package com.ivy.wallet.domain.sync.uploader +package com.ivy.wallet.domain.deprecated.sync.uploader -import com.ivy.wallet.domain.data.entity.Category +import com.ivy.wallet.domain.data.core.Category import com.ivy.wallet.io.network.IvySession import com.ivy.wallet.io.network.RestClient import com.ivy.wallet.io.network.request.category.DeleteWalletCategoryRequest @@ -23,7 +23,7 @@ class CategoryUploader( //update service.update( UpdateWalletCategoryRequest( - category = item + category = item.toDTO() ) ) @@ -31,7 +31,7 @@ class CategoryUploader( dao.save( item.copy( isSynced = true - ) + ).toEntity() ) Timber.d("Category updated: $item.") } catch (e: Exception) { diff --git a/app/src/main/java/com/ivy/wallet/domain/sync/uploader/LoanRecordUploader.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/sync/uploader/LoanRecordUploader.kt similarity index 90% rename from app/src/main/java/com/ivy/wallet/domain/sync/uploader/LoanRecordUploader.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/sync/uploader/LoanRecordUploader.kt index 61bbb13ff5..14809c41ff 100644 --- a/app/src/main/java/com/ivy/wallet/domain/sync/uploader/LoanRecordUploader.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/sync/uploader/LoanRecordUploader.kt @@ -1,6 +1,6 @@ -package com.ivy.wallet.domain.sync.uploader +package com.ivy.wallet.domain.deprecated.sync.uploader -import com.ivy.wallet.domain.data.entity.LoanRecord +import com.ivy.wallet.domain.data.core.LoanRecord import com.ivy.wallet.io.network.IvySession import com.ivy.wallet.io.network.RestClient import com.ivy.wallet.io.network.request.loan.DeleteLoanRecordRequest @@ -23,7 +23,7 @@ class LoanRecordUploader( //update service.updateRecord( UpdateLoanRecordRequest( - loanRecord = item + loanRecord = item.toDTO() ) ) @@ -31,7 +31,7 @@ class LoanRecordUploader( dao.save( item.copy( isSynced = true - ) + ).toEntity() ) Timber.d("Loan record updated: $item.") } catch (e: Exception) { diff --git a/app/src/main/java/com/ivy/wallet/domain/sync/uploader/LoanUploader.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/sync/uploader/LoanUploader.kt similarity index 90% rename from app/src/main/java/com/ivy/wallet/domain/sync/uploader/LoanUploader.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/sync/uploader/LoanUploader.kt index 8107b16f04..5ddb17d2af 100644 --- a/app/src/main/java/com/ivy/wallet/domain/sync/uploader/LoanUploader.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/sync/uploader/LoanUploader.kt @@ -1,6 +1,6 @@ -package com.ivy.wallet.domain.sync.uploader +package com.ivy.wallet.domain.deprecated.sync.uploader -import com.ivy.wallet.domain.data.entity.Loan +import com.ivy.wallet.domain.data.core.Loan import com.ivy.wallet.io.network.IvySession import com.ivy.wallet.io.network.RestClient import com.ivy.wallet.io.network.request.loan.DeleteLoanRequest @@ -23,7 +23,7 @@ class LoanUploader( //update service.update( UpdateLoanRequest( - loan = item + loan = item.toDTO() ) ) @@ -31,7 +31,7 @@ class LoanUploader( dao.save( item.copy( isSynced = true - ) + ).toEntity() ) Timber.d("Loan updated: $item.") } catch (e: Exception) { diff --git a/app/src/main/java/com/ivy/wallet/domain/sync/uploader/PlannedPaymentRuleUploader.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/sync/uploader/PlannedPaymentRuleUploader.kt similarity index 90% rename from app/src/main/java/com/ivy/wallet/domain/sync/uploader/PlannedPaymentRuleUploader.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/sync/uploader/PlannedPaymentRuleUploader.kt index e6965922ed..a4d264f6dc 100644 --- a/app/src/main/java/com/ivy/wallet/domain/sync/uploader/PlannedPaymentRuleUploader.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/sync/uploader/PlannedPaymentRuleUploader.kt @@ -1,6 +1,6 @@ -package com.ivy.wallet.domain.sync.uploader +package com.ivy.wallet.domain.deprecated.sync.uploader -import com.ivy.wallet.domain.data.entity.PlannedPaymentRule +import com.ivy.wallet.domain.data.core.PlannedPaymentRule import com.ivy.wallet.io.network.IvySession import com.ivy.wallet.io.network.RestClient import com.ivy.wallet.io.network.request.planned.DeletePlannedPaymentRuleRequest @@ -23,7 +23,7 @@ class PlannedPaymentRuleUploader( //update service.update( UpdatePlannedPaymentRuleRequest( - rule = item + rule = item.toDTO() ) ) @@ -31,7 +31,7 @@ class PlannedPaymentRuleUploader( dao.save( item.copy( isSynced = true - ) + ).toEntity() ) Timber.d("PlannedPaymentRule updated: $item.") } catch (e: Exception) { diff --git a/app/src/main/java/com/ivy/wallet/domain/sync/uploader/TransactionUploader.kt b/app/src/main/java/com/ivy/wallet/domain/deprecated/sync/uploader/TransactionUploader.kt similarity index 90% rename from app/src/main/java/com/ivy/wallet/domain/sync/uploader/TransactionUploader.kt rename to app/src/main/java/com/ivy/wallet/domain/deprecated/sync/uploader/TransactionUploader.kt index f0c3008d4b..78cdabd6d5 100644 --- a/app/src/main/java/com/ivy/wallet/domain/sync/uploader/TransactionUploader.kt +++ b/app/src/main/java/com/ivy/wallet/domain/deprecated/sync/uploader/TransactionUploader.kt @@ -1,6 +1,6 @@ -package com.ivy.wallet.domain.sync.uploader +package com.ivy.wallet.domain.deprecated.sync.uploader -import com.ivy.wallet.domain.data.entity.Transaction +import com.ivy.wallet.domain.data.core.Transaction import com.ivy.wallet.io.network.IvySession import com.ivy.wallet.io.network.RestClient import com.ivy.wallet.io.network.request.transaction.DeleteTransactionRequest @@ -23,7 +23,7 @@ class TransactionUploader( //update service.update( UpdateTransactionRequest( - transaction = item + transaction = item.toDTO() ) ) @@ -31,7 +31,7 @@ class TransactionUploader( dao.save( item.copy( isSynced = true - ) + ).toEntity() ) Timber.d("Transaction updated: $item.") } catch (e: Exception) { diff --git a/app/src/main/java/com/ivy/wallet/domain/fp/account/AccountCore.kt b/app/src/main/java/com/ivy/wallet/domain/fp/account/AccountCore.kt deleted file mode 100644 index 493efeb1b4..0000000000 --- a/app/src/main/java/com/ivy/wallet/domain/fp/account/AccountCore.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.ivy.wallet.domain.fp.account - -import arrow.core.NonEmptyList -import com.ivy.wallet.domain.data.entity.Transaction -import com.ivy.wallet.domain.fp.core.Total -import com.ivy.wallet.domain.fp.core.calculateValueFunctionsSum -import com.ivy.wallet.domain.fp.data.ClosedTimeRange -import com.ivy.wallet.domain.fp.data.toFPTransaction -import com.ivy.wallet.io.persistence.dao.TransactionDao -import java.math.BigDecimal -import java.util.* - -@Total -suspend fun calculateAccountValues( - transactionDao: TransactionDao, - accountId: UUID, - range: ClosedTimeRange, - valueFunctions: NonEmptyList -): NonEmptyList { - return calculateAccountValues( - accountId = accountId, - retrieveAccountTransactions = { - transactionDao.findAllByAccountAndBetween( - accountId = accountId, - startDate = range.from, - endDate = range.to - ) - }, - retrieveToAccountTransfers = { - transactionDao.findAllToAccountAndBetween( - toAccountId = accountId, - startDate = range.from, - endDate = range.to - ) - }, - valueFunctions = valueFunctions - ) -} - -@Total -suspend fun calculateAccountValues( - accountId: UUID, - retrieveAccountTransactions: suspend (UUID) -> List, - retrieveToAccountTransfers: suspend (UUID) -> List, - valueFunctions: NonEmptyList -): NonEmptyList { - val accountTrns = retrieveAccountTransactions(accountId) - .plus(retrieveToAccountTransfers(accountId)) - .map { it.toFPTransaction() } - - return calculateValueFunctionsSum( - valueFunctionArgument = accountId, - transactions = accountTrns, - valueFunctions = valueFunctions - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/fp/account/AccountFunctions.kt b/app/src/main/java/com/ivy/wallet/domain/fp/account/AccountFunctions.kt deleted file mode 100644 index 20e99c8902..0000000000 --- a/app/src/main/java/com/ivy/wallet/domain/fp/account/AccountFunctions.kt +++ /dev/null @@ -1,82 +0,0 @@ -package com.ivy.wallet.domain.fp.account - -import arrow.core.nonEmptyListOf -import com.ivy.wallet.domain.fp.data.ClosedTimeRange -import com.ivy.wallet.domain.fp.data.IncomeExpensePair -import com.ivy.wallet.io.persistence.dao.TransactionDao -import java.math.BigDecimal -import java.util.* - - -suspend fun calculateAccountBalance( - transactionDao: TransactionDao, - accountId: UUID, - range: ClosedTimeRange = ClosedTimeRange.allTimeIvy() -): BigDecimal { - return calculateAccountValues( - transactionDao = transactionDao, - accountId = accountId, - range = range, - valueFunctions = nonEmptyListOf( - AccountValueFunctions::balance - ) - ).head -} - -data class AccountStats( - val balance: BigDecimal, - val income: BigDecimal, - val expense: BigDecimal, - val incomeCount: Int, - val expenseCount: Int -) - -suspend fun calculateAccountStats( - transactionDao: TransactionDao, - accountId: UUID, - range: ClosedTimeRange = ClosedTimeRange.allTimeIvy() -): AccountStats { - val values = calculateAccountValues( - transactionDao = transactionDao, - accountId = accountId, - range = range, - valueFunctions = nonEmptyListOf( - AccountValueFunctions::balance, - AccountValueFunctions::income, - AccountValueFunctions::expense, - AccountValueFunctions::incomeCount, - AccountValueFunctions::expenseCount - ) - ) - - return AccountStats( - balance = values[0], - income = values[1], - expense = values[2], - incomeCount = values[3].toInt(), - expenseCount = values[4].toInt() - ) -} - -suspend fun calculateAccountIncomeExpense( - transactionDao: TransactionDao, - accountId: UUID, - range: ClosedTimeRange = ClosedTimeRange.allTimeIvy() -): IncomeExpensePair { - val values = calculateAccountValues( - transactionDao = transactionDao, - accountId = accountId, - range = range, - valueFunctions = nonEmptyListOf( - AccountValueFunctions::income, - AccountValueFunctions::expense, - ) - ) - - return IncomeExpensePair( - income = values[0], - expense = values[1], - ) -} - - diff --git a/app/src/main/java/com/ivy/wallet/domain/fp/category/CategoryCore.kt b/app/src/main/java/com/ivy/wallet/domain/fp/category/CategoryCore.kt deleted file mode 100644 index 4ce2586dbb..0000000000 --- a/app/src/main/java/com/ivy/wallet/domain/fp/category/CategoryCore.kt +++ /dev/null @@ -1,101 +0,0 @@ -package com.ivy.wallet.domain.fp.category - -import arrow.core.NonEmptyList -import com.ivy.wallet.domain.data.entity.Transaction -import com.ivy.wallet.domain.fp.core.calculateValueFunctionsSumSuspend -import com.ivy.wallet.domain.fp.data.ClosedTimeRange -import com.ivy.wallet.domain.fp.data.WalletDAOs -import com.ivy.wallet.domain.fp.data.toFPTransaction -import java.math.BigDecimal -import java.util.* - -suspend fun calculateCategoryValues( - walletDAOs: WalletDAOs, - baseCurrencyCode: String, - categoryId: UUID?, - range: ClosedTimeRange, - valueFunctions: NonEmptyList -): NonEmptyList { - return calculateCategoryValues( - argument = CategoryValueFunctions.Argument( - categoryId = categoryId, - accounts = walletDAOs.accountDao.findAll(), - exchangeRateDao = walletDAOs.exchangeRateDao, - baseCurrencyCode = baseCurrencyCode - ), - retrieveCategoryTransactions = { forCategoryId -> - if (forCategoryId == null) { - walletDAOs.transactionDao.findAllUnspecifiedAndBetween( - startDate = range.from, - endDate = range.to - ) - } else { - walletDAOs.transactionDao.findAllByCategoryAndBetween( - categoryId = forCategoryId, - startDate = range.from, - endDate = range.to - ) - } - - }, - valueFunctions = valueFunctions - ) -} - -suspend fun calculateCategoryValuesWithAccountFilters( - walletDAOs: WalletDAOs, - baseCurrencyCode: String, - categoryId: UUID?, - range: ClosedTimeRange, - accountIdFilterSet: Set, - valueFunctions: NonEmptyList -): NonEmptyList { - return calculateCategoryValues( - argument = CategoryValueFunctions.Argument( - categoryId = categoryId, - accounts = walletDAOs.accountDao.findAll(), - exchangeRateDao = walletDAOs.exchangeRateDao, - baseCurrencyCode = baseCurrencyCode - ), - retrieveCategoryTransactions = { forCategoryId -> - if (forCategoryId == null) { - walletDAOs.transactionDao.findAllUnspecifiedAndBetween( - startDate = range.from, - endDate = range.to - ).filter { - if (accountIdFilterSet.isNotEmpty()) - accountIdFilterSet.contains(it.accountId) - else - true - } - } else { - walletDAOs.transactionDao.findAllByCategoryAndBetween( - categoryId = forCategoryId, - startDate = range.from, - endDate = range.to - ).filter { - if (accountIdFilterSet.isNotEmpty()) - accountIdFilterSet.contains(it.accountId) - else - true - } - } - }, - valueFunctions = valueFunctions - ) -} - -suspend fun calculateCategoryValues( - argument: CategoryValueFunctions.Argument, - retrieveCategoryTransactions: suspend (UUID?) -> List, - valueFunctions: NonEmptyList -): NonEmptyList { - val categoryTrns = retrieveCategoryTransactions(argument.categoryId) - .map { it.toFPTransaction() } - - return calculateValueFunctionsSumSuspend( - valueFunctionArgument = argument, - transactions = categoryTrns, - valueFunctions = valueFunctions - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/fp/category/CategoryFunctions.kt b/app/src/main/java/com/ivy/wallet/domain/fp/category/CategoryFunctions.kt deleted file mode 100644 index 2c17284b6f..0000000000 --- a/app/src/main/java/com/ivy/wallet/domain/fp/category/CategoryFunctions.kt +++ /dev/null @@ -1,135 +0,0 @@ -package com.ivy.wallet.domain.fp.category - -import arrow.core.nonEmptyListOf -import com.ivy.wallet.domain.fp.data.ClosedTimeRange -import com.ivy.wallet.domain.fp.data.WalletDAOs -import java.math.BigDecimal -import java.util.* - -suspend fun calculateCategoryBalance( - walletDAOs: WalletDAOs, - baseCurrencyCode: String, - categoryId: UUID?, - range: ClosedTimeRange, -): BigDecimal { - return calculateCategoryValues( - walletDAOs = walletDAOs, - baseCurrencyCode = baseCurrencyCode, - categoryId = categoryId, - range = range, - valueFunctions = nonEmptyListOf( - CategoryValueFunctions::balance - ) - ).head -} - -data class CategoryStats( - val balance: BigDecimal, - val income: BigDecimal, - val expense: BigDecimal, - val incomeCount: BigDecimal, - val expenseCount: BigDecimal -) - -suspend fun calculateCategoryStats( - walletDAOs: WalletDAOs, - baseCurrencyCode: String, - categoryId: UUID?, - range: ClosedTimeRange, -): CategoryStats { - val values = calculateCategoryValues( - walletDAOs = walletDAOs, - baseCurrencyCode = baseCurrencyCode, - categoryId = categoryId, - range = range, - valueFunctions = nonEmptyListOf( - CategoryValueFunctions::income, - CategoryValueFunctions::expense, - CategoryValueFunctions::incomeCount, - CategoryValueFunctions::expenseCount - ) - ) - - val income = values[0] - val expense = values[1] - - return CategoryStats( - balance = income - expense, - income = income, - expense = expense, - incomeCount = values[2], - expenseCount = values[3] - ) -} - -suspend fun calculateCategoryIncome( - walletDAOs: WalletDAOs, - baseCurrencyCode: String, - categoryId: UUID?, - range: ClosedTimeRange, -): BigDecimal { - return calculateCategoryValues( - walletDAOs = walletDAOs, - baseCurrencyCode = baseCurrencyCode, - categoryId = categoryId, - range = range, - valueFunctions = nonEmptyListOf( - CategoryValueFunctions::income - ) - ).head -} - -suspend fun calculateCategoryIncomeWithAccountFilters( - walletDAOs: WalletDAOs, - baseCurrencyCode: String, - categoryId: UUID?, - accountIdFilterList: List, - range: ClosedTimeRange, -): BigDecimal { - return calculateCategoryValuesWithAccountFilters( - walletDAOs = walletDAOs, - baseCurrencyCode = baseCurrencyCode, - categoryId = categoryId, - range = range, - accountIdFilterSet = accountIdFilterList.toHashSet(), - valueFunctions = nonEmptyListOf( - CategoryValueFunctions::income - ) - ).head -} - -suspend fun calculateCategoryExpense( - walletDAOs: WalletDAOs, - baseCurrencyCode: String, - categoryId: UUID?, - range: ClosedTimeRange, -): BigDecimal { - return calculateCategoryValues( - walletDAOs = walletDAOs, - baseCurrencyCode = baseCurrencyCode, - categoryId = categoryId, - range = range, - valueFunctions = nonEmptyListOf( - CategoryValueFunctions::expense - ) - ).head -} - -suspend fun calculateCategoryExpenseWithAccountFilters( - walletDAOs: WalletDAOs, - baseCurrencyCode: String, - categoryId: UUID?, - accountIdList: List = emptyList(), - range: ClosedTimeRange, -): BigDecimal { - return calculateCategoryValuesWithAccountFilters( - walletDAOs = walletDAOs, - baseCurrencyCode = baseCurrencyCode, - categoryId = categoryId, - range = range, - accountIdFilterSet = accountIdList.toHashSet(), - valueFunctions = nonEmptyListOf( - CategoryValueFunctions::expense - ) - ).head -} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/fp/core/CoreValueFunctions.kt b/app/src/main/java/com/ivy/wallet/domain/fp/core/CoreValueFunctions.kt deleted file mode 100644 index ce1d113b37..0000000000 --- a/app/src/main/java/com/ivy/wallet/domain/fp/core/CoreValueFunctions.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.ivy.wallet.domain.fp.core - -import arrow.core.toOption -import com.ivy.wallet.domain.fp.data.FPTransaction -import com.ivy.wallet.domain.fp.exchange -import com.ivy.wallet.io.persistence.dao.AccountDao -import com.ivy.wallet.io.persistence.dao.ExchangeRateDao -import java.math.BigDecimal - -data class ExchangeData( - val exchangeRateDao: ExchangeRateDao, - val accountDao: AccountDao, - val baseCurrencyCode: String, - val toCurrency: String, -) - -suspend fun amountInCurrency(fpTransaction: FPTransaction, data: ExchangeData): BigDecimal { - val fromCurrencyCode = - data.accountDao.findById(fpTransaction.accountId)?.currency.toOption() - - return exchange( - exchangeRateDao = data.exchangeRateDao, - baseCurrencyCode = data.baseCurrencyCode, - fromCurrencyCode = fromCurrencyCode, - fromAmount = fpTransaction.amount, - toCurrencyCode = data.toCurrency - ).orNull() ?: BigDecimal.ZERO -} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/fp/core/FP.kt b/app/src/main/java/com/ivy/wallet/domain/fp/core/FP.kt deleted file mode 100644 index f5d7feb972..0000000000 --- a/app/src/main/java/com/ivy/wallet/domain/fp/core/FP.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.ivy.wallet.domain.fp.core - -@Target(AnnotationTarget.FUNCTION) -@Retention(AnnotationRetention.SOURCE) -@MustBeDocumented -annotation class Pure - -@Target(AnnotationTarget.FUNCTION) -@Retention(AnnotationRetention.SOURCE) -@MustBeDocumented -annotation class Total - -@Target(AnnotationTarget.FUNCTION) -@Retention(AnnotationRetention.SOURCE) -@MustBeDocumented -annotation class Partial(val inCaseOf: String = "") - - -infix fun ((A) -> B).compose(fn2: (B) -> C): (A) -> C = { a -> - val b = this(a) - val c = fn2(b) - c -} - -infix fun ((B) -> C).after(fn1: (A) -> B): (A) -> C = { a -> - val b = fn1(a) - val c = this(b) - c -} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/fp/core/TransactionFunctions.kt b/app/src/main/java/com/ivy/wallet/domain/fp/core/TransactionFunctions.kt deleted file mode 100644 index a48b38a31a..0000000000 --- a/app/src/main/java/com/ivy/wallet/domain/fp/core/TransactionFunctions.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.ivy.wallet.domain.fp.core - -import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.data.entity.Transaction -import com.ivy.wallet.domain.fp.data.FPTransaction -import com.ivy.wallet.domain.fp.data.toFPTransaction -import java.math.BigDecimal - -suspend fun sum( - transactions: List, - valueFunction: SuspendValueFunction, - argument: A -): BigDecimal { - return transactions.sumOf { - valueFunction(it, argument) - } -} - -fun expenses(transactions: List): List { - return transactions.filter { it.type == TransactionType.EXPENSE } -} - -fun incomes(transactions: List): List { - return transactions.filter { it.type == TransactionType.INCOME } -} - -fun transfers(transactions: List): List { - return transactions.filter { it.type == TransactionType.TRANSFER } -} - -fun List.toFPTransactions(): List { - return this.map { it.toFPTransaction() } -} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/fp/core/Uncertain.kt b/app/src/main/java/com/ivy/wallet/domain/fp/core/Uncertain.kt deleted file mode 100644 index 024501df1d..0000000000 --- a/app/src/main/java/com/ivy/wallet/domain/fp/core/Uncertain.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.ivy.wallet.domain.fp.core - -data class Uncertain, V>( - val error: E, - val value: V -) { - fun isCertain(): Boolean { - return error.isEmpty() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/fp/core/ValueFunction.kt b/app/src/main/java/com/ivy/wallet/domain/fp/core/ValueFunction.kt deleted file mode 100644 index 7dde3b18e5..0000000000 --- a/app/src/main/java/com/ivy/wallet/domain/fp/core/ValueFunction.kt +++ /dev/null @@ -1,48 +0,0 @@ -package com.ivy.wallet.domain.fp.core - -import arrow.core.NonEmptyList -import com.ivy.wallet.domain.fp.data.FPTransaction -import java.math.BigDecimal - -typealias ValueFunction = (FPTransaction, A) -> BigDecimal -typealias SuspendValueFunction = suspend (FPTransaction, A) -> BigDecimal - -internal tailrec fun calculateValueFunctionsSum( - valueFunctionArgument: A, - transactions: List, - valueFunctions: NonEmptyList>, - sum: NonEmptyList = nonEmptyListOfZeros(n = valueFunctions.size) -): NonEmptyList { - return if (transactions.isEmpty()) - sum - else - calculateValueFunctionsSum( - valueFunctionArgument = valueFunctionArgument, - transactions = transactions.drop(1), - valueFunctions = valueFunctions, - sum = sum.mapIndexedNel { index, sumValue -> - val valueFunction = valueFunctions[index] - sumValue + valueFunction(transactions.first(), valueFunctionArgument) - } - ) -} - -internal tailrec suspend fun calculateValueFunctionsSumSuspend( - valueFunctionArgument: A, - transactions: List, - valueFunctions: NonEmptyList>, - sum: NonEmptyList = nonEmptyListOfZeros(n = valueFunctions.size) -): NonEmptyList { - return if (transactions.isEmpty()) - sum - else - calculateValueFunctionsSumSuspend( - valueFunctionArgument = valueFunctionArgument, - transactions = transactions.drop(1), - valueFunctions = valueFunctions, - sum = sum.mapIndexedNelSuspend { index, sumValue -> - val valueFunction = valueFunctions[index] - sumValue + valueFunction(transactions.first(), valueFunctionArgument) - } - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/fp/data/CurrencyConvError.kt b/app/src/main/java/com/ivy/wallet/domain/fp/data/CurrencyConvError.kt deleted file mode 100644 index fde5cdb0ae..0000000000 --- a/app/src/main/java/com/ivy/wallet/domain/fp/data/CurrencyConvError.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.ivy.wallet.domain.fp.data - -data class CurrencyConvError(val account: FPAccount) \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/fp/data/FPAccount.kt b/app/src/main/java/com/ivy/wallet/domain/fp/data/FPAccount.kt deleted file mode 100644 index 0e5d8e3758..0000000000 --- a/app/src/main/java/com/ivy/wallet/domain/fp/data/FPAccount.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.ivy.wallet.domain.fp.data - -import arrow.core.Option -import arrow.core.toOption -import com.ivy.wallet.domain.data.entity.Account -import java.util.* - -data class FPAccount( - val id: UUID, - val name: String, - val currencyCode: String, - val color: Int, - val icon: Option, - val orderNum: Double, - val includeInBalance: Boolean, -) - -fun Account.toFPAccount( - baseCurrencyCode: String -): FPAccount = - FPAccount( - id = id, - name = name, - currencyCode = currency ?: baseCurrencyCode, - color = color, - icon = icon.toOption(), - orderNum = orderNum, - includeInBalance = includeInBalance - ) \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/fp/data/FPTransaction.kt b/app/src/main/java/com/ivy/wallet/domain/fp/data/FPTransaction.kt deleted file mode 100644 index ce220b9627..0000000000 --- a/app/src/main/java/com/ivy/wallet/domain/fp/data/FPTransaction.kt +++ /dev/null @@ -1,46 +0,0 @@ -package com.ivy.wallet.domain.fp.data - -import arrow.core.Option -import arrow.core.toOption -import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.data.entity.Transaction -import java.math.BigDecimal -import java.time.LocalDateTime -import java.util.* - -data class FPTransaction( - val id: UUID, - val type: TransactionType, - val accountId: UUID, - val categoryId: Option, - val amount: BigDecimal, - val toAccountId: Option, - val toAmount: BigDecimal, - val dateTime: Option, - - val loanId: Option, - val loanRecordId:Option, - - val title: Option, - val description: Option, - val dueDate: Option, - val recurringRuleId: Option -) - -fun Transaction.toFPTransaction(): FPTransaction = - FPTransaction( - id = id, - accountId = accountId, - type = type, - amount = amount.toBigDecimal(), - toAccountId = toAccountId.toOption(), - toAmount = toAmount?.toBigDecimal() ?: amount.toBigDecimal(), - title = title.toOption(), - description = description.toOption(), - dateTime = dateTime.toOption(), - categoryId = categoryId.toOption(), - dueDate = dueDate.toOption(), - recurringRuleId = recurringRuleId.toOption(), - loanId = loanId.toOption(), - loanRecordId = loanRecordId.toOption() - ) diff --git a/app/src/main/java/com/ivy/wallet/domain/fp/wallet/WalletCore.kt b/app/src/main/java/com/ivy/wallet/domain/fp/wallet/WalletCore.kt deleted file mode 100644 index 7f4b431dbe..0000000000 --- a/app/src/main/java/com/ivy/wallet/domain/fp/wallet/WalletCore.kt +++ /dev/null @@ -1,148 +0,0 @@ -package com.ivy.wallet.domain.fp.wallet - -import arrow.core.NonEmptyList -import arrow.core.Some -import com.ivy.wallet.domain.fp.account.AccountValueFunction -import com.ivy.wallet.domain.fp.account.calculateAccountValues -import com.ivy.wallet.domain.fp.core.Uncertain -import com.ivy.wallet.domain.fp.core.mapIndexedNel -import com.ivy.wallet.domain.fp.core.nonEmptyListOfZeros -import com.ivy.wallet.domain.fp.data.* -import com.ivy.wallet.domain.fp.exchangeToBaseCurrency -import com.ivy.wallet.io.persistence.dao.ExchangeRateDao -import com.ivy.wallet.utils.scopedIOThread -import kotlinx.coroutines.async -import kotlinx.coroutines.awaitAll -import java.math.BigDecimal -import java.util.* - -typealias UncertainWalletValues = Uncertain, NonEmptyList> -typealias AccountValuesPair = Pair> - -suspend fun calculateWalletValues( - walletDAOs: WalletDAOs, - baseCurrencyCode: String, - filterExcluded: Boolean = true, - range: ClosedTimeRange = ClosedTimeRange.allTimeIvy(), - valueFunctions: NonEmptyList -): UncertainWalletValues { - val uncertainWalletValues = walletDAOs.accountDao.findAll() - .filter { !filterExcluded || it.includeInBalance } - .map { account -> - Pair( - first = account.toFPAccount(baseCurrencyCode), - second = calculateAccountValues( - transactionDao = walletDAOs.transactionDao, - accountId = account.id, - range = range, - valueFunctions = valueFunctions - ) - ) - } - .convertValuesInBaseCurrency( - exchangeRateDao = walletDAOs.exchangeRateDao, - baseCurrencyCode = baseCurrencyCode - ) - - return sumUncertainWalletValues( - valueN = valueFunctions.size, - uncertainWalletValues = uncertainWalletValues - ) -} - -suspend fun calculateWalletValuesWithAccountFilters( - walletDAOs: WalletDAOs, - baseCurrencyCode: String, - filterExcluded: Boolean = true, - accountIdFilterList: List, - range: ClosedTimeRange = ClosedTimeRange.allTimeIvy(), - valueFunctions: NonEmptyList -): UncertainWalletValues { - - val accounts = scopedIOThread { scope -> - if (accountIdFilterList.isNotEmpty()) - accountIdFilterList.map { accId -> - scope.async { - walletDAOs.accountDao.findById(accId) - } - }.awaitAll().filterNotNull() - else { - walletDAOs.accountDao.findAll() - } - } - - val uncertainWalletValues = accounts - .filter { !filterExcluded || it.includeInBalance } - .map { account -> - Pair( - first = account.toFPAccount(baseCurrencyCode), - second = calculateAccountValues( - transactionDao = walletDAOs.transactionDao, - accountId = account.id, - range = range, - valueFunctions = valueFunctions - ) - ) - } - .convertValuesInBaseCurrency( - exchangeRateDao = walletDAOs.exchangeRateDao, - baseCurrencyCode = baseCurrencyCode - ) - - return sumUncertainWalletValues( - valueN = valueFunctions.size, - uncertainWalletValues = uncertainWalletValues - ) -} - -private suspend fun Iterable.convertValuesInBaseCurrency( - exchangeRateDao: ExchangeRateDao, - baseCurrencyCode: String, -): List { - return this.map { (account, values) -> - val valuesInBaseCurrency = values.map { - exchangeToBaseCurrency( - exchangeRateDao = exchangeRateDao, - baseCurrencyCode = baseCurrencyCode, - fromCurrencyCode = account.currencyCode, - fromAmount = it - ) - } - val hasError = valuesInBaseCurrency.any { !it.isDefined() } - - Uncertain( - error = if (hasError) - listOf(CurrencyConvError(account = account)) else emptyList(), - value = if (!hasError) { - //if there is no error all values must be Some() - valuesInBaseCurrency.map { (it as Some).value } - } else nonEmptyListOfZeros(values.size) - ) - } -} - -private tailrec fun sumUncertainWalletValues( - valueN: Int, - uncertainWalletValues: List, - sum: UncertainWalletValues = Uncertain( - error = emptyList(), - value = nonEmptyListOfZeros(n = valueN) - ) -): UncertainWalletValues { - return if (uncertainWalletValues.isEmpty()) sum else { - val uncertainValues = uncertainWalletValues.first() - - sumUncertainWalletValues( - valueN = valueN, - uncertainWalletValues = uncertainWalletValues.drop(1), - sum = Uncertain( - error = sum.error.plus(uncertainValues.error), - value = if (uncertainValues.isCertain()) { - sum.value.mapIndexedNel { index, value -> - value.plus(uncertainValues.value[index]) - } - } else sum.value //no need to sum it, if it's uncertain (it'll be all ZEROs) - ) - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/fp/wallet/WalletFunctions.kt b/app/src/main/java/com/ivy/wallet/domain/fp/wallet/WalletFunctions.kt deleted file mode 100644 index 2e1b844447..0000000000 --- a/app/src/main/java/com/ivy/wallet/domain/fp/wallet/WalletFunctions.kt +++ /dev/null @@ -1,194 +0,0 @@ -package com.ivy.wallet.domain.fp.wallet - -import arrow.core.nonEmptyListOf -import com.ivy.wallet.domain.data.entity.Settings -import com.ivy.wallet.domain.fp.account.AccountValueFunctions -import com.ivy.wallet.domain.fp.core.Uncertain -import com.ivy.wallet.domain.fp.data.ClosedTimeRange -import com.ivy.wallet.domain.fp.data.CurrencyConvError -import com.ivy.wallet.domain.fp.data.IncomeExpensePair -import com.ivy.wallet.domain.fp.data.WalletDAOs -import com.ivy.wallet.io.persistence.dao.SettingsDao -import java.math.BigDecimal -import java.util.* - -fun walletBufferDiff( - settings: Settings, - balance: BigDecimal -): BigDecimal { - return balance - settings.bufferAmount.toBigDecimal() -} - -suspend fun baseCurrencyCode( - settingsDao: SettingsDao -): String { - return settingsDao.findFirst().currency -} - -suspend fun calculateWalletBalance( - walletDAOs: WalletDAOs, - baseCurrencyCode: String, - filterExcluded: Boolean = true, - range: ClosedTimeRange = ClosedTimeRange.allTimeIvy(), -): Uncertain, BigDecimal> { - val uncertainValues = calculateWalletValues( - walletDAOs = walletDAOs, - baseCurrencyCode = baseCurrencyCode, - filterExcluded = filterExcluded, - range = range, - valueFunctions = nonEmptyListOf( - AccountValueFunctions::balance - ) - ) - - return Uncertain( - error = uncertainValues.error, - value = uncertainValues.value.head - ) -} - -suspend fun calculateWalletIncome( - walletDAOs: WalletDAOs, - baseCurrencyCode: String, - filterExcluded: Boolean = true, - range: ClosedTimeRange = ClosedTimeRange.allTimeIvy(), -): Uncertain, BigDecimal> { - val uncertainValues = calculateWalletValues( - walletDAOs = walletDAOs, - baseCurrencyCode = baseCurrencyCode, - filterExcluded = filterExcluded, - range = range, - valueFunctions = nonEmptyListOf( - AccountValueFunctions::income - ) - ) - - return Uncertain( - error = uncertainValues.error, - value = uncertainValues.value.head - ) -} - -suspend fun calculateWalletIncomeWithAccountFilters( - walletDAOs: WalletDAOs, - baseCurrencyCode: String, - filterExcluded: Boolean = true, - accountIdFilterList: List, - range: ClosedTimeRange = ClosedTimeRange.allTimeIvy(), -): Uncertain, BigDecimal> { - val uncertainValues = calculateWalletValuesWithAccountFilters( - walletDAOs = walletDAOs, - baseCurrencyCode = baseCurrencyCode, - filterExcluded = filterExcluded, - accountIdFilterList = accountIdFilterList, - range = range, - valueFunctions = nonEmptyListOf( - AccountValueFunctions::income - ) - ) - - return Uncertain( - error = uncertainValues.error, - value = uncertainValues.value.head - ) -} - -suspend fun calculateWalletExpense( - walletDAOs: WalletDAOs, - baseCurrencyCode: String, - filterExcluded: Boolean = true, - range: ClosedTimeRange = ClosedTimeRange.allTimeIvy(), -): Uncertain, BigDecimal> { - val uncertainValues = calculateWalletValues( - walletDAOs = walletDAOs, - baseCurrencyCode = baseCurrencyCode, - filterExcluded = filterExcluded, - range = range, - valueFunctions = nonEmptyListOf( - AccountValueFunctions::expense - ) - ) - - return Uncertain( - error = uncertainValues.error, - value = uncertainValues.value.head - ) -} - -suspend fun calculateWalletExpenseWithAccountFilters( - walletDAOs: WalletDAOs, - baseCurrencyCode: String, - filterExcluded: Boolean = true, - accountIdFilterList: List, - range: ClosedTimeRange = ClosedTimeRange.allTimeIvy(), -): Uncertain, BigDecimal> { - val uncertainValues = calculateWalletValuesWithAccountFilters( - walletDAOs = walletDAOs, - baseCurrencyCode = baseCurrencyCode, - filterExcluded = filterExcluded, - accountIdFilterList = accountIdFilterList, - range = range, - valueFunctions = nonEmptyListOf( - AccountValueFunctions::expense - ) - ) - - return Uncertain( - error = uncertainValues.error, - value = uncertainValues.value.head - ) -} - -suspend fun calculateWalletIncomeExpense( - walletDAOs: WalletDAOs, - baseCurrencyCode: String, - filterExcluded: Boolean = true, - range: ClosedTimeRange, -): Uncertain, IncomeExpensePair> { - val uncertainValues = calculateWalletValues( - walletDAOs = walletDAOs, - baseCurrencyCode = baseCurrencyCode, - filterExcluded = filterExcluded, - range = range, - valueFunctions = nonEmptyListOf( - AccountValueFunctions::income, - AccountValueFunctions::expense - ) - ) - - return Uncertain( - error = uncertainValues.error, - value = IncomeExpensePair( - income = uncertainValues.value[0], - expense = uncertainValues.value[1] - ) - ) -} - -suspend fun calculateWalletIncomeExpenseCount( - walletDAOs: WalletDAOs, - baseCurrencyCode: String, - filterExcluded: Boolean = true, - range: ClosedTimeRange, -): Uncertain, Pair> { - val uncertainValues = calculateWalletValues( - walletDAOs = walletDAOs, - baseCurrencyCode = baseCurrencyCode, - filterExcluded = filterExcluded, - range = range, - valueFunctions = nonEmptyListOf( - AccountValueFunctions::incomeCount, - AccountValueFunctions::expenseCount - ) - ) - - return Uncertain( - error = uncertainValues.error, - value = Pair( - uncertainValues.value[0], uncertainValues.value[1] - ) - ) -} - -//TODO: upcomingIncomeExpense -//TODO: overdueIncomeExpense diff --git a/app/src/main/java/com/ivy/wallet/domain/fp/wallet/WalletTransactions.kt b/app/src/main/java/com/ivy/wallet/domain/fp/wallet/WalletTransactions.kt deleted file mode 100644 index 6742e592b3..0000000000 --- a/app/src/main/java/com/ivy/wallet/domain/fp/wallet/WalletTransactions.kt +++ /dev/null @@ -1,83 +0,0 @@ -package com.ivy.wallet.domain.fp.wallet - -import com.ivy.wallet.domain.data.TransactionHistoryDateDivider -import com.ivy.wallet.domain.data.TransactionHistoryItem -import com.ivy.wallet.domain.data.entity.Transaction -import com.ivy.wallet.domain.fp.core.* -import com.ivy.wallet.domain.fp.data.ClosedTimeRange -import com.ivy.wallet.domain.fp.data.WalletDAOs -import com.ivy.wallet.io.persistence.dao.AccountDao -import com.ivy.wallet.io.persistence.dao.ExchangeRateDao -import com.ivy.wallet.io.persistence.dao.TransactionDao -import com.ivy.wallet.utils.convertUTCtoLocal -import com.ivy.wallet.utils.toEpochSeconds - -//TODO: overdue(range) -//TODO: upcoming(range) - -suspend fun historyWithDateDividers( - walletDAOs: WalletDAOs, - baseCurrencyCode: String, - range: ClosedTimeRange -): List { - return history( - transactionDao = walletDAOs.transactionDao, - range = range - ).withDateDividers( - exchangeRateDao = walletDAOs.exchangeRateDao, - accountDao = walletDAOs.accountDao, - baseCurrencyCode = baseCurrencyCode - ) -} - -suspend fun history( - transactionDao: TransactionDao, - range: ClosedTimeRange -): List { - return transactionDao.findAllBetween( - startDate = range.from, - endDate = range.to - ) -} - -suspend fun List.withDateDividers( - exchangeRateDao: ExchangeRateDao, - accountDao: AccountDao, - baseCurrencyCode: String, -): List { - val history = this - if (history.isEmpty()) return emptyList() - - return history - .groupBy { it.dateTime?.convertUTCtoLocal()?.toLocalDate() } - .filterKeys { it != null } - .toSortedMap { date1, date2 -> - if (date1 == null || date2 == null) return@toSortedMap 0 //this case shouldn't happen - (date2.atStartOfDay().toEpochSeconds() - date1.atStartOfDay().toEpochSeconds()).toInt() - } - .flatMap { (date, transactionsForDate) -> - val baseCurrencyExchangeData = ExchangeData( - exchangeRateDao = exchangeRateDao, - accountDao = accountDao, - baseCurrencyCode = baseCurrencyCode, - toCurrency = baseCurrencyCode - ) - val fpTransactions = transactionsForDate.toFPTransactions() - - listOf( - TransactionHistoryDateDivider( - date = date!!, - income = sum( - incomes(fpTransactions), - ::amountInCurrency, - baseCurrencyExchangeData - ).toDouble(), - expenses = sum( - expenses(fpTransactions), - ::amountInCurrency, - baseCurrencyExchangeData - ).toDouble() - ), - ).plus(transactionsForDate) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/WalletLogic.kt b/app/src/main/java/com/ivy/wallet/domain/logic/WalletLogic.kt deleted file mode 100644 index c3570fd371..0000000000 --- a/app/src/main/java/com/ivy/wallet/domain/logic/WalletLogic.kt +++ /dev/null @@ -1,135 +0,0 @@ -package com.ivy.wallet.domain.logic - -import com.ivy.wallet.domain.data.TransactionHistoryDateDivider -import com.ivy.wallet.domain.data.TransactionHistoryItem -import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.data.entity.Transaction -import com.ivy.wallet.domain.logic.currency.ExchangeRatesLogic -import com.ivy.wallet.domain.logic.currency.sumInBaseCurrency -import com.ivy.wallet.io.persistence.dao.AccountDao -import com.ivy.wallet.io.persistence.dao.SettingsDao -import com.ivy.wallet.io.persistence.dao.TransactionDao -import com.ivy.wallet.ui.onboarding.model.FromToTimeRange -import com.ivy.wallet.ui.onboarding.model.filterOverdue -import com.ivy.wallet.ui.onboarding.model.filterUpcoming -import com.ivy.wallet.utils.beginningOfIvyTime -import com.ivy.wallet.utils.convertUTCtoLocal -import com.ivy.wallet.utils.toEpochSeconds -import java.time.LocalDate - -@Deprecated("Migrate to FP Style") -class WalletLogic( - private val accountDao: AccountDao, - private val transactionDao: TransactionDao, - private val settingsDao: SettingsDao, - private val exchangeRatesLogic: ExchangeRatesLogic, -) { - fun history(range: FromToTimeRange): List { - return transactionDao.findAllBetween( - startDate = range.from(), - endDate = range.to() - ).withDateDividers( - exchangeRatesLogic = exchangeRatesLogic, - settingsDao = settingsDao, - accountDao = accountDao - ) - } - - fun calculateUpcomingIncome(range: FromToTimeRange): Double { - return calculateIncome(upcomingTransactions(range)) - } - - fun calculateUpcomingExpenses(range: FromToTimeRange): Double { - return calculateExpenses(upcomingTransactions(range)) - } - - fun calculateOverdueIncome(range: FromToTimeRange): Double { - return calculateIncome(overdueTransactions(range)) - } - - fun calculateOverdueExpenses(range: FromToTimeRange): Double { - return calculateExpenses(overdueTransactions(range)) - } - - fun calculateIncome(transactions: List): Double { - return calculate(transactions, TransactionType.INCOME) - } - - fun calculateExpenses(transactions: List): Double { - return calculate(transactions, TransactionType.EXPENSE) - } - - private fun calculate(transactions: List, trnType: TransactionType): Double { - return transactions - .filter { it.type == trnType } - .sumInBaseCurrency( - exchangeRatesLogic = exchangeRatesLogic, - settingsDao = settingsDao, - accountDao = accountDao - ) - } - - fun upcomingTransactions(range: FromToTimeRange): List { - return transactionDao.findAllDueToBetween( - startDate = range.upcomingFrom(), - endDate = range.to() - ).filterUpcoming() - } - - fun overdueTransactions(range: FromToTimeRange): List { - return transactionDao.findAllDueToBetween( - startDate = beginningOfIvyTime(), - endDate = range.overdueTo() - ).filterOverdue() - } -} - -@Deprecated("Migrate to FP Style") -fun List.withDateDividers( - exchangeRatesLogic: ExchangeRatesLogic, - settingsDao: SettingsDao, - accountDao: AccountDao -): List { - val trns = this - if (trns.isEmpty()) return trns - - val historyWithDividers = mutableListOf() - - val dateTransactionsMap = mutableMapOf>() - for (transaction in trns) { - if (transaction.dateTime != null) { - val date = transaction.dateTime.convertUTCtoLocal().toLocalDate() - dateTransactionsMap[date]?.add(transaction) ?: run { - dateTransactionsMap[date] = mutableListOf(transaction) - } - } - } - - dateTransactionsMap.toSortedMap { date1, date2 -> - (date2.atStartOfDay().toEpochSeconds() - date1.atStartOfDay().toEpochSeconds()).toInt() - }.forEach { (date, trns) -> - historyWithDividers.add( - TransactionHistoryDateDivider( - date = date, - income = trns - .filter { it.type == TransactionType.INCOME } - .sumInBaseCurrency( - exchangeRatesLogic = exchangeRatesLogic, - settingsDao = settingsDao, - accountDao = accountDao - ), - expenses = trns - .filter { it.type == TransactionType.EXPENSE } - .sumInBaseCurrency( - exchangeRatesLogic = exchangeRatesLogic, - settingsDao = settingsDao, - accountDao = accountDao - ) - ) - ) - - historyWithDividers.addAll(trns) - } - - return historyWithDividers -} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/bankintegrations/BankIntegrationsLogic.kt b/app/src/main/java/com/ivy/wallet/domain/logic/bankintegrations/BankIntegrationsLogic.kt deleted file mode 100644 index 8ecda8e692..0000000000 --- a/app/src/main/java/com/ivy/wallet/domain/logic/bankintegrations/BankIntegrationsLogic.kt +++ /dev/null @@ -1,63 +0,0 @@ -package com.ivy.wallet.domain.logic.bankintegrations - -import com.ivy.wallet.domain.data.bankintegrations.SEAccount -import com.ivy.wallet.domain.data.bankintegrations.SEConnection -import com.ivy.wallet.domain.data.bankintegrations.SETransaction -import com.ivy.wallet.io.network.IvySession -import com.ivy.wallet.io.network.RestClient -import com.ivy.wallet.io.persistence.SharedPrefs -import com.ivy.wallet.ui.RootActivity - -class BankIntegrationsLogic( - restClient: RestClient, - private val seTransactionMapper: SaltEdgeTransactionMapper, - private val ivySession: IvySession, - private val sharedPrefs: SharedPrefs -) { - private val bankIntegrationsService = restClient.bankIntegrationsService - - suspend fun connect( - rootActivity: RootActivity - ) { - val response = bankIntegrationsService.connectSession() - rootActivity.openUrlInBrowser(response.connectUrl) - } - - suspend fun fetchConnections(): List { - if (!hasBankConnection()) return emptyList() //do nothing if the user isn't logged in - - return bankIntegrationsService.getConnections().connections - } - - suspend fun sync() { - if (!hasBankConnection()) return //do nothing if the user isn't logged in - - val seAccounts = fetchAccounts() - val seTransactions = fetchTransactions() - - seTransactionMapper.save( - seAccounts = seAccounts, - seTransactions = seTransactions - ) - } - - private suspend fun hasBankConnection(): Boolean { - //TODO: Check for SEConnections in table - return ivySession.isLoggedIn() && sharedPrefs.getBoolean( - SharedPrefs.ENABLE_BANK_SYNC, - false - ) - } - - private suspend fun fetchAccounts(): List { - return bankIntegrationsService.getAccounts().accounts - } - - private suspend fun fetchTransactions(): List { - return bankIntegrationsService.getTransactions().transactions - } - - suspend fun removeCustomer() { - bankIntegrationsService.removeCustomer() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/bankintegrations/SaltEdgeAccountMapper.kt b/app/src/main/java/com/ivy/wallet/domain/logic/bankintegrations/SaltEdgeAccountMapper.kt deleted file mode 100644 index 9ee3c230b5..0000000000 --- a/app/src/main/java/com/ivy/wallet/domain/logic/bankintegrations/SaltEdgeAccountMapper.kt +++ /dev/null @@ -1,66 +0,0 @@ -package com.ivy.wallet.domain.logic.bankintegrations - -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.toArgb -import com.ivy.wallet.domain.data.IvyCurrency -import com.ivy.wallet.domain.data.bankintegrations.SEAccount -import com.ivy.wallet.domain.data.bankintegrations.SETransaction -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.io.persistence.dao.AccountDao -import com.ivy.wallet.ui.theme.components.IVY_COLOR_PICKER_COLORS_FREE -import com.ivy.wallet.utils.toLowerCaseLocal -import java.util.* - -class SaltEdgeAccountMapper( - private val accountDao: AccountDao -) { - - fun mapAccount( - seAccounts: List, - seTransaction: SETransaction - ): UUID? { - val existingAccount = accountDao.findBySeAccountId( - seAccountId = seTransaction.account_id - ) - - val account = if (existingAccount == null) { - //create account - val seAccount = seAccounts.find { it.id == seTransaction.account_id } ?: return null - - val account = Account( - seAccountId = seAccount.id, - - name = seAccount.name, - color = mapColor(seAccount).toArgb(), - icon = mapIcon(seAccount), - - currency = IvyCurrency.fromCode(seAccount.currency_code)?.code, - orderNum = accountDao.findMaxOrderNum(), - - isSynced = false, - isDeleted = false - ) - - accountDao.save(account) - - account - } else existingAccount - - return account.id - } - - private fun mapColor(seAccount: SEAccount): Color { - //TODO: Create better mapping - return IVY_COLOR_PICKER_COLORS_FREE.shuffled().first() - } - - private fun mapIcon(seAccount: SEAccount): String? { - //TODO: Create better mapping - return when { - seAccount.name.toLowerCaseLocal().contains("revolut") -> { - "revolut" - } - else -> null - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/bankintegrations/SaltEdgeCategoryMapper.kt b/app/src/main/java/com/ivy/wallet/domain/logic/bankintegrations/SaltEdgeCategoryMapper.kt deleted file mode 100644 index a23e1d7a50..0000000000 --- a/app/src/main/java/com/ivy/wallet/domain/logic/bankintegrations/SaltEdgeCategoryMapper.kt +++ /dev/null @@ -1,370 +0,0 @@ -package com.ivy.wallet.domain.logic.bankintegrations - -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.toArgb -import com.ivy.wallet.domain.data.bankintegrations.SETransaction -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.io.persistence.dao.CategoryDao -import com.ivy.wallet.ui.theme.* -import com.ivy.wallet.ui.theme.components.IVY_COLOR_PICKER_COLORS_FREE -import com.ivy.wallet.utils.capitalizeLocal -import java.util.* - -/* -{ - "data": { - "business": { - "equipment_and_materials": [ - "electronics", - "software", - "supplies_and_furniture", - "raw_materials", - "consumer_goods" - ], - "financials": [ - "dividends", - "donations", - "interest", - "fees", - "fines", - "loans" - ], - "human_resources": [ - "wages", - "bonus", - "employee_benefits", - "education_and_trainings", - "staff_outsourcing", - "travel", - "entertainment", - "meals" - ], - "income": [ - "investments", - "sales", - "returns", - "prepayments" - ], - "insurance": [ - "business_insurance", - "liability_insurance", - "health_insurance", - "equipment_insurance", - "vehicle_insurance", - "professional_insurance" - ], - "real_estate": [ - "office_rent", - "mortgage", - "construction_and_repair" - ], - "services": [ - "contractors", - "accounting_and_auditing", - "legal", - "consulting", - "storage", - "marketing_and_media", - "online_subscriptions", - "it_services", - "cleaning" - ], - "taxes": [ - "vat", - "federal_taxes", - "property_taxes", - "income_taxes", - "duty_taxes", - "tax_return", - "payroll_taxes" - ], - "transport": [ - "shipping", - "leasing", - "gas_and_fuel", - "taxi", - "service_and_parts" - ], - "uncategorized": [], - "utilities": [ - "internet", - "phone", - "water", - "gas", - "electricity" - ] - }, - "personal": { - "auto_and_transport": [ - "car_rental", - "gas_and_fuel", - "parking", - "public_transportation", - "service_and_parts", - "taxi" - ], - "bills_and_utilities": [ - "internet", - "phone", - "television", - "utilities" - ], - "business_services": [ - "advertising", - "office_supplies", - "shipping" - ], - "education": [ - "books_and_supplies", - "student_loan", - "tuition" - ], - "entertainment": [ - "amusement", - "arts", - "games", - "movies_and_music", - "newspapers_and_magazines" - ], - "fees_and_charges": [ - "provider_fee", - "loans", - "service_fee", - "taxes" - ], - "food_and_dining": [ - "alcohol_and_bars", - "cafes_and_restaurants", - "groceries" - ], - "gifts_and_donations": [ - "charity", - "gifts" - ], - "health_and_fitness": [ - "doctor", - "personal_care", - "pharmacy", - "sports", - "wellness" - ], - "home": [ - "home_improvement", - "home_services", - "home_supplies", - "mortgage", - "rent" - ], - "income": [ - "bonus", - "investment_income", - "paycheck" - ], - "insurance": [ - "car_insurance", - "health_insurance", - "life_insurance", - "property_insurance" - ], - "kids": [ - "allowance", - "babysitter_and_daycare", - "baby_supplies", - "child_support", - "kids_activities", - "toys" - ], - "pets": [ - "pet_food_and_supplies", - "pet_grooming", - "veterinary" - ], - "shopping": [ - "clothing", - "electronics_and_software", - "sporting_goods" - ], - "transfer": [], - "travel": [ - "hotel", - "transportation", - "vacation" - ], - "uncategorized": [] - } - } -} - */ - -class SaltEdgeCategoryMapper( - private val categoryDao: CategoryDao -) { - - fun mapSeAutoCategoryId( - seTransaction: SETransaction - ): UUID? { - val existingCategory = categoryDao.findBySeCategoryName( - seCategoryName = seTransaction.category - ) - - val seAutoCategory = if (existingCategory == null) { - //Try to map and create category - val seAutoCategory = mapSeCategoryName(seTransaction.category)?.copy( - orderNum = categoryDao.findMaxOrderNum(), - - isSynced = false, - isDeleted = false - ) - - if (seAutoCategory != null) { - categoryDao.save( - seAutoCategory - ) - } - - seAutoCategory - } else existingCategory - - return seAutoCategory?.id - } - - private fun mapSeCategoryName(seCategoryName: String): Category? { - if (seCategoryName == "uncategorized") return null - - val colorIcon = mapCategoryColorIcon(seCategoryName = seCategoryName) - - return Category( - name = seCategoryName - .replace("_", " ") - .capitalizeLocal(), - seCategoryName = seCategoryName, - color = (colorIcon?.first ?: IVY_COLOR_PICKER_COLORS_FREE.shuffled().first()).toArgb(), - icon = colorIcon?.second - ) - } - - private fun mapCategoryColorIcon(seCategoryName: String): Pair? { - return when (seCategoryName) { - "electronics" -> Pair(Blue, "atom") - "software" -> Pair(Blue3, "programming") - "supplies_and_furniture" -> Pair(Orange2Dark, "label") - "raw_materials" -> Pair(Green2Dark, "hike") - "consumer_goods" -> Pair(Blue3Light, "shopping") - "dividends" -> Pair(Orange3, "calculator") - "donations" -> Pair(Yellow, "relationship") - "interest" -> Pair(Purple1Light, "selfdevelopment") - "fees" -> Pair(Orange, "document") - "fines" -> Pair(Orange2Dark, "loan") - "loans" -> Pair(Blue2Dark, "loan") - "wages" -> Pair(IvyDark, "work") - "bonus" -> Pair(YellowLight, "diamond") - "employee_benefits" -> Pair(Blue2, "people") - "education_and_trainings" -> Pair(Blue, "education") - "staff_outsourcing" -> Pair(Green3, "people") - "travel" -> Pair(BlueLight, "travel") - "entertainment" -> Pair(Orange, "game") - "meals" -> Pair(Green2, "restaurant") - "investments" -> Pair(Green2Light, "document") - "sales" -> Pair(Yellow, "label") - "returns" -> Pair(OrangeLight, "category") - "prepayments" -> Pair(GreenLight, "bills") - "business_insurance" -> Pair(Blue2, "insurance") - "liability_insurance" -> Pair(Blue2, "insurance") - "health_insurance," -> Pair(Green2Light, "insurance") - "equipment_insurance" -> Pair(Blue2, "insurance") - "vehicle_insurance" -> Pair(Blue2, "insurance") - "professional_insurance" -> Pair(Blue2, "insurance") - "office_rent" -> Pair(BlueDark, "work") - "mortgage" -> Pair(Orange2Dark, "house") - "construction_and_repair" -> Pair(OrangeDark, "tools") - "contractors" -> Pair(Blue3Dark, "document") - "accounting_and_auditing" -> Pair(Blue3, "document") - "legal" -> Pair(Blue3Dark, "document") - "consulting" -> Pair(Blue2Light, "people") - "storage" -> Pair(Orange2Dark, "furniture") - "marketing_and_media" -> Pair(Orange, "rocket") - "online_subscriptions" -> Pair(Orange3, "connect") - "it_services" -> Pair(Blue, "programming") - "cleaning" -> Pair(Blue2Light, "house") - "vat" -> Pair(OrangeLight, "loan") - "federal_taxes" -> Pair(RedDark, "loan") - "property_taxes" -> Pair(RedDark, "loan") - "income_taxes" -> Pair(RedDark, "loan") - "duty_taxes" -> Pair(RedDark, "loan") - "tax_return" -> Pair(GreenDark, "loan") - "payroll_taxes" -> Pair(RedDark, "loan") - "shipping" -> Pair(Yellow, "shopping2") - "leasing" -> Pair(Green4, "house") - "gas_and_fuel" -> Pair(Green3Dark, "vehicle") - "taxi" -> Pair(YellowLight, "vehicle") - "service_and_parts" -> Pair(Green2, "tools") - "internet" -> Pair(BlueLight, "connect") - "phone" -> Pair(BlueLight, "bills") - "water" -> Pair(BlueLight, "bills") - "gas" -> Pair(Orange3Light, "bills") - "electricity" -> Pair(Blue2Light, "bills") - "car_rental" -> Pair(BlueDark, "vehicle") - "parking" -> Pair(GreenDark, "vehicle") - "public_transportation" -> Pair(Purple1, "transport") - "television" -> Pair(Green3Dark, "bills") - "utilities" -> Pair(Blue, "bills") - "advertising" -> Pair(Orange2, "rocket") - "office_supplies" -> Pair(BlueLight, "work") - "books_and_supplies" -> Pair(BlueLight, "document") - "student_loan" -> Pair(Blue2Dark, "loan") - "tuition" -> Pair(Blue2Dark, "education") - "education" -> Pair(Blue2Dark, "education") - "amusement" -> Pair(Orange, "rocket") - "arts" -> Pair(Red, "palette") - "games" -> Pair(Orange, "game") - "movies_and_music" -> Pair(Red, "music") - "newspapers_and_magazines" -> Pair(Orange, "document") - "provider_fee" -> Pair(RedLight, "loan") - "service_fee" -> Pair(RedDark, "loan") - "taxes" -> Pair(RedDark, "bills") - "alcohol_and_bars" -> Pair(Purple2Dark, "fooddrink") - "cafes_and_restaurants" -> Pair(Green, "coffee") - "groceries" -> Pair(Green4, "groceries") - "charity" -> Pair(GreenLight, "selfdevelopment") - "gifts" -> Pair(Red, "gift") - "doctor" -> Pair(Blue2Light, "health") - "personal_care" -> Pair(Blue2Light, "hairdresser") - "pharmacy" -> Pair(Blue2Light, "farmacy") - "sports" -> Pair(IvyLight, "sports") - "wellness" -> Pair(GreenLight, "fitness") - "home_improvement" -> Pair(Blue2Light, "house") - "home_services" -> Pair(Blue2Light, "house") - "home_supplies" -> Pair(Blue2Light, "house") - "rent" -> Pair(BlueDark, "house") - "investment_income" -> Pair(Green2Light, "leaf") - "paycheck" -> Pair(Green, "work") - "car_insurance" -> Pair(Blue2, "insurance") - "health_insurance" -> Pair(Blue2, "insurance") - "life_insurance" -> Pair(Green2Light, "insurance") - "property_insurance" -> Pair(Blue2, "insurance") - "allowance" -> Pair(GreenLight, "loan") - "babysitter_and_daycare" -> Pair(BlueLight, "family") - "baby_supplies" -> Pair(BlueLight, "shopping2") - "child_support" -> Pair(BlueLight, "family") - "kids_activities" -> Pair(Blue, "sports") - "toys" -> Pair(Yellow, "rocket") - "pet_food_and_supplies" -> Pair(Orange3Light, "pet") - "pet_grooming" -> Pair(Orange3Light, "pet") - "veterinary" -> Pair(Purple1, "pet") - "clothing" -> Pair(Red3, "clothes2") - "electronics_and_software" -> Pair(Blue, "atom") - "sporting_goods" -> Pair(Blue, "sports") - "hotel" -> Pair(Orange2Dark, "location") - "transportation" -> Pair(Yellow, "transport") - "vacation" -> Pair(Blue2Light, "travel") - - //Extra not documented categories - "bills_and_utilities" -> Pair(Red, "bills") - "business_services" -> Pair(BlueDark, "groceries") - "shopping" -> Pair(BlueLight, "shopping") - "income" -> Pair(Green, "work") - "transfer" -> Pair(Ivy, "bank") - else -> null - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/logic/bankintegrations/SaltEdgeTransactionMapper.kt b/app/src/main/java/com/ivy/wallet/domain/logic/bankintegrations/SaltEdgeTransactionMapper.kt deleted file mode 100644 index b874ee0a17..0000000000 --- a/app/src/main/java/com/ivy/wallet/domain/logic/bankintegrations/SaltEdgeTransactionMapper.kt +++ /dev/null @@ -1,164 +0,0 @@ -package com.ivy.wallet.domain.logic.bankintegrations - -import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.data.bankintegrations.SEAccount -import com.ivy.wallet.domain.data.bankintegrations.SETransaction -import com.ivy.wallet.domain.data.entity.Transaction -import com.ivy.wallet.domain.logic.WalletAccountLogic -import com.ivy.wallet.io.persistence.dao.AccountDao -import com.ivy.wallet.io.persistence.dao.TransactionDao -import com.ivy.wallet.utils.ioThread -import timber.log.Timber -import java.time.LocalDate -import java.time.LocalDateTime -import java.time.LocalTime -import java.time.format.DateTimeFormatter -import java.util.* -import kotlin.math.absoluteValue - -class SaltEdgeTransactionMapper( - private val transactionDao: TransactionDao, - private val seAccountMapper: SaltEdgeAccountMapper, - private val seCategoryMapper: SaltEdgeCategoryMapper, - private val accountDao: AccountDao, - private val walletAccountLogic: WalletAccountLogic -) { - - suspend fun save(seAccounts: List, seTransactions: List) { - ioThread { - syncTransactions( - seTransactions = seTransactions, - seAccounts = seAccounts - ) - - syncBalances( - seAccounts = seAccounts - ) - } - } - - private fun syncTransactions( - seTransactions: List, - seAccounts: List - ) { - seTransactions.mapNotNull { - mapSeTransaction( - seAccounts = seAccounts, - seTransaction = it - ) - }.forEach { - transactionDao.save(it) - } - } - - private fun syncBalances( - seAccounts: List - ) { - val seAccountToAccount = seAccounts.map { seAccount -> - seAccount to accountDao.findBySeAccountId(seAccount.id) - }.toMap() - - seAccountToAccount.forEach { (seAccount, account) -> - if (account != null) { - walletAccountLogic.adjustBalance( - account = account, - newBalance = seAccount.balance, - adjustTransactionTitle = "Auto-adjust bank account balance", - isFiat = true, - trnIsSyncedFlag = false - ) - } - } - } - - private fun mapSeTransaction( - seAccounts: List, - seTransaction: SETransaction - ): Transaction? { - val existingIvyTransaction = transactionDao.findBySeTransactionId( - seTransactionId = seTransaction.id - ) - - //Persist ---------------------------------------------------------------------- - val ivyTrnId = existingIvyTransaction?.id ?: UUID.randomUUID() - val categoryId = existingIvyTransaction?.categoryId - val title = existingIvyTransaction?.title ?: seTransaction.description - val description = existingIvyTransaction?.description - //Persist ---------------------------------------------------------------------- - - //Map -------------------------------------------------------------------------- - val accountId = seAccountMapper.mapAccount( - seAccounts = seAccounts, - seTransaction = seTransaction - ) ?: return null - - val seAutoCategoryId = seCategoryMapper.mapSeAutoCategoryId( - seTransaction = seTransaction - ) - - val type = if (seTransaction.amount > 0) - TransactionType.INCOME else TransactionType.EXPENSE - val amount = seTransaction.amount.absoluteValue - - val dateTime = mapDateTime(seTransaction = seTransaction) - //Map -------------------------------------------------------------------------- - - val finalTransaction = Transaction( - id = ivyTrnId, - type = type, - amount = amount, - accountId = accountId, - dateTime = dateTime, - - categoryId = categoryId, - seTransactionId = seTransaction.id, - seAutoCategoryId = seAutoCategoryId, - description = description, - title = title, - ) - val shouldUploadToSerer = !finalTransaction.isIdenticalWith(existingIvyTransaction) || - existingIvyTransaction?.isSynced == false - Timber.i("Should upload to server: $shouldUploadToSerer") - - return finalTransaction.copy( - isSynced = !shouldUploadToSerer, - isDeleted = false - ) - } - - private fun mapDateTime(seTransaction: SETransaction): LocalDateTime? { - val time = (seTransaction.extra?.get("time") as? String?) - ?.parseSaltEdgeTime() ?: LocalTime.of(12, 0, 0) - - return seTransaction.made_on - .parseSaltEdgeDate() - ?.atTime(time) ?: return null - } - - private fun String.parseSaltEdgeTime(): LocalTime? { - return try { - LocalTime.parse(this) - } catch (e: Exception) { - e.printStackTrace() - null - } - } - - private fun String.parseSaltEdgeDate(): LocalDate? { - return try { - return LocalDate.parse(this, DateTimeFormatter.ofPattern("yyyy-MM-dd")) - } catch (e: Exception) { - e.printStackTrace() - null - } - } - - private fun String.parseSaltEdgeDateTime(): LocalDateTime? { - return try { - return LocalDateTime.parse(this, DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssz")) - } catch (e: Exception) { - e.printStackTrace() - null - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/pure/account/AccountFunctions.kt b/app/src/main/java/com/ivy/wallet/domain/pure/account/AccountFunctions.kt new file mode 100644 index 0000000000..755b01446b --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/pure/account/AccountFunctions.kt @@ -0,0 +1,9 @@ +package com.ivy.wallet.domain.pure.account + +import com.ivy.wallet.domain.data.core.Account + +fun filterExcluded(accounts: List): List = + accounts.filter { it.includeInBalance } + +fun accountCurrency(account: Account, baseCurrency: String): String = + account.currency ?: baseCurrency \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/fp/charts/ChartPeriod.kt b/app/src/main/java/com/ivy/wallet/domain/pure/charts/ChartPeriod.kt similarity index 97% rename from app/src/main/java/com/ivy/wallet/domain/fp/charts/ChartPeriod.kt rename to app/src/main/java/com/ivy/wallet/domain/pure/charts/ChartPeriod.kt index 167d7393de..40c1ec1ea0 100644 --- a/app/src/main/java/com/ivy/wallet/domain/fp/charts/ChartPeriod.kt +++ b/app/src/main/java/com/ivy/wallet/domain/pure/charts/ChartPeriod.kt @@ -1,6 +1,6 @@ -package com.ivy.wallet.domain.fp.charts +package com.ivy.wallet.domain.pure.charts -import com.ivy.wallet.domain.fp.data.ClosedTimeRange +import com.ivy.wallet.domain.pure.data.ClosedTimeRange import com.ivy.wallet.utils.dateNowUTC import com.ivy.wallet.utils.endOfDayNowUTC import com.ivy.wallet.utils.endOfMonth diff --git a/app/src/main/java/com/ivy/wallet/domain/fp/charts/ChartsCore.kt b/app/src/main/java/com/ivy/wallet/domain/pure/charts/ChartsCore.kt similarity index 65% rename from app/src/main/java/com/ivy/wallet/domain/fp/charts/ChartsCore.kt rename to app/src/main/java/com/ivy/wallet/domain/pure/charts/ChartsCore.kt index 32cd162b2b..a8baaaf298 100644 --- a/app/src/main/java/com/ivy/wallet/domain/fp/charts/ChartsCore.kt +++ b/app/src/main/java/com/ivy/wallet/domain/pure/charts/ChartsCore.kt @@ -1,7 +1,7 @@ -package com.ivy.wallet.domain.fp.charts +package com.ivy.wallet.domain.pure.charts -import com.ivy.wallet.domain.fp.data.ClosedTimeRange -import com.ivy.wallet.domain.fp.data.IncomeExpensePair +import com.ivy.wallet.domain.pure.data.ClosedTimeRange +import com.ivy.wallet.domain.pure.data.IncomeExpensePair import java.math.BigDecimal data class ChartPoint( diff --git a/app/src/main/java/com/ivy/wallet/domain/fp/charts/WalletCharts.kt b/app/src/main/java/com/ivy/wallet/domain/pure/charts/WalletCharts.kt similarity index 71% rename from app/src/main/java/com/ivy/wallet/domain/fp/charts/WalletCharts.kt rename to app/src/main/java/com/ivy/wallet/domain/pure/charts/WalletCharts.kt index 6411414586..633f0fdcc5 100644 --- a/app/src/main/java/com/ivy/wallet/domain/fp/charts/WalletCharts.kt +++ b/app/src/main/java/com/ivy/wallet/domain/pure/charts/WalletCharts.kt @@ -1,11 +1,10 @@ -package com.ivy.wallet.domain.fp.charts - -import com.ivy.wallet.domain.fp.data.ClosedTimeRange -import com.ivy.wallet.domain.fp.data.IncomeExpensePair -import com.ivy.wallet.domain.fp.data.WalletDAOs -import com.ivy.wallet.domain.fp.wallet.calculateWalletBalance -import com.ivy.wallet.domain.fp.wallet.calculateWalletIncomeExpense -import com.ivy.wallet.domain.fp.wallet.calculateWalletIncomeExpenseCount +package com.ivy.wallet.domain.pure.charts + +import com.ivy.fp.Pure +import com.ivy.fp.SideEffect +import com.ivy.wallet.domain.pure.data.ClosedTimeRange +import com.ivy.wallet.domain.pure.data.IncomeExpensePair +import com.ivy.wallet.domain.pure.data.WalletDAOs import com.ivy.wallet.utils.beginningOfIvyTime import com.ivy.wallet.utils.toEpochSeconds import java.math.BigDecimal @@ -15,10 +14,12 @@ data class ToRange( val to: LocalDateTime ) +@Pure suspend fun balanceChart( - walletDAOs: WalletDAOs, - baseCurrencyCode: String, - period: ChartPeriod + period: ChartPeriod, + + @SideEffect + calcWalletBalance: suspend (ClosedTimeRange) -> BigDecimal ): List { val orderedPeriod = period.toRangesList().sortedBy { it.to.toEpochSeconds() @@ -26,21 +27,17 @@ suspend fun balanceChart( return generateBalanceChart( orderedPeriod = orderedPeriod.map { ToRange(it.to) }, - calculateWalletBalance = { range -> - calculateWalletBalance( - walletDAOs = walletDAOs, - baseCurrencyCode = baseCurrencyCode, - filterExcluded = true, - range = range - ).value - } + calcWalletBalance = calcWalletBalance ) } +@Pure tailrec suspend fun generateBalanceChart( orderedPeriod: List, - calculateWalletBalance: suspend (range: ClosedTimeRange) -> BigDecimal, - accumulator: List = emptyList() + accumulator: List = emptyList(), + + @SideEffect + calcWalletBalance: suspend (ClosedTimeRange) -> BigDecimal ): List { return if (orderedPeriod.isEmpty()) accumulator else { //recurse @@ -49,7 +46,7 @@ tailrec suspend fun generateBalanceChart( val chartPoint = ChartPoint( range = ClosedTimeRange.to(to = toDateTime), - value = calculateWalletBalance( + value = calcWalletBalance( ClosedTimeRange( from = previousChartPoint?.range?.to?.plusSeconds(1) ?: beginningOfIvyTime(), to = toDateTime @@ -59,7 +56,7 @@ tailrec suspend fun generateBalanceChart( generateBalanceChart( orderedPeriod = orderedPeriod.drop(1), - calculateWalletBalance = calculateWalletBalance, + calcWalletBalance = calcWalletBalance, accumulator = accumulator.plus(chartPoint) ) } @@ -78,12 +75,13 @@ suspend fun incomeExpenseChart( return generateIncomeExpenseChart( orderedPeriod = orderedPeriod, calculateWalletIncomeExpense = { range -> - calculateWalletIncomeExpense( - walletDAOs = walletDAOs, - baseCurrencyCode = baseCurrencyCode, - range = range, - filterExcluded = true - ).value + TODO() +// calculateWalletIncomeExpense( +// walletDAOs = walletDAOs, +// baseCurrencyCode = baseCurrencyCode, +// range = range, +// filterExcluded = true +// ).value } ) } @@ -123,12 +121,13 @@ suspend fun incomeExpenseCountChart( return generateIncomeExpenseCountChart( orderedPeriod = orderedPeriod, calculateWalletIncomeExpenseCount = { range -> - calculateWalletIncomeExpenseCount( - walletDAOs = walletDAOs, - baseCurrencyCode = baseCurrencyCode, - range = range, - filterExcluded = true - ).value + TODO() +// calculateWalletIncomeExpenseCount( +// walletDAOs = walletDAOs, +// baseCurrencyCode = baseCurrencyCode, +// range = range, +// filterExcluded = true +// ).value } ) } diff --git a/app/src/main/java/com/ivy/wallet/domain/fp/data/ClosedTimeRange.kt b/app/src/main/java/com/ivy/wallet/domain/pure/data/ClosedTimeRange.kt similarity index 94% rename from app/src/main/java/com/ivy/wallet/domain/fp/data/ClosedTimeRange.kt rename to app/src/main/java/com/ivy/wallet/domain/pure/data/ClosedTimeRange.kt index a89ce785d0..b9859b7340 100644 --- a/app/src/main/java/com/ivy/wallet/domain/fp/data/ClosedTimeRange.kt +++ b/app/src/main/java/com/ivy/wallet/domain/pure/data/ClosedTimeRange.kt @@ -1,4 +1,4 @@ -package com.ivy.wallet.domain.fp.data +package com.ivy.wallet.domain.pure.data import com.ivy.wallet.ui.onboarding.model.FromToTimeRange import com.ivy.wallet.utils.beginningOfIvyTime diff --git a/app/src/main/java/com/ivy/wallet/domain/fp/data/IncomeExpensePair.kt b/app/src/main/java/com/ivy/wallet/domain/pure/data/IncomeExpensePair.kt similarity index 85% rename from app/src/main/java/com/ivy/wallet/domain/fp/data/IncomeExpensePair.kt rename to app/src/main/java/com/ivy/wallet/domain/pure/data/IncomeExpensePair.kt index acfa28deb6..e42f1bce9f 100644 --- a/app/src/main/java/com/ivy/wallet/domain/fp/data/IncomeExpensePair.kt +++ b/app/src/main/java/com/ivy/wallet/domain/pure/data/IncomeExpensePair.kt @@ -1,4 +1,4 @@ -package com.ivy.wallet.domain.fp.data +package com.ivy.wallet.domain.pure.data import java.math.BigDecimal diff --git a/app/src/main/java/com/ivy/wallet/domain/pure/data/IncomeExpenseTransferPair.kt b/app/src/main/java/com/ivy/wallet/domain/pure/data/IncomeExpenseTransferPair.kt new file mode 100644 index 0000000000..2301ccfba6 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/pure/data/IncomeExpenseTransferPair.kt @@ -0,0 +1,19 @@ +package com.ivy.wallet.domain.pure.data + +import java.math.BigDecimal + +data class IncomeExpenseTransferPair( + val income: BigDecimal, + val expense: BigDecimal, + val transferIncome: BigDecimal, + val transferExpense: BigDecimal +) { + companion object { + fun zero(): IncomeExpenseTransferPair = IncomeExpenseTransferPair( + BigDecimal.ZERO, + BigDecimal.ZERO, + BigDecimal.ZERO, + BigDecimal.ZERO + ) + } +} diff --git a/app/src/main/java/com/ivy/wallet/domain/fp/data/WalletDAOs.kt b/app/src/main/java/com/ivy/wallet/domain/pure/data/WalletDAOs.kt similarity index 88% rename from app/src/main/java/com/ivy/wallet/domain/fp/data/WalletDAOs.kt rename to app/src/main/java/com/ivy/wallet/domain/pure/data/WalletDAOs.kt index 69c8c44e3a..450ed0ea77 100644 --- a/app/src/main/java/com/ivy/wallet/domain/fp/data/WalletDAOs.kt +++ b/app/src/main/java/com/ivy/wallet/domain/pure/data/WalletDAOs.kt @@ -1,4 +1,4 @@ -package com.ivy.wallet.domain.fp.data +package com.ivy.wallet.domain.pure.data import com.ivy.wallet.io.persistence.dao.AccountDao import com.ivy.wallet.io.persistence.dao.ExchangeRateDao diff --git a/app/src/main/java/com/ivy/wallet/domain/fp/ExchangeRates.kt b/app/src/main/java/com/ivy/wallet/domain/pure/exchange/Exchange.kt similarity index 51% rename from app/src/main/java/com/ivy/wallet/domain/fp/ExchangeRates.kt rename to app/src/main/java/com/ivy/wallet/domain/pure/exchange/Exchange.kt index 5899266a35..f2927168af 100644 --- a/app/src/main/java/com/ivy/wallet/domain/fp/ExchangeRates.kt +++ b/app/src/main/java/com/ivy/wallet/domain/pure/exchange/Exchange.kt @@ -1,96 +1,54 @@ -package com.ivy.wallet.domain.fp +package com.ivy.wallet.domain.pure.exchange import arrow.core.None import arrow.core.Option import arrow.core.Some import arrow.core.computations.option import arrow.core.toOption -import com.ivy.wallet.domain.data.entity.ExchangeRate -import com.ivy.wallet.domain.fp.core.Pure -import com.ivy.wallet.domain.fp.core.Total -import com.ivy.wallet.io.persistence.dao.ExchangeRateDao +import com.ivy.fp.Pure +import com.ivy.fp.SideEffect +import com.ivy.wallet.domain.data.core.ExchangeRate import com.ivy.wallet.utils.isNotNullOrBlank import java.math.BigDecimal -@Total -suspend fun exchangeToBaseCurrency( - exchangeRateDao: ExchangeRateDao, - baseCurrencyCode: String, - fromCurrencyCode: String, - fromAmount: BigDecimal, -): Option { - return exchangeToBaseCurrency( - exchangeRateDao = exchangeRateDao, - baseCurrencyCode = baseCurrencyCode, - fromCurrencyCode = fromCurrencyCode.toOption(), - fromAmount = fromAmount - ) -} +data class ExchangeData( + val baseCurrency: String, + val fromCurrency: Option, + val toCurrency: String = baseCurrency, +) -@Total -suspend fun exchangeToBaseCurrency( - exchangeRateDao: ExchangeRateDao, - baseCurrencyCode: String, - fromCurrencyCode: Option, - fromAmount: BigDecimal, -): Option { - return exchange( - exchangeRateDao = exchangeRateDao, - baseCurrencyCode = baseCurrencyCode, - fromCurrencyCode = fromCurrencyCode, - fromAmount = fromAmount, - toCurrencyCode = baseCurrencyCode, - ) -} -@Total +@Pure suspend fun exchange( - exchangeRateDao: ExchangeRateDao, - baseCurrencyCode: String, - fromCurrencyCode: Option, - fromAmount: BigDecimal, - toCurrencyCode: String, -): Option { - return exchange( - baseCurrencyCode = baseCurrencyCode, - fromCurrencyCode = fromCurrencyCode, - fromAmount = fromAmount, - toCurrencyCode = toCurrencyCode, - retrieveExchangeRate = exchangeRateDao::findByBaseCurrencyAndCurrency - ) -} + data: ExchangeData, + amount: BigDecimal, -@Total -suspend fun exchange( - baseCurrencyCode: String, - fromCurrencyCode: Option, - fromAmount: BigDecimal, - toCurrencyCode: String, - retrieveExchangeRate: suspend (baseCurrency: String, toCurrency: String) -> ExchangeRate?, + @SideEffect + getExchangeRate: suspend (baseCurrency: String, toCurrency: String) -> ExchangeRate?, ): Option = option { - if (fromAmount == BigDecimal.ZERO) { + if (amount == BigDecimal.ZERO) { return@option BigDecimal.ZERO } - val fromCurrency = fromCurrencyCode.bind().validateCurrency().bind() - val toCurrency = toCurrencyCode.validateCurrency().bind() + val fromCurrency = data.fromCurrency.bind().validateCurrency().bind() + val toCurrency = data.toCurrency.validateCurrency().bind() if (fromCurrency == toCurrency) { - return@option fromAmount + return@option amount } - when (val baseCurrency = baseCurrencyCode.validateCurrency().bind()) { + when (val baseCurrency = data.baseCurrency.validateCurrency().bind()) { fromCurrency -> { //exchange from base currency to other currency //we need the rate from baseCurrency to toCurrency val rateFromTo = validExchangeRate( baseCurrency = fromCurrency, //fromCurrency = baseCurrency toCurrency = toCurrency, - retrieveExchangeRate = retrieveExchangeRate + retrieveExchangeRate = getExchangeRate ).bind() //toAmount = fromAmount * rateFromTo - fromAmount * rateFromTo + amount * rateFromTo } toCurrency -> { //exchange from other currency to base currency @@ -99,7 +57,7 @@ suspend fun exchange( val rateToFrom = validExchangeRate( baseCurrency = toCurrency, //toCurrency = baseCurrency toCurrency = fromCurrency, - retrieveExchangeRate = retrieveExchangeRate + retrieveExchangeRate = getExchangeRate ).bind() /* @@ -111,7 +69,7 @@ suspend fun exchange( EXPECTED: 10 EUR ~= 19.67 BGN */ - fromAmount / rateToFrom + amount / rateToFrom } else -> { //exchange from other currency to other currency @@ -120,17 +78,17 @@ suspend fun exchange( val rateBaseFrom = validExchangeRate( baseCurrency = baseCurrency, toCurrency = fromCurrency, - retrieveExchangeRate = retrieveExchangeRate + retrieveExchangeRate = getExchangeRate ).bind() val rateBaseTo = validExchangeRate( baseCurrency = baseCurrency, toCurrency = toCurrency, - retrieveExchangeRate = retrieveExchangeRate + retrieveExchangeRate = getExchangeRate ).bind() //Convert: toBaseCurrency -> toToCurrency - val amountBaseCurrency = fromAmount / rateBaseFrom + val amountBaseCurrency = amount / rateBaseFrom amountBaseCurrency * rateBaseTo } } @@ -141,7 +99,7 @@ private fun String.validateCurrency(): Option { return if (this.isNotNullOrBlank()) return Some(this) else None } -@Total +@Pure suspend fun validExchangeRate( baseCurrency: String, toCurrency: String, diff --git a/app/src/main/java/com/ivy/wallet/domain/pure/exchange/ExchangeTrns.kt b/app/src/main/java/com/ivy/wallet/domain/pure/exchange/ExchangeTrns.kt new file mode 100644 index 0000000000..0e18a19eb5 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/pure/exchange/ExchangeTrns.kt @@ -0,0 +1,95 @@ +package com.ivy.wallet.domain.pure.exchange + +import arrow.core.Option +import arrow.core.toOption +import com.ivy.fp.Pure +import com.ivy.fp.SideEffect +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Transaction +import com.ivy.wallet.domain.pure.account.accountCurrency +import com.ivy.wallet.domain.pure.transaction.trnCurrency +import java.math.BigDecimal +import java.util.* + +typealias ExchangeEffect = suspend (ExchangeData, BigDecimal) -> Option + +data class ExchangeTrnArgument( + val baseCurrency: String, + @SideEffect + val getAccount: suspend (accountId: UUID) -> Account?, + @SideEffect + val exchange: ExchangeEffect +) + +@Pure +suspend fun exchangeInBaseCurrency( + transaction: Transaction, + arg: ExchangeTrnArgument +): BigDecimal { + val fromCurrency = arg.getAccount(transaction.accountId)?.let { + accountCurrency(it, arg.baseCurrency) + }.toOption() + + return exchangeInCurrency( + transaction = transaction, + baseCurrency = arg.baseCurrency, + trnCurrency = fromCurrency, + toCurrency = arg.baseCurrency, + exchange = arg.exchange + ) +} + +@Pure +suspend fun exchangeInBaseCurrency( + transaction: Transaction, + baseCurrency: String, + accounts: List, + + @SideEffect + exchange: ExchangeEffect +): BigDecimal = exchangeInCurrency( + transaction = transaction, + baseCurrency = baseCurrency, + accounts = accounts, + toCurrency = baseCurrency, + exchange = exchange +) + +@Pure +suspend fun exchangeInCurrency( + transaction: Transaction, + baseCurrency: String, + accounts: List, + toCurrency: String, + + @SideEffect + exchange: ExchangeEffect +): BigDecimal { + return exchange( + ExchangeData( + baseCurrency = baseCurrency, + fromCurrency = trnCurrency(transaction, accounts, baseCurrency), + toCurrency = toCurrency + ), + transaction.amount + ).orNull() ?: BigDecimal.ZERO +} + +suspend fun exchangeInCurrency( + transaction: Transaction, + baseCurrency: String, + trnCurrency: Option, + toCurrency: String, + + @SideEffect + exchange: ExchangeEffect +): BigDecimal { + return exchange( + ExchangeData( + baseCurrency = baseCurrency, + fromCurrency = trnCurrency, + toCurrency = toCurrency + ), + transaction.amount + ).orNull() ?: BigDecimal.ZERO +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/fp/account/AccountValueFunctions.kt b/app/src/main/java/com/ivy/wallet/domain/pure/transaction/AccValueFunctions.kt similarity index 58% rename from app/src/main/java/com/ivy/wallet/domain/fp/account/AccountValueFunctions.kt rename to app/src/main/java/com/ivy/wallet/domain/pure/transaction/AccValueFunctions.kt index c804134621..8b624c86f5 100644 --- a/app/src/main/java/com/ivy/wallet/domain/fp/account/AccountValueFunctions.kt +++ b/app/src/main/java/com/ivy/wallet/domain/pure/transaction/AccValueFunctions.kt @@ -1,8 +1,7 @@ -package com.ivy.wallet.domain.fp.account +package com.ivy.wallet.domain.pure.transaction import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.fp.core.ValueFunction -import com.ivy.wallet.domain.fp.data.FPTransaction +import com.ivy.wallet.domain.data.core.Transaction import java.math.BigDecimal import java.util.* @@ -10,16 +9,16 @@ typealias AccountValueFunction = ValueFunction object AccountValueFunctions { fun balance( - fpTransaction: FPTransaction, + transaction: Transaction, accountId: UUID - ): BigDecimal = with(fpTransaction) { + ): BigDecimal = with(transaction) { if (this.accountId == accountId) { //Account's transactions when (type) { TransactionType.INCOME -> amount TransactionType.EXPENSE -> amount.negate() TransactionType.TRANSFER -> { - if (toAccountId.orNull() != accountId) { + if (toAccountId != accountId) { //transfer to another account amount.negate() } else { @@ -30,39 +29,56 @@ object AccountValueFunctions { } } else { //potential transfer to account? - toAccountId.orNull()?.takeIf { it == accountId } ?: return BigDecimal.ZERO + toAccountId?.takeIf { it == accountId } ?: return BigDecimal.ZERO toAmount } } fun income( - fpTransaction: FPTransaction, + transaction: Transaction, accountId: UUID - ): BigDecimal = with(fpTransaction) { + ): BigDecimal = with(transaction) { if (this.accountId == accountId && type == TransactionType.INCOME) amount else BigDecimal.ZERO } + fun transferIncome( + transaction: Transaction, + accountId: UUID + ): BigDecimal = with(transaction) { + if (this.toAccountId == accountId && type == TransactionType.TRANSFER) + toAmount else BigDecimal.ZERO + } + fun expense( - fpTransaction: FPTransaction, + transaction: Transaction, accountId: UUID - ): BigDecimal = with(fpTransaction) { + ): BigDecimal = with(transaction) { if (this.accountId == accountId && type == TransactionType.EXPENSE) amount else BigDecimal.ZERO } + fun transferExpense( + transaction: Transaction, + accountId: UUID + ): BigDecimal = with(transaction) { + if (this.accountId == accountId && type == TransactionType.TRANSFER) + amount else BigDecimal.ZERO + } + + fun incomeCount( - fpTransaction: FPTransaction, + transaction: Transaction, accountId: UUID - ): BigDecimal = with(fpTransaction) { + ): BigDecimal = with(transaction) { if (this.accountId == accountId && type == TransactionType.INCOME) BigDecimal.ONE else BigDecimal.ZERO } fun expenseCount( - fpTransaction: FPTransaction, + transaction: Transaction, accountId: UUID - ): BigDecimal = with(fpTransaction) { + ): BigDecimal = with(transaction) { if (this.accountId == accountId && type == TransactionType.EXPENSE) BigDecimal.ONE else BigDecimal.ZERO } diff --git a/app/src/main/java/com/ivy/wallet/domain/fp/category/CategoryValueFunctions.kt b/app/src/main/java/com/ivy/wallet/domain/pure/transaction/CatValueFunctions.kt similarity index 58% rename from app/src/main/java/com/ivy/wallet/domain/fp/category/CategoryValueFunctions.kt rename to app/src/main/java/com/ivy/wallet/domain/pure/transaction/CatValueFunctions.kt index bce496e14f..217d4d2fd9 100644 --- a/app/src/main/java/com/ivy/wallet/domain/fp/category/CategoryValueFunctions.kt +++ b/app/src/main/java/com/ivy/wallet/domain/pure/transaction/CatValueFunctions.kt @@ -1,13 +1,11 @@ -package com.ivy.wallet.domain.fp.category +package com.ivy.wallet.domain.pure.transaction import arrow.core.Option import arrow.core.toOption +import com.ivy.fp.SideEffect import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.fp.core.SuspendValueFunction -import com.ivy.wallet.domain.fp.data.FPTransaction -import com.ivy.wallet.domain.fp.exchangeToBaseCurrency -import com.ivy.wallet.io.persistence.dao.ExchangeRateDao +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Transaction import java.math.BigDecimal import java.util.* @@ -16,17 +14,20 @@ typealias CategoryValueFunction = SuspendValueFunction, - val exchangeRateDao: ExchangeRateDao, - val baseCurrencyCode: String, + + @SideEffect + val exchangeToBaseCurrency: suspend ( + fromCurrency: Option, + amount: BigDecimal + ) -> Option ) suspend fun balance( - fpTransaction: FPTransaction, + transaction: Transaction, arg: Argument, - ): BigDecimal = with(fpTransaction) { - if (this.categoryId.orNull() == arg.categoryId) { + ): BigDecimal = with(transaction) { + if (this.categoryId == arg.categoryId) { when (type) { TransactionType.INCOME -> amount.toBaseCurrencyOrZero(arg, accountId) TransactionType.EXPENSE -> amount.toBaseCurrencyOrZero(arg, accountId).negate() @@ -36,10 +37,10 @@ object CategoryValueFunctions { } suspend fun income( - fpTransaction: FPTransaction, + transaction: Transaction, arg: Argument, - ): BigDecimal = with(fpTransaction) { - if (this.categoryId.orNull() == arg.categoryId) { + ): BigDecimal = with(transaction) { + if (this.categoryId == arg.categoryId) { when (type) { TransactionType.INCOME -> amount.toBaseCurrencyOrZero(arg, accountId) else -> BigDecimal.ZERO @@ -48,10 +49,10 @@ object CategoryValueFunctions { } suspend fun expense( - fpTransaction: FPTransaction, + transaction: Transaction, arg: Argument, - ): BigDecimal = with(fpTransaction) { - if (this.categoryId.orNull() == arg.categoryId) { + ): BigDecimal = with(transaction) { + if (this.categoryId == arg.categoryId) { when (type) { TransactionType.EXPENSE -> amount.toBaseCurrencyOrZero(arg, accountId) else -> BigDecimal.ZERO @@ -60,10 +61,10 @@ object CategoryValueFunctions { } suspend fun incomeCount( - fpTransaction: FPTransaction, + transaction: Transaction, arg: Argument, - ): BigDecimal = with(fpTransaction) { - if (this.categoryId.orNull() == arg.categoryId) { + ): BigDecimal = with(transaction) { + if (this.categoryId == arg.categoryId) { when (type) { TransactionType.INCOME -> BigDecimal.ONE else -> BigDecimal.ZERO @@ -72,10 +73,10 @@ object CategoryValueFunctions { } suspend fun expenseCount( - fpTransaction: FPTransaction, + transaction: Transaction, arg: Argument, - ): BigDecimal = with(fpTransaction) { - if (this.categoryId.orNull() == arg.categoryId) { + ): BigDecimal = with(transaction) { + if (this.categoryId == arg.categoryId) { when (type) { TransactionType.EXPENSE -> BigDecimal.ONE else -> BigDecimal.ZERO @@ -84,24 +85,20 @@ object CategoryValueFunctions { } private suspend fun BigDecimal.toBaseCurrencyOrZero( - argument: Argument, + arg: Argument, accountId: UUID ): BigDecimal { return this.convertToBaseCurrency( - argument = argument, + arg = arg, accountId = accountId ).orNull() ?: BigDecimal.ZERO } private suspend fun BigDecimal.convertToBaseCurrency( accountId: UUID, - argument: Argument + arg: Argument ): Option { - return exchangeToBaseCurrency( - exchangeRateDao = argument.exchangeRateDao, - baseCurrencyCode = argument.baseCurrencyCode, - fromAmount = this, - fromCurrencyCode = argument.accounts.find { it.id == accountId }?.currency.toOption() - ) + val trnCurrency = arg.accounts.find { it.id == accountId }?.currency.toOption() + return arg.exchangeToBaseCurrency(trnCurrency, this) } } \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/pure/transaction/FoldTransactions.kt b/app/src/main/java/com/ivy/wallet/domain/pure/transaction/FoldTransactions.kt new file mode 100644 index 0000000000..54dc741231 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/pure/transaction/FoldTransactions.kt @@ -0,0 +1,89 @@ +package com.ivy.wallet.domain.pure.transaction + +import arrow.core.NonEmptyList +import arrow.core.nonEmptyListOf +import com.ivy.fp.Pure +import com.ivy.wallet.domain.data.core.Transaction +import com.ivy.wallet.domain.pure.util.mapIndexedNel +import com.ivy.wallet.domain.pure.util.mapIndexedNelSuspend +import com.ivy.wallet.domain.pure.util.nonEmptyListOfZeros +import java.math.BigDecimal + +typealias ValueFunction = (Transaction, A) -> BigDecimal +typealias SuspendValueFunction = suspend (Transaction, A) -> BigDecimal + +@Pure +fun foldTransactions( + transactions: List, + valueFunctions: NonEmptyList>, + arg: Arg +): NonEmptyList = sumTransactionsInternal( + valueFunctionArgument = arg, + transactions = transactions, + valueFunctions = valueFunctions +) + +@Pure +internal tailrec fun sumTransactionsInternal( + transactions: List, + valueFunctionArgument: A, + valueFunctions: NonEmptyList>, + sum: NonEmptyList = nonEmptyListOfZeros(n = valueFunctions.size) +): NonEmptyList { + return if (transactions.isEmpty()) + sum + else + sumTransactionsInternal( + valueFunctionArgument = valueFunctionArgument, + transactions = transactions.drop(1), + valueFunctions = valueFunctions, + sum = sum.mapIndexedNel { index, sumValue -> + val valueFunction = valueFunctions[index] + sumValue + valueFunction(transactions.first(), valueFunctionArgument) + } + ) +} + +@Pure +suspend fun foldTransactionsSuspend( + transactions: List, + valueFunctions: NonEmptyList>, + arg: Arg +): NonEmptyList = sumTransactionsSuspendInternal( + transactions = transactions, + valueFunctions = valueFunctions, + valueFunctionArgument = arg +) + +@Pure +internal tailrec suspend fun sumTransactionsSuspendInternal( + transactions: List, + valueFunctionArgument: A, + valueFunctions: NonEmptyList>, + sum: NonEmptyList = nonEmptyListOfZeros(n = valueFunctions.size) +): NonEmptyList { + return if (transactions.isEmpty()) + sum + else + sumTransactionsSuspendInternal( + valueFunctionArgument = valueFunctionArgument, + transactions = transactions.drop(1), + valueFunctions = valueFunctions, + sum = sum.mapIndexedNelSuspend { index, sumValue -> + val valueFunction = valueFunctions[index] + sumValue + valueFunction(transactions.first(), valueFunctionArgument) + } + ) +} + +suspend fun sumTrns( + transactions: List, + valueFunction: SuspendValueFunction, + argument: A +): BigDecimal { + return sumTransactionsSuspendInternal( + transactions = transactions, + valueFunctionArgument = argument, + valueFunctions = nonEmptyListOf(valueFunction) + ).head +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/pure/transaction/TrnDateDividers.kt b/app/src/main/java/com/ivy/wallet/domain/pure/transaction/TrnDateDividers.kt new file mode 100644 index 0000000000..2d34d59d5a --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/pure/transaction/TrnDateDividers.kt @@ -0,0 +1,87 @@ +package com.ivy.wallet.domain.pure.transaction + +import arrow.core.Option +import arrow.core.toOption +import com.ivy.fp.Pure +import com.ivy.fp.SideEffect +import com.ivy.fp.then +import com.ivy.wallet.domain.data.TransactionHistoryDateDivider +import com.ivy.wallet.domain.data.TransactionHistoryItem +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Transaction +import com.ivy.wallet.domain.deprecated.logic.currency.ExchangeRatesLogic +import com.ivy.wallet.domain.pure.exchange.ExchangeData +import com.ivy.wallet.domain.pure.exchange.ExchangeTrnArgument +import com.ivy.wallet.domain.pure.exchange.exchangeInBaseCurrency +import com.ivy.wallet.io.persistence.dao.AccountDao +import com.ivy.wallet.io.persistence.dao.SettingsDao +import com.ivy.wallet.utils.convertUTCtoLocal +import com.ivy.wallet.utils.toEpochSeconds +import java.math.BigDecimal +import java.util.* + +@Deprecated("Migrate to actions") +suspend fun List.withDateDividers( + exchangeRatesLogic: ExchangeRatesLogic, + settingsDao: SettingsDao, + accountDao: AccountDao +): List { + return transactionsWithDateDividers( + transactions = this, + baseCurrencyCode = settingsDao.findFirst().currency, + getAccount = accountDao::findById then { it?.toDomain() }, + exchange = { data, amount -> + exchangeRatesLogic.convertAmount( + baseCurrency = data.baseCurrency, + fromCurrency = data.fromCurrency.orNull() ?: "", + toCurrency = data.toCurrency, + amount = amount.toDouble() + ).toBigDecimal().toOption() + } + ) +} + +@Pure +suspend fun transactionsWithDateDividers( + transactions: List, + baseCurrencyCode: String, + + @SideEffect + getAccount: suspend (accountId: UUID) -> Account?, + @SideEffect + exchange: suspend (ExchangeData, BigDecimal) -> Option +): List { + if (transactions.isEmpty()) return emptyList() + + return transactions + .groupBy { it.dateTime?.convertUTCtoLocal()?.toLocalDate() } + .filterKeys { it != null } + .toSortedMap { date1, date2 -> + if (date1 == null || date2 == null) return@toSortedMap 0 //this case shouldn't happen + (date2.atStartOfDay().toEpochSeconds() - date1.atStartOfDay().toEpochSeconds()).toInt() + } + .flatMap { (date, transactionsForDate) -> + val arg = ExchangeTrnArgument( + baseCurrency = baseCurrencyCode, + getAccount = getAccount, + exchange = exchange + ) + + + listOf( + TransactionHistoryDateDivider( + date = date!!, + income = sumTrns( + incomes(transactionsForDate), + ::exchangeInBaseCurrency, + arg + ).toDouble(), + expenses = sumTrns( + expenses(transactionsForDate), + ::exchangeInBaseCurrency, + arg + ).toDouble() + ), + ).plus(transactionsForDate) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/pure/transaction/TrnFunctions.kt b/app/src/main/java/com/ivy/wallet/domain/pure/transaction/TrnFunctions.kt new file mode 100644 index 0000000000..b8b4a694f0 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/pure/transaction/TrnFunctions.kt @@ -0,0 +1,48 @@ +package com.ivy.wallet.domain.pure.transaction + +import arrow.core.Option +import arrow.core.toOption +import com.ivy.fp.Pure +import com.ivy.wallet.domain.data.TransactionType +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Transaction +import com.ivy.wallet.domain.pure.account.accountCurrency +import java.time.LocalDate + +@Pure +fun expenses(transactions: List): List { + return transactions.filter { it.type == TransactionType.EXPENSE } +} + +@Pure +fun incomes(transactions: List): List { + return transactions.filter { it.type == TransactionType.INCOME } +} + +@Pure +fun transfers(transactions: List): List { + return transactions.filter { it.type == TransactionType.TRANSFER } +} + +@Pure +fun isUpcoming(transaction: Transaction, dateNow: LocalDate): Boolean { + val dueDate = transaction.dueDate?.toLocalDate() ?: return false + return dateNow.isBefore(dueDate) || dateNow.isEqual(dueDate) +} + +@Pure +fun isOverdue(transaction: Transaction, dateNow: LocalDate): Boolean { + val dueDate = transaction.dueDate?.toLocalDate() ?: return false + return dateNow.isAfter(dueDate) +} + +@Pure +fun trnCurrency( + transaction: Transaction, + accounts: List, + baseCurrency: String +): Option { + val account = accounts.find { it.id == transaction.accountId } + ?: return baseCurrency.toOption() + return accountCurrency(account, baseCurrency).toOption() +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/pure/transaction/WalletValueFunctions.kt b/app/src/main/java/com/ivy/wallet/domain/pure/transaction/WalletValueFunctions.kt new file mode 100644 index 0000000000..d046a0f04f --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/pure/transaction/WalletValueFunctions.kt @@ -0,0 +1,81 @@ +package com.ivy.wallet.domain.pure.transaction + +import com.ivy.fp.SideEffect +import com.ivy.wallet.domain.data.TransactionType +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Transaction +import com.ivy.wallet.domain.pure.exchange.ExchangeEffect +import com.ivy.wallet.domain.pure.exchange.exchangeInBaseCurrency +import java.math.BigDecimal + +object WalletValueFunctions { + data class Argument( + val accounts: List, + val baseCurrency: String, + + @SideEffect + val exchange: ExchangeEffect + ) + + suspend fun income( + transaction: Transaction, + arg: Argument + ): BigDecimal = with(transaction) { + when (type) { + TransactionType.INCOME -> exchangeInBaseCurrency( + transaction = this, + accounts = arg.accounts, + baseCurrency = arg.baseCurrency, + exchange = arg.exchange + ) + else -> BigDecimal.ZERO + } + } + + suspend fun transferIncome( + transaction: Transaction, + arg: Argument + ): BigDecimal = with(transaction) { + val condition = arg.accounts.any { it.id == this.toAccountId } + when { + type == TransactionType.TRANSFER && condition -> exchangeInBaseCurrency( + transaction = this.copy(amount = this.toAmount), + accounts = arg.accounts, + baseCurrency = arg.baseCurrency, + exchange = arg.exchange + ) + else -> BigDecimal.ZERO + } + } + + suspend fun expense( + transaction: Transaction, + arg: Argument + ): BigDecimal = with(transaction) { + when (type) { + TransactionType.EXPENSE -> exchangeInBaseCurrency( + transaction = this, + accounts = arg.accounts, + baseCurrency = arg.baseCurrency, + exchange = arg.exchange + ) + else -> BigDecimal.ZERO + } + } + + suspend fun transferExpenses( + transaction: Transaction, + arg: Argument + ): BigDecimal = with(transaction) { + val condition = arg.accounts.any { it.id == this.accountId } + when { + type == TransactionType.TRANSFER && condition -> exchangeInBaseCurrency( + transaction = this, + accounts = arg.accounts, + baseCurrency = arg.baseCurrency, + exchange = arg.exchange + ) + else -> BigDecimal.ZERO + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/pure/util/IvyDomainUtils.kt b/app/src/main/java/com/ivy/wallet/domain/pure/util/IvyDomainUtils.kt new file mode 100644 index 0000000000..2b1eb487e9 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/domain/pure/util/IvyDomainUtils.kt @@ -0,0 +1,3 @@ +package com.ivy.wallet.domain.pure.util + +fun Double?.nextOrderNum(): Double = this?.plus(1) ?: 0.0 \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/fp/core/Utils.kt b/app/src/main/java/com/ivy/wallet/domain/pure/util/Utils.kt similarity index 78% rename from app/src/main/java/com/ivy/wallet/domain/fp/core/Utils.kt rename to app/src/main/java/com/ivy/wallet/domain/pure/util/Utils.kt index dc953e5044..71c53d3627 100644 --- a/app/src/main/java/com/ivy/wallet/domain/fp/core/Utils.kt +++ b/app/src/main/java/com/ivy/wallet/domain/pure/util/Utils.kt @@ -1,6 +1,7 @@ -package com.ivy.wallet.domain.fp.core +package com.ivy.wallet.domain.pure.util import arrow.core.NonEmptyList +import arrow.core.Option import java.math.BigDecimal fun NonEmptyList.mapIndexedNel( @@ -25,4 +26,8 @@ fun nonEmptyListOfZeros(n: Int): NonEmptyList { return NonEmptyList.fromListUnsafe( List(n) { BigDecimal.ZERO } ) +} + +fun Option.orZero(): BigDecimal { + return this.orNull() ?: BigDecimal.ZERO } \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/network/IvySession.kt b/app/src/main/java/com/ivy/wallet/io/network/IvySession.kt index 5a4da91126..5f3d2ddffb 100644 --- a/app/src/main/java/com/ivy/wallet/io/network/IvySession.kt +++ b/app/src/main/java/com/ivy/wallet/io/network/IvySession.kt @@ -28,9 +28,9 @@ class IvySession( return userId != null && authToken != null } - fun initiate(authResponse: AuthResponse) { + suspend fun initiate(authResponse: AuthResponse) { val user = authResponse.user - userDao.save(user) + userDao.save(user.toEntity()) sharedPrefs.putString(SharedPrefs.SESSION_USER_ID, user.id.toString()) sharedPrefs.putString(SharedPrefs.SESSION_AUTH_TOKEN, authResponse.sessionToken) diff --git a/app/src/main/java/com/ivy/wallet/io/network/RestClient.kt b/app/src/main/java/com/ivy/wallet/io/network/RestClient.kt index 60dc6236f8..5bd80f9e06 100644 --- a/app/src/main/java/com/ivy/wallet/io/network/RestClient.kt +++ b/app/src/main/java/com/ivy/wallet/io/network/RestClient.kt @@ -205,9 +205,6 @@ class RestClient private constructor( } val analyticsService: AnalyticsService by lazy { retrofit.create(AnalyticsService::class.java) } val coinbaseService: CoinbaseService by lazy { retrofit.create(CoinbaseService::class.java) } - val bankIntegrationsService: BankIntegrationsService by lazy { - retrofit.create(BankIntegrationsService::class.java) - } val githubService: GithubService by lazy { retrofit.create(GithubService::class.java) } val nukeService: NukeService by lazy { retrofit.create(NukeService::class.java) } } \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/network/data/AccountDTO.kt b/app/src/main/java/com/ivy/wallet/io/network/data/AccountDTO.kt new file mode 100644 index 0000000000..37f758c248 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/io/network/data/AccountDTO.kt @@ -0,0 +1,30 @@ +package com.ivy.wallet.io.network.data + +import androidx.compose.ui.graphics.toArgb +import com.ivy.wallet.io.persistence.data.AccountEntity +import com.ivy.wallet.ui.theme.Green +import java.util.* + +data class AccountDTO( + val name: String, + val currency: String? = null, + val color: Int = Green.toArgb(), + val icon: String? = null, + val orderNum: Double = 0.0, + val includeInBalance: Boolean = true, + + val id: UUID = UUID.randomUUID() +) { + fun toEntity(): AccountEntity = AccountEntity( + name = name, + currency = currency, + color = color, + icon = icon, + orderNum = orderNum, + includeInBalance = includeInBalance, + id = id, + isSynced = true, + isDeleted = false + ) +} + diff --git a/app/src/main/java/com/ivy/wallet/io/network/data/BudgetDTO.kt b/app/src/main/java/com/ivy/wallet/io/network/data/BudgetDTO.kt new file mode 100644 index 0000000000..c3e395afc8 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/io/network/data/BudgetDTO.kt @@ -0,0 +1,66 @@ +package com.ivy.wallet.io.network.data + +import com.ivy.wallet.io.persistence.data.BudgetEntity +import java.util.* + +data class BudgetDTO( + val name: String, + val amount: Double, + + val categoryIdsSerialized: String?, + val accountIdsSerialized: String?, + + val orderId: Double, + val id: UUID = UUID.randomUUID() +) { + fun toEntity(): BudgetEntity = BudgetEntity( + name = name, + amount = amount, + categoryIdsSerialized = categoryIdsSerialized, + accountIdsSerialized = accountIdsSerialized, + orderId = orderId, + id = id, + isSynced = true, + isDeleted = false + ) + + companion object { + fun serialize(ids: List): String { + return ids.joinToString(separator = ",") + } + + fun type(categoriesCount: Int): String { + return when (categoriesCount) { + 0 -> "Total Budget" + 1 -> "Category Budget" + else -> "Multi-Category ($categoriesCount) Budget" + } + } + } + + fun parseCategoryIds(): List { + return parseIdsString(categoryIdsSerialized) + } + + fun parseAccountIds(): List { + return parseIdsString(accountIdsSerialized) + } + + private fun parseIdsString(idsString: String?): List { + return try { + if (idsString == null) return emptyList() + + idsString + .split(",") + .map { UUID.fromString(it) } + } catch (e: Exception) { + e.printStackTrace() + emptyList() + } + } + + + fun validate(): Boolean { + return name.isNotEmpty() && amount > 0.0 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/network/data/CategoryDTO.kt b/app/src/main/java/com/ivy/wallet/io/network/data/CategoryDTO.kt new file mode 100644 index 0000000000..7446f8184e --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/io/network/data/CategoryDTO.kt @@ -0,0 +1,25 @@ +package com.ivy.wallet.io.network.data + +import androidx.compose.ui.graphics.toArgb +import com.ivy.wallet.io.persistence.data.CategoryEntity +import com.ivy.wallet.ui.theme.Ivy +import java.util.* + +data class CategoryDTO( + val name: String, + val color: Int = Ivy.toArgb(), + val icon: String? = null, + val orderNum: Double = 0.0, + + val id: UUID = UUID.randomUUID() +) { + fun toEntity(): CategoryEntity = CategoryEntity( + name = name, + color = color, + icon = icon, + orderNum = orderNum, + isSynced = true, + isDeleted = false, + id = id + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/network/data/ExchangeRateDTO.kt b/app/src/main/java/com/ivy/wallet/io/network/data/ExchangeRateDTO.kt new file mode 100644 index 0000000000..0dc2b63ee9 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/io/network/data/ExchangeRateDTO.kt @@ -0,0 +1,13 @@ +package com.ivy.wallet.io.network.data + +data class ExchangeRateDTO( + val baseCurrency: String, + val currency: String, + val rate: Double, +) { + fun toEntity(): ExchangeRateDTO = ExchangeRateDTO( + baseCurrency = baseCurrency, + currency = currency, + rate = rate + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/network/data/LoanDTO.kt b/app/src/main/java/com/ivy/wallet/io/network/data/LoanDTO.kt new file mode 100644 index 0000000000..bccc945f43 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/io/network/data/LoanDTO.kt @@ -0,0 +1,35 @@ +package com.ivy.wallet.io.network.data + +import com.ivy.wallet.domain.data.LoanType +import com.ivy.wallet.io.persistence.data.LoanEntity +import java.util.* + +data class LoanDTO( + val name: String, + val amount: Double, + val type: LoanType, + val color: Int = 0, + val icon: String? = null, + val orderNum: Double = 0.0, + val accountId: UUID? = null, + + val id: UUID = UUID.randomUUID() +) { + fun toEntity(): LoanEntity = LoanEntity( + name = name, + amount = amount, + type = type, + color = color, + icon = icon, + orderNum = orderNum, + accountId = accountId, + id = id, + + isSynced = true, + isDeleted = false + ) + + fun humanReadableType(): String { + return if (type == LoanType.BORROW) "BORROWED" else "LENT" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/network/data/LoanRecordDTO.kt b/app/src/main/java/com/ivy/wallet/io/network/data/LoanRecordDTO.kt new file mode 100644 index 0000000000..9062cec26f --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/io/network/data/LoanRecordDTO.kt @@ -0,0 +1,32 @@ +package com.ivy.wallet.io.network.data + +import com.ivy.wallet.io.persistence.data.LoanRecordEntity +import java.time.LocalDateTime +import java.util.* + +data class LoanRecordDTO( + val loanId: UUID, + val amount: Double, + val note: String? = null, + val dateTime: LocalDateTime, + val interest: Boolean = false, + val accountId: UUID? = null, + //This is used store the converted amount for currencies which are different from the loan account currency + val convertedAmount: Double? = null, + + val id: UUID = UUID.randomUUID() +) { + fun toEntity(): LoanRecordEntity = LoanRecordEntity( + loanId = loanId, + amount = amount, + note = note, + dateTime = dateTime, + interest = interest, + accountId = accountId, + convertedAmount = convertedAmount, + id = id, + + isSynced = true, + isDeleted = false + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/network/data/PlannedPaymentRuleDTO.kt b/app/src/main/java/com/ivy/wallet/io/network/data/PlannedPaymentRuleDTO.kt new file mode 100644 index 0000000000..f9310370d3 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/io/network/data/PlannedPaymentRuleDTO.kt @@ -0,0 +1,39 @@ +package com.ivy.wallet.io.network.data + +import com.ivy.wallet.domain.data.IntervalType +import com.ivy.wallet.domain.data.TransactionType +import com.ivy.wallet.io.persistence.data.PlannedPaymentRuleEntity +import java.time.LocalDateTime +import java.util.* + +data class PlannedPaymentRuleDTO( + val startDate: LocalDateTime?, + val intervalN: Int?, + val intervalType: IntervalType?, + val oneTime: Boolean, + + val type: TransactionType, + val accountId: UUID, + val amount: Double = 0.0, + val categoryId: UUID? = null, + val title: String? = null, + val description: String? = null, + + val id: UUID = UUID.randomUUID() +) { + fun toEntity(): PlannedPaymentRuleEntity = PlannedPaymentRuleEntity( + startDate = startDate, + intervalN = intervalN, + intervalType = intervalType, + oneTime = oneTime, + type = type, + accountId = accountId, + amount = amount, + categoryId = categoryId, + title = title, + id = id, + + isSynced = true, + isDeleted = false + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/network/data/SettingsDTO.kt b/app/src/main/java/com/ivy/wallet/io/network/data/SettingsDTO.kt new file mode 100644 index 0000000000..49b0d54695 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/io/network/data/SettingsDTO.kt @@ -0,0 +1,25 @@ +package com.ivy.wallet.io.network.data + +import com.ivy.design.l0_system.Theme +import com.ivy.wallet.io.persistence.data.SettingsEntity +import java.util.* + +data class SettingsDTO( + val theme: Theme, + val currency: String, + val bufferAmount: Double, + val name: String, + + val id: UUID = UUID.randomUUID() +) { + fun toEntity(): SettingsEntity = SettingsEntity( + theme = theme, + currency = currency, + bufferAmount = bufferAmount, + name = name, + id = id, + + isSynced = true, + isDeleted = false + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/network/data/TransactionDTO.kt b/app/src/main/java/com/ivy/wallet/io/network/data/TransactionDTO.kt new file mode 100644 index 0000000000..fa7c7f5f54 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/io/network/data/TransactionDTO.kt @@ -0,0 +1,52 @@ +package com.ivy.wallet.io.network.data + +import com.ivy.wallet.domain.data.TransactionType +import com.ivy.wallet.io.persistence.data.TransactionEntity +import java.time.LocalDateTime +import java.util.* + +data class TransactionDTO( + val accountId: UUID, + val type: TransactionType, + val amount: Double, + val toAccountId: UUID? = null, + val toAmount: Double? = null, + val title: String? = null, + val description: String? = null, + val dateTime: LocalDateTime? = null, + val categoryId: UUID? = null, + val dueDate: LocalDateTime? = null, + + val recurringRuleId: UUID? = null, + + val attachmentUrl: String? = null, + + //This refers to the loan id that is linked with a transaction + val loanId: UUID? = null, + + //This refers to the loan record id that is linked with a transaction + val loanRecordId: UUID? = null, + + val id: UUID = UUID.randomUUID() +) { + fun toEntity(): TransactionEntity = TransactionEntity( + accountId = accountId, + type = type, + amount = amount, + toAccountId = toAccountId, + toAmount = toAmount, + title = title, + description = description, + dateTime = dateTime, + categoryId = categoryId, + dueDate = dueDate, + recurringRuleId = recurringRuleId, + attachmentUrl = attachmentUrl, + loanId = loanId, + loanRecordId = loanRecordId, + id = id, + + isSynced = true, + isDeleted = false + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/network/data/UserDTO.kt b/app/src/main/java/com/ivy/wallet/io/network/data/UserDTO.kt new file mode 100644 index 0000000000..fcb34ae15a --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/io/network/data/UserDTO.kt @@ -0,0 +1,30 @@ +package com.ivy.wallet.io.network.data + +import com.ivy.wallet.domain.data.AuthProviderType +import com.ivy.wallet.io.persistence.data.UserEntity +import java.util.* + +data class UserDTO( + val email: String, + val authProviderType: AuthProviderType, + var firstName: String, + val lastName: String?, + val profilePicture: String?, + val color: Int, + + val testUser: Boolean = false, + var id: UUID +) { + fun toEntity(): UserEntity = UserEntity( + email = email, + authProviderType = authProviderType, + firstName = firstName, + lastName = lastName, + profilePicture = profilePicture, + color = color, + testUser = testUser, + id = id, + ) + + fun names(): String = firstName + if (lastName != null) " $lastName" else "" +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/network/request/account/AccountsResponse.kt b/app/src/main/java/com/ivy/wallet/io/network/request/account/AccountsResponse.kt index 31ae7b1250..fd0d885d68 100644 --- a/app/src/main/java/com/ivy/wallet/io/network/request/account/AccountsResponse.kt +++ b/app/src/main/java/com/ivy/wallet/io/network/request/account/AccountsResponse.kt @@ -1,8 +1,8 @@ package com.ivy.wallet.io.network.request.account -import com.ivy.wallet.domain.data.entity.Account +import com.ivy.wallet.io.network.data.AccountDTO data class AccountsResponse( - val accounts: List + val accounts: List ) \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/network/request/account/UpdateAccountRequest.kt b/app/src/main/java/com/ivy/wallet/io/network/request/account/UpdateAccountRequest.kt index 9e3bca215e..d2136e8a6d 100644 --- a/app/src/main/java/com/ivy/wallet/io/network/request/account/UpdateAccountRequest.kt +++ b/app/src/main/java/com/ivy/wallet/io/network/request/account/UpdateAccountRequest.kt @@ -1,7 +1,7 @@ package com.ivy.wallet.io.network.request.account -import com.ivy.wallet.domain.data.entity.Account +import com.ivy.wallet.io.network.data.AccountDTO data class UpdateAccountRequest( - val account: Account? = null + val account: AccountDTO? = null ) \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/network/request/auth/AuthResponse.kt b/app/src/main/java/com/ivy/wallet/io/network/request/auth/AuthResponse.kt index 674a7cf43e..62eddc5051 100644 --- a/app/src/main/java/com/ivy/wallet/io/network/request/auth/AuthResponse.kt +++ b/app/src/main/java/com/ivy/wallet/io/network/request/auth/AuthResponse.kt @@ -1,7 +1,7 @@ package com.ivy.wallet.io.network.request.auth import com.google.gson.annotations.SerializedName -import com.ivy.wallet.domain.data.entity.User +import com.ivy.wallet.domain.data.core.User data class AuthResponse( @SerializedName("user") diff --git a/app/src/main/java/com/ivy/wallet/io/network/request/auth/UpdateUserInfoResponse.kt b/app/src/main/java/com/ivy/wallet/io/network/request/auth/UpdateUserInfoResponse.kt index 6570cfbdd6..d37241f64f 100644 --- a/app/src/main/java/com/ivy/wallet/io/network/request/auth/UpdateUserInfoResponse.kt +++ b/app/src/main/java/com/ivy/wallet/io/network/request/auth/UpdateUserInfoResponse.kt @@ -1,7 +1,7 @@ package com.ivy.wallet.io.network.request.auth import com.google.gson.annotations.SerializedName -import com.ivy.wallet.domain.data.entity.User +import com.ivy.wallet.domain.data.core.User data class UpdateUserInfoResponse( @SerializedName("user") diff --git a/app/src/main/java/com/ivy/wallet/io/network/request/bankintegrations/BankAccountsResponse.kt b/app/src/main/java/com/ivy/wallet/io/network/request/bankintegrations/BankAccountsResponse.kt deleted file mode 100644 index efa3904b45..0000000000 --- a/app/src/main/java/com/ivy/wallet/io/network/request/bankintegrations/BankAccountsResponse.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.ivy.wallet.io.network.request.bankintegrations - -import com.google.gson.annotations.SerializedName -import com.ivy.wallet.domain.data.bankintegrations.SEAccount - -data class BankAccountsResponse( - @SerializedName("accounts") - val accounts: List -) \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/network/request/bankintegrations/BankConnectionSessionResponse.kt b/app/src/main/java/com/ivy/wallet/io/network/request/bankintegrations/BankConnectionSessionResponse.kt deleted file mode 100644 index db67cf0dc5..0000000000 --- a/app/src/main/java/com/ivy/wallet/io/network/request/bankintegrations/BankConnectionSessionResponse.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.ivy.wallet.io.network.request.bankintegrations - -import com.google.gson.annotations.SerializedName - -data class BankConnectionSessionResponse( - @SerializedName("connectUrl") - val connectUrl: String -) \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/network/request/bankintegrations/BankConnectionsResponse.kt b/app/src/main/java/com/ivy/wallet/io/network/request/bankintegrations/BankConnectionsResponse.kt deleted file mode 100644 index 1dae8bb2ca..0000000000 --- a/app/src/main/java/com/ivy/wallet/io/network/request/bankintegrations/BankConnectionsResponse.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.ivy.wallet.io.network.request.bankintegrations - -import com.google.gson.annotations.SerializedName -import com.ivy.wallet.domain.data.bankintegrations.SEConnection - -data class BankConnectionsResponse( - @SerializedName("connections") - val connections: List -) \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/network/request/bankintegrations/BankTransactionsResponse.kt b/app/src/main/java/com/ivy/wallet/io/network/request/bankintegrations/BankTransactionsResponse.kt deleted file mode 100644 index 2869ede286..0000000000 --- a/app/src/main/java/com/ivy/wallet/io/network/request/bankintegrations/BankTransactionsResponse.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.ivy.wallet.io.network.request.bankintegrations - -import com.google.gson.annotations.SerializedName -import com.ivy.wallet.domain.data.bankintegrations.SETransaction - -data class BankTransactionsResponse( - @SerializedName("transactions") - val transactions: List -) \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/network/request/budget/BudgetsResponse.kt b/app/src/main/java/com/ivy/wallet/io/network/request/budget/BudgetsResponse.kt index 3f3eb0c2ef..0a390092f8 100644 --- a/app/src/main/java/com/ivy/wallet/io/network/request/budget/BudgetsResponse.kt +++ b/app/src/main/java/com/ivy/wallet/io/network/request/budget/BudgetsResponse.kt @@ -1,8 +1,8 @@ package com.ivy.wallet.io.network.request.budget -import com.ivy.wallet.domain.data.entity.Budget +import com.ivy.wallet.io.network.data.BudgetDTO data class BudgetsResponse( - val budgets: List + val budgets: List ) \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/network/request/budget/CrupdateBudgetRequest.kt b/app/src/main/java/com/ivy/wallet/io/network/request/budget/CrupdateBudgetRequest.kt index 78f90f8af5..4273e5efb4 100644 --- a/app/src/main/java/com/ivy/wallet/io/network/request/budget/CrupdateBudgetRequest.kt +++ b/app/src/main/java/com/ivy/wallet/io/network/request/budget/CrupdateBudgetRequest.kt @@ -1,8 +1,8 @@ package com.ivy.wallet.io.network.request.budget -import com.ivy.wallet.domain.data.entity.Budget +import com.ivy.wallet.io.network.data.BudgetDTO data class CrupdateBudgetRequest( - val budget: Budget? = null + val budget: BudgetDTO? = null ) \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/network/request/category/UpdateWalletCategoryRequest.kt b/app/src/main/java/com/ivy/wallet/io/network/request/category/UpdateWalletCategoryRequest.kt index 3fd9b1e4e8..5642bce39d 100644 --- a/app/src/main/java/com/ivy/wallet/io/network/request/category/UpdateWalletCategoryRequest.kt +++ b/app/src/main/java/com/ivy/wallet/io/network/request/category/UpdateWalletCategoryRequest.kt @@ -1,7 +1,7 @@ package com.ivy.wallet.io.network.request.category -import com.ivy.wallet.domain.data.entity.Category +import com.ivy.wallet.io.network.data.CategoryDTO data class UpdateWalletCategoryRequest( - val category: Category? = null + val category: CategoryDTO? = null ) \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/network/request/category/WalletCategoriesResponse.kt b/app/src/main/java/com/ivy/wallet/io/network/request/category/WalletCategoriesResponse.kt index 1dd6db5340..41ff386004 100644 --- a/app/src/main/java/com/ivy/wallet/io/network/request/category/WalletCategoriesResponse.kt +++ b/app/src/main/java/com/ivy/wallet/io/network/request/category/WalletCategoriesResponse.kt @@ -1,8 +1,8 @@ package com.ivy.wallet.io.network.request.category -import com.ivy.wallet.domain.data.entity.Category +import com.ivy.wallet.io.network.data.CategoryDTO data class WalletCategoriesResponse( - val categories: List + val categories: List ) \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/network/request/loan/LoanRecordsResponse.kt b/app/src/main/java/com/ivy/wallet/io/network/request/loan/LoanRecordsResponse.kt index 9ab03665de..be6a3e030a 100644 --- a/app/src/main/java/com/ivy/wallet/io/network/request/loan/LoanRecordsResponse.kt +++ b/app/src/main/java/com/ivy/wallet/io/network/request/loan/LoanRecordsResponse.kt @@ -1,7 +1,7 @@ package com.ivy.wallet.io.network.request.loan -import com.ivy.wallet.domain.data.entity.LoanRecord +import com.ivy.wallet.io.network.data.LoanRecordDTO data class LoanRecordsResponse( - val loanRecords: List + val loanRecords: List ) \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/network/request/loan/LoansResponse.kt b/app/src/main/java/com/ivy/wallet/io/network/request/loan/LoansResponse.kt index 8acae599be..6397632fa8 100644 --- a/app/src/main/java/com/ivy/wallet/io/network/request/loan/LoansResponse.kt +++ b/app/src/main/java/com/ivy/wallet/io/network/request/loan/LoansResponse.kt @@ -1,7 +1,7 @@ package com.ivy.wallet.io.network.request.loan -import com.ivy.wallet.domain.data.entity.Loan +import com.ivy.wallet.io.network.data.LoanDTO data class LoansResponse( - val loans: List + val loans: List ) \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/network/request/loan/UpdateLoanRecordRequest.kt b/app/src/main/java/com/ivy/wallet/io/network/request/loan/UpdateLoanRecordRequest.kt index 6b7f5bb300..b35a489089 100644 --- a/app/src/main/java/com/ivy/wallet/io/network/request/loan/UpdateLoanRecordRequest.kt +++ b/app/src/main/java/com/ivy/wallet/io/network/request/loan/UpdateLoanRecordRequest.kt @@ -1,7 +1,7 @@ package com.ivy.wallet.io.network.request.loan -import com.ivy.wallet.domain.data.entity.LoanRecord +import com.ivy.wallet.io.network.data.LoanRecordDTO data class UpdateLoanRecordRequest( - val loanRecord: LoanRecord? = null + val loanRecord: LoanRecordDTO? = null ) \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/network/request/loan/UpdateLoanRequest.kt b/app/src/main/java/com/ivy/wallet/io/network/request/loan/UpdateLoanRequest.kt index 2a5eb75061..7b3f48408a 100644 --- a/app/src/main/java/com/ivy/wallet/io/network/request/loan/UpdateLoanRequest.kt +++ b/app/src/main/java/com/ivy/wallet/io/network/request/loan/UpdateLoanRequest.kt @@ -1,7 +1,7 @@ package com.ivy.wallet.io.network.request.loan -import com.ivy.wallet.domain.data.entity.Loan +import com.ivy.wallet.io.network.data.LoanDTO data class UpdateLoanRequest( - val loan: Loan? = null + val loan: LoanDTO? = null ) \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/network/request/planned/PlannedPaymentRulesResponse.kt b/app/src/main/java/com/ivy/wallet/io/network/request/planned/PlannedPaymentRulesResponse.kt index 7beb4bd362..5009e0cbaa 100644 --- a/app/src/main/java/com/ivy/wallet/io/network/request/planned/PlannedPaymentRulesResponse.kt +++ b/app/src/main/java/com/ivy/wallet/io/network/request/planned/PlannedPaymentRulesResponse.kt @@ -1,8 +1,8 @@ package com.ivy.wallet.io.network.request.planned -import com.ivy.wallet.domain.data.entity.PlannedPaymentRule +import com.ivy.wallet.io.network.data.PlannedPaymentRuleDTO data class PlannedPaymentRulesResponse( - val rules: List + val rules: List ) \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/network/request/planned/UpdatePlannedPaymentRuleRequest.kt b/app/src/main/java/com/ivy/wallet/io/network/request/planned/UpdatePlannedPaymentRuleRequest.kt index d0e22444d3..f0e53df538 100644 --- a/app/src/main/java/com/ivy/wallet/io/network/request/planned/UpdatePlannedPaymentRuleRequest.kt +++ b/app/src/main/java/com/ivy/wallet/io/network/request/planned/UpdatePlannedPaymentRuleRequest.kt @@ -1,7 +1,7 @@ package com.ivy.wallet.io.network.request.planned -import com.ivy.wallet.domain.data.entity.PlannedPaymentRule +import com.ivy.wallet.io.network.data.PlannedPaymentRuleDTO data class UpdatePlannedPaymentRuleRequest( - val rule: PlannedPaymentRule? = null + val rule: PlannedPaymentRuleDTO? = null ) \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/network/request/transaction/TransactionsResponse.kt b/app/src/main/java/com/ivy/wallet/io/network/request/transaction/TransactionsResponse.kt index 7abf78d9a5..e09e65b88f 100644 --- a/app/src/main/java/com/ivy/wallet/io/network/request/transaction/TransactionsResponse.kt +++ b/app/src/main/java/com/ivy/wallet/io/network/request/transaction/TransactionsResponse.kt @@ -1,8 +1,8 @@ package com.ivy.wallet.io.network.request.transaction -import com.ivy.wallet.domain.data.entity.Transaction +import com.ivy.wallet.io.network.data.TransactionDTO data class TransactionsResponse( - val transactions: List + val transactions: List ) \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/network/request/transaction/UpdateTransactionRequest.kt b/app/src/main/java/com/ivy/wallet/io/network/request/transaction/UpdateTransactionRequest.kt index 54a1258ac2..d0c26b57a0 100644 --- a/app/src/main/java/com/ivy/wallet/io/network/request/transaction/UpdateTransactionRequest.kt +++ b/app/src/main/java/com/ivy/wallet/io/network/request/transaction/UpdateTransactionRequest.kt @@ -1,7 +1,7 @@ package com.ivy.wallet.io.network.request.transaction -import com.ivy.wallet.domain.data.entity.Transaction +import com.ivy.wallet.io.network.data.TransactionDTO data class UpdateTransactionRequest( - val transaction: Transaction? = null + val transaction: TransactionDTO? = null ) \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/network/service/BankIntegrationsService.kt b/app/src/main/java/com/ivy/wallet/io/network/service/BankIntegrationsService.kt deleted file mode 100644 index b2dbcdb296..0000000000 --- a/app/src/main/java/com/ivy/wallet/io/network/service/BankIntegrationsService.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.ivy.wallet.io.network.service - -import com.ivy.wallet.io.network.request.bankintegrations.BankAccountsResponse -import com.ivy.wallet.io.network.request.bankintegrations.BankConnectionSessionResponse -import com.ivy.wallet.io.network.request.bankintegrations.BankConnectionsResponse -import com.ivy.wallet.io.network.request.bankintegrations.BankTransactionsResponse -import retrofit2.http.DELETE -import retrofit2.http.GET -import retrofit2.http.POST - -interface BankIntegrationsService { - @POST("wallet/bank-integrations/connect") - suspend fun connectSession(): BankConnectionSessionResponse - - @GET("wallet/bank-integrations/connections") - suspend fun getConnections(): BankConnectionsResponse - - @GET("wallet/bank-integrations/accounts") - suspend fun getAccounts(): BankAccountsResponse - - @GET("wallet/bank-integrations/transactions") - suspend fun getTransactions(): BankTransactionsResponse - - @DELETE("wallet/bank-integrations/remove-customer") - suspend fun removeCustomer() -} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/persistence/IvyRoomDatabase.kt b/app/src/main/java/com/ivy/wallet/io/persistence/IvyRoomDatabase.kt index c103b00085..66b50e77ea 100644 --- a/app/src/main/java/com/ivy/wallet/io/persistence/IvyRoomDatabase.kt +++ b/app/src/main/java/com/ivy/wallet/io/persistence/IvyRoomDatabase.kt @@ -1,23 +1,28 @@ package com.ivy.wallet.io.persistence import android.content.Context -import androidx.room.Database -import androidx.room.Room -import androidx.room.RoomDatabase -import androidx.room.TypeConverters -import com.ivy.wallet.domain.data.entity.* +import androidx.room.* +import androidx.room.migration.AutoMigrationSpec import com.ivy.wallet.io.persistence.dao.* +import com.ivy.wallet.io.persistence.data.* import com.ivy.wallet.io.persistence.migration.* @Database( entities = [ - Account::class, Transaction::class, Category::class, - WishlistItem::class, Settings::class, PlannedPaymentRule::class, - User::class, ExchangeRate::class, Budget::class, Loan::class, - LoanRecord::class + AccountEntity::class, TransactionEntity::class, CategoryEntity::class, + SettingsEntity::class, PlannedPaymentRuleEntity::class, + UserEntity::class, ExchangeRateEntity::class, BudgetEntity::class, + LoanEntity::class, LoanRecordEntity::class ], - version = 120, + autoMigrations = [ + AutoMigration( + from = 121, + to = 122, + spec = IvyRoomDatabase.DeleteSEMigration::class + ) + ], + version = 122, exportSchema = true ) @TypeConverters(RoomTypeConverters::class) @@ -30,8 +35,6 @@ abstract class IvyRoomDatabase : RoomDatabase() { abstract fun budgetDao(): BudgetDao - abstract fun wishlistItemDao(): WishlistItemDao - abstract fun plannedPaymentRuleDao(): PlannedPaymentRuleDao abstract fun settingsDao(): SettingsDao @@ -69,16 +72,16 @@ abstract class IvyRoomDatabase : RoomDatabase() { Migration117to118_Budgets(), Migration118to119_Loans(), Migration119to120_LoanTransactions(), + Migration120to121_DropWishlistItem() ) .build() } } - fun reset() { + suspend fun reset() { accountDao().deleteAll() transactionDao().deleteAll() categoryDao().deleteAll() - wishlistItemDao().deleteAll() settingsDao().deleteAll() plannedPaymentRuleDao().deleteAll() userDao().deleteAll() @@ -86,4 +89,10 @@ abstract class IvyRoomDatabase : RoomDatabase() { loanDao().deleteAll() loanRecordDao().deleteAll() } + + @DeleteColumn(tableName = "accounts", columnName = "seAccountId") + @DeleteColumn(tableName = "transactions", columnName = "seTransactionId") + @DeleteColumn(tableName = "transactions", columnName = "seAutoCategoryId") + @DeleteColumn(tableName = "categories", columnName = "seCategoryName") + class DeleteSEMigration : AutoMigrationSpec } \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/persistence/SharedPrefs.kt b/app/src/main/java/com/ivy/wallet/io/persistence/SharedPrefs.kt index 83b7695abd..4706a6fd5b 100644 --- a/app/src/main/java/com/ivy/wallet/io/persistence/SharedPrefs.kt +++ b/app/src/main/java/com/ivy/wallet/io/persistence/SharedPrefs.kt @@ -49,6 +49,7 @@ class SharedPrefs(appContext: Context) { const val START_DATE_OF_MONTH = "start_date_of_month" const val SHOW_NOTIFICATIONS = "show_notifications" const val HIDE_CURRENT_BALANCE = "hide_current_balance" + const val TRANSFERS_AS_INCOME_EXPENSE = "transfers_as_inc_exp" //----------------------------- App Settings ----------------------------------------------- //-------------------------------- Customer Journey ---------------------------------------- diff --git a/app/src/main/java/com/ivy/wallet/io/persistence/dao/AccountDao.kt b/app/src/main/java/com/ivy/wallet/io/persistence/dao/AccountDao.kt index f3bb21fbce..8dee90628c 100644 --- a/app/src/main/java/com/ivy/wallet/io/persistence/dao/AccountDao.kt +++ b/app/src/main/java/com/ivy/wallet/io/persistence/dao/AccountDao.kt @@ -4,41 +4,38 @@ import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query -import com.ivy.wallet.domain.data.entity.Account +import com.ivy.wallet.io.persistence.data.AccountEntity import java.util.* @Dao interface AccountDao { @Insert(onConflict = OnConflictStrategy.REPLACE) - fun save(value: Account) + suspend fun save(value: AccountEntity) @Insert(onConflict = OnConflictStrategy.REPLACE) - fun save(value: List) + suspend fun save(value: List) @Query("SELECT * FROM accounts WHERE isDeleted = 0 ORDER BY orderNum ASC") - fun findAll(): List + suspend fun findAll(): List @Query("SELECT * FROM accounts WHERE isSynced = :synced AND isDeleted = :deleted") - fun findByIsSyncedAndIsDeleted(synced: Boolean, deleted: Boolean = false): List + suspend fun findByIsSyncedAndIsDeleted(synced: Boolean, deleted: Boolean = false): List @Query("SELECT * FROM accounts WHERE id = :id") - fun findById(id: UUID): Account? - - @Query("SELECT * FROM accounts WHERE seAccountId = :seAccountId") - fun findBySeAccountId(seAccountId: String): Account? + suspend fun findById(id: UUID): AccountEntity? @Query("UPDATE accounts SET isDeleted = 1, isSynced = 0 WHERE id = :id") - fun flagDeleted(id: UUID) + suspend fun flagDeleted(id: UUID) @Query("DELETE FROM accounts WHERE id = :id") - fun deleteById(id: UUID) + suspend fun deleteById(id: UUID) @Query("DELETE FROM accounts") - fun deleteAll() + suspend fun deleteAll() @Query("SELECT MIN(orderNum) FROM accounts") - fun findMinOrderNum(): Double + suspend fun findMinOrderNum(): Double @Query("SELECT MAX(orderNum) FROM accounts") - fun findMaxOrderNum(): Double + suspend fun findMaxOrderNum(): Double? } \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/persistence/dao/BudgetDao.kt b/app/src/main/java/com/ivy/wallet/io/persistence/dao/BudgetDao.kt index bfc0e5b6d6..b04d7e2ae6 100644 --- a/app/src/main/java/com/ivy/wallet/io/persistence/dao/BudgetDao.kt +++ b/app/src/main/java/com/ivy/wallet/io/persistence/dao/BudgetDao.kt @@ -4,35 +4,35 @@ import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query -import com.ivy.wallet.domain.data.entity.Budget +import com.ivy.wallet.io.persistence.data.BudgetEntity import java.util.* @Dao interface BudgetDao { @Insert(onConflict = OnConflictStrategy.REPLACE) - fun save(value: Budget) + suspend fun save(value: BudgetEntity) @Insert(onConflict = OnConflictStrategy.REPLACE) - fun save(value: List) + suspend fun save(value: List) @Query("SELECT * FROM budgets WHERE isDeleted = 0 ORDER BY orderId ASC") - fun findAll(): List + suspend fun findAll(): List @Query("SELECT * FROM budgets WHERE isSynced = :synced AND isDeleted = :deleted") - fun findByIsSyncedAndIsDeleted(synced: Boolean, deleted: Boolean = false): List + suspend fun findByIsSyncedAndIsDeleted(synced: Boolean, deleted: Boolean = false): List @Query("SELECT * FROM budgets WHERE id = :id") - fun findById(id: UUID): Budget? + suspend fun findById(id: UUID): BudgetEntity? @Query("DELETE FROM budgets WHERE id = :id") - fun deleteById(id: UUID) + suspend fun deleteById(id: UUID) @Query("UPDATE budgets SET isDeleted = 1, isSynced = 0 WHERE id = :id") - fun flagDeleted(id: UUID) + suspend fun flagDeleted(id: UUID) @Query("DELETE FROM budgets") - fun deleteAll() + suspend fun deleteAll() @Query("SELECT MAX(orderId) FROM budgets") - fun findMaxOrderNum(): Double + suspend fun findMaxOrderNum(): Double? } \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/persistence/dao/CategoryDao.kt b/app/src/main/java/com/ivy/wallet/io/persistence/dao/CategoryDao.kt index 22f86856e4..5055935921 100644 --- a/app/src/main/java/com/ivy/wallet/io/persistence/dao/CategoryDao.kt +++ b/app/src/main/java/com/ivy/wallet/io/persistence/dao/CategoryDao.kt @@ -4,38 +4,35 @@ import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query -import com.ivy.wallet.domain.data.entity.Category +import com.ivy.wallet.io.persistence.data.CategoryEntity import java.util.* @Dao interface CategoryDao { @Insert(onConflict = OnConflictStrategy.REPLACE) - fun save(value: Category) + suspend fun save(value: CategoryEntity) @Insert(onConflict = OnConflictStrategy.REPLACE) - fun save(value: List) + suspend fun save(value: List) @Query("SELECT * FROM categories WHERE isDeleted = 0 ORDER BY orderNum ASC") - fun findAll(): List + suspend fun findAll(): List @Query("SELECT * FROM categories WHERE isSynced = :synced AND isDeleted = :deleted") - fun findByIsSyncedAndIsDeleted(synced: Boolean, deleted: Boolean = false): List + suspend fun findByIsSyncedAndIsDeleted(synced: Boolean, deleted: Boolean = false): List @Query("SELECT * FROM categories WHERE id = :id") - fun findById(id: UUID): Category? - - @Query("SELECT * FROM categories WHERE seCategoryName = :seCategoryName") - fun findBySeCategoryName(seCategoryName: String): Category? + suspend fun findById(id: UUID): CategoryEntity? @Query("DELETE FROM categories WHERE id = :id") - fun deleteById(id: UUID) + suspend fun deleteById(id: UUID) @Query("UPDATE categories SET isDeleted = 1, isSynced = 0 WHERE id = :id") - fun flagDeleted(id: UUID) + suspend fun flagDeleted(id: UUID) @Query("DELETE FROM categories") - fun deleteAll() + suspend fun deleteAll() @Query("SELECT MAX(orderNum) FROM categories") - fun findMaxOrderNum(): Double + suspend fun findMaxOrderNum(): Double? } \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/persistence/dao/ExchangeRateDao.kt b/app/src/main/java/com/ivy/wallet/io/persistence/dao/ExchangeRateDao.kt index 3d3c921f11..fac1ea743c 100644 --- a/app/src/main/java/com/ivy/wallet/io/persistence/dao/ExchangeRateDao.kt +++ b/app/src/main/java/com/ivy/wallet/io/persistence/dao/ExchangeRateDao.kt @@ -4,20 +4,19 @@ import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query -import com.ivy.wallet.domain.data.entity.ExchangeRate +import com.ivy.wallet.io.persistence.data.ExchangeRateEntity @Dao interface ExchangeRateDao { @Insert(onConflict = OnConflictStrategy.REPLACE) - fun save(value: ExchangeRate) + suspend fun save(value: ExchangeRateEntity) @Query("SELECT * FROM exchange_rates WHERE baseCurrency = :baseCurrency AND currency = :currency") - fun findByBaseCurrencyAndCurrency( + suspend fun findByBaseCurrencyAndCurrency( baseCurrency: String, currency: String - ): ExchangeRate? + ): ExchangeRateEntity? @Query("DELETE FROM exchange_rates") - fun deleteALl() { - } + suspend fun deleteALl() } \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/persistence/dao/LoanDao.kt b/app/src/main/java/com/ivy/wallet/io/persistence/dao/LoanDao.kt index 83c12e6d8e..e4cfd76991 100644 --- a/app/src/main/java/com/ivy/wallet/io/persistence/dao/LoanDao.kt +++ b/app/src/main/java/com/ivy/wallet/io/persistence/dao/LoanDao.kt @@ -4,35 +4,35 @@ import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query -import com.ivy.wallet.domain.data.entity.Loan +import com.ivy.wallet.io.persistence.data.LoanEntity import java.util.* @Dao interface LoanDao { @Insert(onConflict = OnConflictStrategy.REPLACE) - fun save(value: Loan) + suspend fun save(value: LoanEntity) @Insert(onConflict = OnConflictStrategy.REPLACE) - fun save(value: List) + suspend fun save(value: List) @Query("SELECT * FROM loans WHERE isDeleted = 0 ORDER BY orderNum ASC") - fun findAll(): List + suspend fun findAll(): List @Query("SELECT * FROM loans WHERE isSynced = :synced AND isDeleted = :deleted") - fun findByIsSyncedAndIsDeleted(synced: Boolean, deleted: Boolean = false): List + suspend fun findByIsSyncedAndIsDeleted(synced: Boolean, deleted: Boolean = false): List @Query("SELECT * FROM loans WHERE id = :id") - fun findById(id: UUID): Loan? + suspend fun findById(id: UUID): LoanEntity? @Query("DELETE FROM loans WHERE id = :id") - fun deleteById(id: UUID) + suspend fun deleteById(id: UUID) @Query("UPDATE loans SET isDeleted = 1, isSynced = 0 WHERE id = :id") - fun flagDeleted(id: UUID) + suspend fun flagDeleted(id: UUID) @Query("DELETE FROM loans") - fun deleteAll() + suspend fun deleteAll() @Query("SELECT MAX(orderNum) FROM loans") - fun findMaxOrderNum(): Double + suspend fun findMaxOrderNum(): Double? } \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/persistence/dao/LoanRecordDao.kt b/app/src/main/java/com/ivy/wallet/io/persistence/dao/LoanRecordDao.kt index 058ccfa0df..b0c01b911d 100644 --- a/app/src/main/java/com/ivy/wallet/io/persistence/dao/LoanRecordDao.kt +++ b/app/src/main/java/com/ivy/wallet/io/persistence/dao/LoanRecordDao.kt @@ -4,35 +4,38 @@ import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query -import com.ivy.wallet.domain.data.entity.LoanRecord +import com.ivy.wallet.io.persistence.data.LoanRecordEntity import java.util.* @Dao interface LoanRecordDao { @Insert(onConflict = OnConflictStrategy.REPLACE) - fun save(value: LoanRecord) + suspend fun save(value: LoanRecordEntity) @Insert(onConflict = OnConflictStrategy.REPLACE) - fun save(value: List) + suspend fun save(value: List) @Query("SELECT * FROM loan_records WHERE isDeleted = 0 ORDER BY dateTime DESC") - fun findAll(): List + suspend fun findAll(): List @Query("SELECT * FROM loan_records WHERE isSynced = :synced AND isDeleted = :deleted") - fun findByIsSyncedAndIsDeleted(synced: Boolean, deleted: Boolean = false): List + suspend fun findByIsSyncedAndIsDeleted( + synced: Boolean, + deleted: Boolean = false + ): List @Query("SELECT * FROM loan_records WHERE id = :id") - fun findById(id: UUID): LoanRecord? + suspend fun findById(id: UUID): LoanRecordEntity? @Query("SELECT * FROM loan_records WHERE loanId = :loanId AND isDeleted = 0 ORDER BY dateTime DESC") - fun findAllByLoanId(loanId: UUID): List + suspend fun findAllByLoanId(loanId: UUID): List @Query("DELETE FROM loan_records WHERE id = :id") - fun deleteById(id: UUID) + suspend fun deleteById(id: UUID) @Query("UPDATE loan_records SET isDeleted = 1, isSynced = 0 WHERE id = :id") - fun flagDeleted(id: UUID) + suspend fun flagDeleted(id: UUID) @Query("DELETE FROM loan_records") - fun deleteAll() + suspend fun deleteAll() } \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/persistence/dao/PlannedPaymentRuleDao.kt b/app/src/main/java/com/ivy/wallet/io/persistence/dao/PlannedPaymentRuleDao.kt index 458c4cd3fe..23d6492392 100644 --- a/app/src/main/java/com/ivy/wallet/io/persistence/dao/PlannedPaymentRuleDao.kt +++ b/app/src/main/java/com/ivy/wallet/io/persistence/dao/PlannedPaymentRuleDao.kt @@ -4,44 +4,44 @@ import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query -import com.ivy.wallet.domain.data.entity.PlannedPaymentRule +import com.ivy.wallet.io.persistence.data.PlannedPaymentRuleEntity import java.util.* @Dao interface PlannedPaymentRuleDao { @Insert(onConflict = OnConflictStrategy.REPLACE) - fun save(value: PlannedPaymentRule) + suspend fun save(value: PlannedPaymentRuleEntity) @Insert(onConflict = OnConflictStrategy.REPLACE) - fun save(value: List) + suspend fun save(value: List) @Query("SELECT * FROM planned_payment_rules WHERE isDeleted = 0 ORDER BY amount DESC, startDate ASC") - fun findAll(): List + suspend fun findAll(): List @Query("SELECT * FROM planned_payment_rules WHERE isSynced = :synced AND isDeleted = :deleted") - fun findByIsSyncedAndIsDeleted( + suspend fun findByIsSyncedAndIsDeleted( synced: Boolean, deleted: Boolean = false - ): List + ): List @Query("SELECT * FROM planned_payment_rules WHERE isDeleted = 0 AND oneTime = :oneTime ORDER BY amount DESC, startDate ASC") - fun findAllByOneTime(oneTime: Boolean): List + suspend fun findAllByOneTime(oneTime: Boolean): List @Query("SELECT * FROM planned_payment_rules WHERE id = :id AND isDeleted = 0") - fun findById(id: UUID): PlannedPaymentRule? + suspend fun findById(id: UUID): PlannedPaymentRuleEntity? @Query("UPDATE planned_payment_rules SET isDeleted = 1, isSynced = 0 WHERE id = :id") - fun flagDeleted(id: UUID) + suspend fun flagDeleted(id: UUID) @Query("UPDATE planned_payment_rules SET isDeleted = 1, isSynced = 0 WHERE accountId = :accountId") - fun flagDeletedByAccountId(accountId: UUID) + suspend fun flagDeletedByAccountId(accountId: UUID) @Query("DELETE FROM planned_payment_rules WHERE id = :id") - fun deleteById(id: UUID) + suspend fun deleteById(id: UUID) @Query("DELETE FROM planned_payment_rules") - fun deleteAll() + suspend fun deleteAll() @Query("SELECT COUNT(*) FROM planned_payment_rules WHERE isDeleted = 0 ") - fun countPlannedPayments(): Long + suspend fun countPlannedPayments(): Long } \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/persistence/dao/SettingsDao.kt b/app/src/main/java/com/ivy/wallet/io/persistence/dao/SettingsDao.kt index b921cc578d..81b24813c2 100644 --- a/app/src/main/java/com/ivy/wallet/io/persistence/dao/SettingsDao.kt +++ b/app/src/main/java/com/ivy/wallet/io/persistence/dao/SettingsDao.kt @@ -4,29 +4,29 @@ import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query -import com.ivy.wallet.domain.data.entity.Settings +import com.ivy.wallet.io.persistence.data.SettingsEntity import java.util.* @Dao interface SettingsDao { @Insert(onConflict = OnConflictStrategy.REPLACE) - fun save(value: Settings) + suspend fun save(value: SettingsEntity) @Insert(onConflict = OnConflictStrategy.REPLACE) - fun save(value: List) + suspend fun save(value: List) @Query("SELECT * FROM settings LIMIT 1") - fun findFirst(): Settings + suspend fun findFirst(): SettingsEntity @Query("SELECT * FROM settings") - fun findAll(): List + suspend fun findAll(): List @Query("SELECT * FROM settings WHERE id = :id") - fun findById(id: UUID): Settings? + suspend fun findById(id: UUID): SettingsEntity? @Query("DELETE FROM settings WHERE id = :id") - fun deleteById(id: UUID) + suspend fun deleteById(id: UUID) @Query("DELETE FROM settings") - fun deleteAll() + suspend fun deleteAll() } \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/persistence/dao/TransactionDao.kt b/app/src/main/java/com/ivy/wallet/io/persistence/dao/TransactionDao.kt index 92cbc96bc4..1c4f3a3ffc 100644 --- a/app/src/main/java/com/ivy/wallet/io/persistence/dao/TransactionDao.kt +++ b/app/src/main/java/com/ivy/wallet/io/persistence/dao/TransactionDao.kt @@ -5,211 +5,214 @@ import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.data.entity.Transaction +import com.ivy.wallet.io.persistence.data.TransactionEntity import java.time.LocalDateTime import java.util.* @Dao interface TransactionDao { @Insert(onConflict = OnConflictStrategy.REPLACE) - fun save(value: Transaction) + suspend fun save(value: TransactionEntity) @Insert(onConflict = OnConflictStrategy.REPLACE) - fun save(value: List) + suspend fun save(value: List) @Query("SELECT * FROM transactions WHERE isDeleted = 0 ORDER BY dateTime DESC, dueDate ASC") - fun findAll(): List + suspend fun findAll(): List @Query("SELECT * FROM transactions WHERE isDeleted = 0 LIMIT 1") - fun findAll_LIMIT_1(): List + suspend fun findAll_LIMIT_1(): List @Query("SELECT * FROM transactions WHERE isDeleted = 0 AND type = :type ORDER BY dateTime DESC") - fun findAllByType(type: TransactionType): List + suspend fun findAllByType(type: TransactionType): List @Query("SELECT * FROM transactions WHERE isDeleted = 0 AND type = :type and accountId = :accountId ORDER BY dateTime DESC") - fun findAllByTypeAndAccount(type: TransactionType, accountId: UUID): List + suspend fun findAllByTypeAndAccount(type: TransactionType, accountId: UUID): List @Query("SELECT * FROM transactions WHERE isDeleted = 0 AND type = :type and accountId = :accountId and dateTime >= :startDate AND dateTime <= :endDate ORDER BY dateTime DESC") - fun findAllByTypeAndAccountBetween( + suspend fun findAllByTypeAndAccountBetween( type: TransactionType, accountId: UUID, startDate: LocalDateTime, endDate: LocalDateTime - ): List + ): List @Query("SELECT * FROM transactions WHERE isDeleted = 0 AND type = :type and toAccountId = :toAccountId ORDER BY dateTime DESC") - fun findAllTransfersToAccount( + suspend fun findAllTransfersToAccount( toAccountId: UUID, type: TransactionType = TransactionType.TRANSFER - ): List + ): List @Query("SELECT * FROM transactions WHERE isDeleted = 0 AND type = :type and toAccountId = :toAccountId and dateTime >= :startDate AND dateTime <= :endDate ORDER BY dateTime DESC") - fun findAllTransfersToAccountBetween( + suspend fun findAllTransfersToAccountBetween( toAccountId: UUID, startDate: LocalDateTime, endDate: LocalDateTime, type: TransactionType = TransactionType.TRANSFER - ): List + ): List @Query("SELECT * FROM transactions WHERE isDeleted = 0 AND dateTime >= :startDate AND dateTime <= :endDate ORDER BY dateTime DESC") - fun findAllBetween(startDate: LocalDateTime, endDate: LocalDateTime): List + suspend fun findAllBetween(startDate: LocalDateTime, endDate: LocalDateTime): List @Query("SELECT * FROM transactions WHERE isDeleted = 0 AND accountId = :accountId AND dateTime >= :startDate AND dateTime <= :endDate ORDER BY dateTime DESC") - fun findAllByAccountAndBetween( + suspend fun findAllByAccountAndBetween( accountId: UUID, startDate: LocalDateTime, endDate: LocalDateTime - ): List + ): List - @Query("SELECT * FROM transactions WHERE isDeleted = 0 AND (categoryId = :categoryId OR seAutoCategoryId = :categoryId) AND dateTime >= :startDate AND dateTime <= :endDate ORDER BY dateTime DESC") - fun findAllByCategoryAndBetween( + @Query("SELECT * FROM transactions WHERE isDeleted = 0 AND (categoryId = :categoryId) AND dateTime >= :startDate AND dateTime <= :endDate ORDER BY dateTime DESC") + suspend fun findAllByCategoryAndBetween( categoryId: UUID, startDate: LocalDateTime, endDate: LocalDateTime - ): List + ): List - @Query("SELECT * FROM transactions WHERE isDeleted = 0 AND (categoryId IS NULL AND seAutoCategoryId IS NULL) AND dateTime >= :startDate AND dateTime <= :endDate ORDER BY dateTime DESC") - fun findAllUnspecifiedAndBetween( + @Query("SELECT * FROM transactions WHERE isDeleted = 0 AND (categoryId IS NULL) AND dateTime >= :startDate AND dateTime <= :endDate ORDER BY dateTime DESC") + suspend fun findAllUnspecifiedAndBetween( startDate: LocalDateTime, endDate: LocalDateTime - ): List + ): List - @Query("SELECT * FROM transactions WHERE isDeleted = 0 AND (categoryId = :categoryId OR seAutoCategoryId = :categoryId) AND type = :type AND dateTime >= :startDate AND dateTime <= :endDate ORDER BY dateTime DESC") - fun findAllByCategoryAndTypeAndBetween( + @Query("SELECT * FROM transactions WHERE isDeleted = 0 AND (categoryId = :categoryId) AND type = :type AND dateTime >= :startDate AND dateTime <= :endDate ORDER BY dateTime DESC") + suspend fun findAllByCategoryAndTypeAndBetween( categoryId: UUID, type: TransactionType, startDate: LocalDateTime, endDate: LocalDateTime - ): List + ): List - @Query("SELECT * FROM transactions WHERE isDeleted = 0 AND (categoryId IS NULL AND seAutoCategoryId IS NULL) AND type = :type AND dateTime >= :startDate AND dateTime <= :endDate ORDER BY dateTime DESC") - fun findAllUnspecifiedAndTypeAndBetween( + @Query("SELECT * FROM transactions WHERE isDeleted = 0 AND (categoryId IS NULL) AND type = :type AND dateTime >= :startDate AND dateTime <= :endDate ORDER BY dateTime DESC") + suspend fun findAllUnspecifiedAndTypeAndBetween( type: TransactionType, startDate: LocalDateTime, endDate: LocalDateTime - ): List + ): List @Query("SELECT * FROM transactions WHERE isDeleted = 0 AND toAccountId = :toAccountId AND dateTime >= :startDate AND dateTime <= :endDate ORDER BY dateTime DESC") - fun findAllToAccountAndBetween( + suspend fun findAllToAccountAndBetween( toAccountId: UUID, startDate: LocalDateTime, endDate: LocalDateTime - ): List + ): List @Query("SELECT * FROM transactions WHERE isDeleted = 0 AND dueDate >= :startDate AND dueDate <= :endDate ORDER BY dueDate ASC") - fun findAllDueToBetween(startDate: LocalDateTime, endDate: LocalDateTime): List + suspend fun findAllDueToBetween( + startDate: LocalDateTime, + endDate: LocalDateTime + ): List - @Query("SELECT * FROM transactions WHERE isDeleted = 0 AND dueDate >= :startDate AND dueDate <= :endDate AND (categoryId = :categoryId OR seAutoCategoryId = :categoryId) ORDER BY dateTime DESC, dueDate ASC") - fun findAllDueToBetweenByCategory( + @Query("SELECT * FROM transactions WHERE isDeleted = 0 AND dueDate >= :startDate AND dueDate <= :endDate AND (categoryId = :categoryId) ORDER BY dateTime DESC, dueDate ASC") + suspend fun findAllDueToBetweenByCategory( startDate: LocalDateTime, endDate: LocalDateTime, categoryId: UUID - ): List + ): List - @Query("SELECT * FROM transactions WHERE isDeleted = 0 AND dueDate >= :startDate AND dueDate <= :endDate AND (categoryId IS NULL AND seAutoCategoryId IS NULL) ORDER BY dateTime DESC, dueDate ASC") - fun findAllDueToBetweenByCategoryUnspecified( + @Query("SELECT * FROM transactions WHERE isDeleted = 0 AND dueDate >= :startDate AND dueDate <= :endDate AND (categoryId IS NULL) ORDER BY dateTime DESC, dueDate ASC") + suspend fun findAllDueToBetweenByCategoryUnspecified( startDate: LocalDateTime, endDate: LocalDateTime, - ): List + ): List @Query("SELECT * FROM transactions WHERE isDeleted = 0 AND dueDate >= :startDate AND dueDate <= :endDate AND accountId = :accountId ORDER BY dateTime DESC, dueDate ASC") - fun findAllDueToBetweenByAccount( + suspend fun findAllDueToBetweenByAccount( startDate: LocalDateTime, endDate: LocalDateTime, accountId: UUID - ): List + ): List @Query("SELECT * FROM transactions WHERE isDeleted = 0 AND recurringRuleId = :recurringRuleId ORDER BY dateTime DESC") - fun findAllByRecurringRuleId(recurringRuleId: UUID): List + suspend fun findAllByRecurringRuleId(recurringRuleId: UUID): List @Query("SELECT * FROM transactions WHERE isDeleted = 0 AND dateTime >= :startDate AND dateTime <= :endDate AND type = :type ORDER BY dateTime DESC") - fun findAllBetweenAndType( + suspend fun findAllBetweenAndType( startDate: LocalDateTime, endDate: LocalDateTime, type: TransactionType - ): List + ): List @Query("SELECT * FROM transactions WHERE isDeleted = 0 AND dateTime >= :startDate AND dateTime <= :endDate AND recurringRuleId = :recurringRuleId ORDER BY dateTime DESC") - fun findAllBetweenAndRecurringRuleId( + suspend fun findAllBetweenAndRecurringRuleId( startDate: LocalDateTime, endDate: LocalDateTime, recurringRuleId: UUID - ): List + ): List @Query("SELECT * FROM transactions WHERE id = :id") - fun findById(id: UUID): Transaction? - - @Query("SELECT * FROM transactions WHERE seTransactionId = :seTransactionId") - fun findBySeTransactionId(seTransactionId: String): Transaction? + suspend fun findById(id: UUID): TransactionEntity? @Query("SELECT * FROM transactions WHERE isSynced = :synced AND isDeleted = :deleted") - fun findByIsSyncedAndIsDeleted(synced: Boolean, deleted: Boolean = false): List + suspend fun findByIsSyncedAndIsDeleted( + synced: Boolean, + deleted: Boolean = false + ): List @Query("UPDATE transactions SET isDeleted = 1, isSynced = 0 WHERE id = :id") - fun flagDeleted(id: UUID) + suspend fun flagDeleted(id: UUID) @Query("UPDATE transactions SET isDeleted = 1, isSynced = 0 WHERE recurringRuleId = :recurringRuleId AND dateTime IS NULL") - fun flagDeletedByRecurringRuleIdAndNoDateTime(recurringRuleId: UUID) + suspend fun flagDeletedByRecurringRuleIdAndNoDateTime(recurringRuleId: UUID) @Query("UPDATE transactions SET isDeleted = 1, isSynced = 0 WHERE accountId = :accountId") - fun flagDeletedByAccountId(accountId: UUID) + suspend fun flagDeletedByAccountId(accountId: UUID) @Query("DELETE FROM transactions WHERE id = :id") - fun deleteById(id: UUID) + suspend fun deleteById(id: UUID) @Query("DELETE FROM transactions WHERE accountId = :accountId") - fun deleteAllByAccountId(accountId: UUID) + suspend fun deleteAllByAccountId(accountId: UUID) @Query("DELETE FROM transactions") - fun deleteAll() + suspend fun deleteAll() @Query("SELECT COUNT(*) FROM transactions WHERE isDeleted = 0 AND dateTime IS NOT null") - fun countHappenedTransactions(): Long + suspend fun countHappenedTransactions(): Long //Smart Title Suggestions @Query("SELECT * FROM transactions WHERE title LIKE :pattern AND isDeleted = 0") - fun findAllByTitleMatchingPattern(pattern: String): List + suspend fun findAllByTitleMatchingPattern(pattern: String): List @Query("SELECT COUNT(*) FROM transactions WHERE title LIKE :pattern AND isDeleted = 0") - fun countByTitleMatchingPattern( + suspend fun countByTitleMatchingPattern( pattern: String, ): Long - @Query("SELECT * FROM transactions WHERE isDeleted = 0 AND (categoryId = :categoryId OR seAutoCategoryId = :categoryId) ORDER BY dateTime DESC") - fun findAllByCategory( + @Query("SELECT * FROM transactions WHERE isDeleted = 0 AND (categoryId = :categoryId) ORDER BY dateTime DESC") + suspend fun findAllByCategory( categoryId: UUID, - ): List + ): List @Query("SELECT COUNT(*) FROM transactions WHERE title LIKE :pattern AND categoryId = :categoryId AND isDeleted = 0") - fun countByTitleMatchingPatternAndCategoryId( + suspend fun countByTitleMatchingPatternAndCategoryId( pattern: String, categoryId: UUID ): Long @Query("SELECT * FROM transactions WHERE isDeleted = 0 AND accountId = :accountId ORDER BY dateTime DESC") - fun findAllByAccount( + suspend fun findAllByAccount( accountId: UUID - ): List + ): List @Query("SELECT COUNT(*) FROM transactions WHERE title LIKE :pattern AND accountId = :accountId AND isDeleted = 0") - fun countByTitleMatchingPatternAndAccountId( + suspend fun countByTitleMatchingPatternAndAccountId( pattern: String, accountId: UUID ): Long @Query("SELECT * FROM transactions WHERE isDeleted = 0 AND loanId = :loanId AND loanRecordId IS NULL") - fun findLoanTransaction( + suspend fun findLoanTransaction( loanId: UUID - ): Transaction? + ): TransactionEntity? @Query("SELECT * FROM transactions WHERE isDeleted = 0 AND loanRecordId = :loanRecordId") - fun findLoanRecordTransaction( + suspend fun findLoanRecordTransaction( loanRecordId: UUID - ): Transaction? + ): TransactionEntity? @Query("SELECT * FROM transactions WHERE isDeleted = 0 AND loanId = :loanId") - fun findAllByLoanId( + suspend fun findAllByLoanId( loanId: UUID - ): List + ): List } \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/persistence/dao/UserDao.kt b/app/src/main/java/com/ivy/wallet/io/persistence/dao/UserDao.kt index 74e1cf61af..081db84a66 100644 --- a/app/src/main/java/com/ivy/wallet/io/persistence/dao/UserDao.kt +++ b/app/src/main/java/com/ivy/wallet/io/persistence/dao/UserDao.kt @@ -4,17 +4,17 @@ import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query -import com.ivy.wallet.domain.data.entity.User +import com.ivy.wallet.io.persistence.data.UserEntity import java.util.* @Dao interface UserDao { @Insert(onConflict = OnConflictStrategy.REPLACE) - fun save(user: User) + suspend fun save(user: UserEntity) @Query("SELECT * FROM users WHERE id = :userId") - fun findById(userId: UUID): User? + suspend fun findById(userId: UUID): UserEntity? @Query("DELETE FROM users") - fun deleteAll() + suspend fun deleteAll() } \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/persistence/dao/WishlistItemDao.kt b/app/src/main/java/com/ivy/wallet/io/persistence/dao/WishlistItemDao.kt deleted file mode 100644 index 714375ddd2..0000000000 --- a/app/src/main/java/com/ivy/wallet/io/persistence/dao/WishlistItemDao.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.ivy.wallet.io.persistence.dao - -import androidx.room.Dao -import androidx.room.Insert -import androidx.room.OnConflictStrategy -import androidx.room.Query -import com.ivy.wallet.domain.data.entity.WishlistItem -import java.util.* - -@Dao -interface WishlistItemDao { - @Insert(onConflict = OnConflictStrategy.REPLACE) - fun save(value: WishlistItem) - - @Query("SELECT * FROM wishlist_items ORDER BY orderNum ASC") - fun findAll(): List - - @Query("SELECT * FROM wishlist_items WHERE id = :id") - fun findById(id: UUID): WishlistItem? - - @Query("DELETE FROM wishlist_items WHERE id = :id") - fun deleteById(id: UUID) - - @Query("DELETE FROM wishlist_items") - fun deleteAll() -} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/data/entity/Account.kt b/app/src/main/java/com/ivy/wallet/io/persistence/data/AccountEntity.kt similarity index 55% rename from app/src/main/java/com/ivy/wallet/domain/data/entity/Account.kt rename to app/src/main/java/com/ivy/wallet/io/persistence/data/AccountEntity.kt index 12abeb68c1..e4d4d8faf7 100644 --- a/app/src/main/java/com/ivy/wallet/domain/data/entity/Account.kt +++ b/app/src/main/java/com/ivy/wallet/io/persistence/data/AccountEntity.kt @@ -1,13 +1,14 @@ -package com.ivy.wallet.domain.data.entity +package com.ivy.wallet.io.persistence.data import androidx.compose.ui.graphics.toArgb import androidx.room.Entity import androidx.room.PrimaryKey +import com.ivy.wallet.domain.data.core.Account import com.ivy.wallet.ui.theme.Green import java.util.* @Entity(tableName = "accounts") -data class Account( +data class AccountEntity( val name: String, val currency: String? = null, val color: Int = Green.toArgb(), @@ -15,13 +16,21 @@ data class Account( val orderNum: Double = 0.0, val includeInBalance: Boolean = true, - //SaltEdge integration ------- - val seAccountId: String? = null, - //SaltEdge integration ------- - val isSynced: Boolean = false, val isDeleted: Boolean = false, @PrimaryKey val id: UUID = UUID.randomUUID() -) \ No newline at end of file +) { + fun toDomain(): Account = Account( + name = name, + currency = currency, + color = color, + icon = icon, + orderNum = orderNum, + includeInBalance = includeInBalance, + isSynced = isSynced, + isDeleted = isDeleted, + id = id + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/data/entity/Budget.kt b/app/src/main/java/com/ivy/wallet/io/persistence/data/BudgetEntity.kt similarity index 77% rename from app/src/main/java/com/ivy/wallet/domain/data/entity/Budget.kt rename to app/src/main/java/com/ivy/wallet/io/persistence/data/BudgetEntity.kt index 86693c0d1a..7446b1ea6f 100644 --- a/app/src/main/java/com/ivy/wallet/domain/data/entity/Budget.kt +++ b/app/src/main/java/com/ivy/wallet/io/persistence/data/BudgetEntity.kt @@ -1,11 +1,12 @@ -package com.ivy.wallet.domain.data.entity +package com.ivy.wallet.io.persistence.data import androidx.room.Entity import androidx.room.PrimaryKey +import com.ivy.wallet.domain.data.core.Budget import java.util.* @Entity(tableName = "budgets") -data class Budget( +data class BudgetEntity( val name: String, val amount: Double, @@ -19,6 +20,17 @@ data class Budget( @PrimaryKey val id: UUID = UUID.randomUUID() ) { + fun toDomain(): Budget = Budget( + name = name, + amount = amount, + categoryIdsSerialized = categoryIdsSerialized, + accountIdsSerialized = accountIdsSerialized, + isSynced = isSynced, + isDeleted = isDeleted, + orderId = orderId, + id = id + ) + companion object { fun serialize(ids: List): String { return ids.joinToString(separator = ",") diff --git a/app/src/main/java/com/ivy/wallet/domain/data/entity/Category.kt b/app/src/main/java/com/ivy/wallet/io/persistence/data/CategoryEntity.kt similarity index 56% rename from app/src/main/java/com/ivy/wallet/domain/data/entity/Category.kt rename to app/src/main/java/com/ivy/wallet/io/persistence/data/CategoryEntity.kt index 231722b953..b6ebf86c68 100644 --- a/app/src/main/java/com/ivy/wallet/domain/data/entity/Category.kt +++ b/app/src/main/java/com/ivy/wallet/io/persistence/data/CategoryEntity.kt @@ -1,25 +1,32 @@ -package com.ivy.wallet.domain.data.entity +package com.ivy.wallet.io.persistence.data import androidx.compose.ui.graphics.toArgb import androidx.room.Entity import androidx.room.PrimaryKey +import com.ivy.wallet.domain.data.core.Category import com.ivy.wallet.ui.theme.Ivy import java.util.* @Entity(tableName = "categories") -data class Category( +data class CategoryEntity( val name: String, val color: Int = Ivy.toArgb(), val icon: String? = null, val orderNum: Double = 0.0, - //SaltEdge integration ------- - val seCategoryName: String? = null, - //SaltEdge integration ------- - val isSynced: Boolean = false, val isDeleted: Boolean = false, @PrimaryKey val id: UUID = UUID.randomUUID() -) \ No newline at end of file +) { + fun toDomain(): Category = Category( + name = name, + color = color, + icon = icon, + orderNum = orderNum, + isSynced = isSynced, + isDeleted = isDeleted, + id = id + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/persistence/data/ExchangeRateEntity.kt b/app/src/main/java/com/ivy/wallet/io/persistence/data/ExchangeRateEntity.kt new file mode 100644 index 0000000000..873dc767bf --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/io/persistence/data/ExchangeRateEntity.kt @@ -0,0 +1,17 @@ +package com.ivy.wallet.io.persistence.data + +import androidx.room.Entity +import com.ivy.wallet.domain.data.core.ExchangeRate + +@Entity(tableName = "exchange_rates", primaryKeys = ["baseCurrency", "currency"]) +data class ExchangeRateEntity( + val baseCurrency: String, + val currency: String, + val rate: Double, +) { + fun toDomain(): ExchangeRate = ExchangeRate( + baseCurrency = baseCurrency, + currency = currency, + rate = rate + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/data/entity/Loan.kt b/app/src/main/java/com/ivy/wallet/io/persistence/data/LoanEntity.kt similarity index 59% rename from app/src/main/java/com/ivy/wallet/domain/data/entity/Loan.kt rename to app/src/main/java/com/ivy/wallet/io/persistence/data/LoanEntity.kt index 849c6d6767..fe5a447cfe 100644 --- a/app/src/main/java/com/ivy/wallet/domain/data/entity/Loan.kt +++ b/app/src/main/java/com/ivy/wallet/io/persistence/data/LoanEntity.kt @@ -1,12 +1,13 @@ -package com.ivy.wallet.domain.data.entity +package com.ivy.wallet.io.persistence.data import androidx.room.Entity import androidx.room.PrimaryKey import com.ivy.wallet.domain.data.LoanType +import com.ivy.wallet.domain.data.core.Loan import java.util.* @Entity(tableName = "loans") -data class Loan( +data class LoanEntity( val name: String, val amount: Double, val type: LoanType, @@ -21,6 +22,19 @@ data class Loan( @PrimaryKey val id: UUID = UUID.randomUUID() ) { + fun toDomain(): Loan = Loan( + name = name, + amount = amount, + type = type, + color = color, + icon = icon, + orderNum = orderNum, + accountId = accountId, + isSynced = isSynced, + isDeleted = isDeleted, + id = id + ) + fun humanReadableType(): String { return if (type == LoanType.BORROW) "BORROWED" else "LENT" } diff --git a/app/src/main/java/com/ivy/wallet/io/persistence/data/LoanRecordEntity.kt b/app/src/main/java/com/ivy/wallet/io/persistence/data/LoanRecordEntity.kt new file mode 100644 index 0000000000..af6487f78b --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/io/persistence/data/LoanRecordEntity.kt @@ -0,0 +1,38 @@ +package com.ivy.wallet.io.persistence.data + +import androidx.room.Entity +import androidx.room.PrimaryKey +import com.ivy.wallet.domain.data.core.LoanRecord +import java.time.LocalDateTime +import java.util.* + +@Entity(tableName = "loan_records") +data class LoanRecordEntity( + val loanId: UUID, + val amount: Double, + val note: String? = null, + val dateTime: LocalDateTime, + val interest: Boolean = false, + val accountId: UUID? = null, + //This is used store the converted amount for currencies which are different from the loan account currency + val convertedAmount: Double? = null, + + val isSynced: Boolean = false, + val isDeleted: Boolean = false, + + @PrimaryKey + val id: UUID = UUID.randomUUID() +) { + fun toDomain(): LoanRecord = LoanRecord( + loanId = loanId, + amount = amount, + note = note, + dateTime = dateTime, + interest = interest, + accountId = accountId, + convertedAmount = convertedAmount, + isSynced = isSynced, + isDeleted = isDeleted, + id = id + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/data/entity/PlannedPaymentRule.kt b/app/src/main/java/com/ivy/wallet/io/persistence/data/PlannedPaymentRuleEntity.kt similarity index 54% rename from app/src/main/java/com/ivy/wallet/domain/data/entity/PlannedPaymentRule.kt rename to app/src/main/java/com/ivy/wallet/io/persistence/data/PlannedPaymentRuleEntity.kt index bdac3aa3aa..e38fa3c151 100644 --- a/app/src/main/java/com/ivy/wallet/domain/data/entity/PlannedPaymentRule.kt +++ b/app/src/main/java/com/ivy/wallet/io/persistence/data/PlannedPaymentRuleEntity.kt @@ -1,14 +1,15 @@ -package com.ivy.wallet.domain.data.entity +package com.ivy.wallet.io.persistence.data import androidx.room.Entity import androidx.room.PrimaryKey import com.ivy.wallet.domain.data.IntervalType import com.ivy.wallet.domain.data.TransactionType +import com.ivy.wallet.domain.data.core.PlannedPaymentRule import java.time.LocalDateTime import java.util.* @Entity(tableName = "planned_payment_rules") -data class PlannedPaymentRule( +data class PlannedPaymentRuleEntity( val startDate: LocalDateTime?, val intervalN: Int?, val intervalType: IntervalType?, @@ -26,4 +27,20 @@ data class PlannedPaymentRule( @PrimaryKey val id: UUID = UUID.randomUUID() -) \ No newline at end of file +) { + fun toDomain(): PlannedPaymentRule = PlannedPaymentRule( + startDate = startDate, + intervalN = intervalN, + intervalType = intervalType, + oneTime = oneTime, + type = type, + accountId = accountId, + amount = amount, + categoryId = categoryId, + title = title, + description = description, + isSynced = isSynced, + isDeleted = isDeleted, + id = id + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/data/entity/Settings.kt b/app/src/main/java/com/ivy/wallet/io/persistence/data/SettingsEntity.kt similarity index 54% rename from app/src/main/java/com/ivy/wallet/domain/data/entity/Settings.kt rename to app/src/main/java/com/ivy/wallet/io/persistence/data/SettingsEntity.kt index f352a3ee5f..f153edabde 100644 --- a/app/src/main/java/com/ivy/wallet/domain/data/entity/Settings.kt +++ b/app/src/main/java/com/ivy/wallet/io/persistence/data/SettingsEntity.kt @@ -1,12 +1,13 @@ -package com.ivy.wallet.domain.data.entity +package com.ivy.wallet.io.persistence.data import androidx.room.Entity import androidx.room.PrimaryKey import com.ivy.design.l0_system.Theme +import com.ivy.wallet.domain.data.core.Settings import java.util.* @Entity(tableName = "settings") -data class Settings( +data class SettingsEntity( val theme: Theme, val currency: String, val bufferAmount: Double, @@ -17,4 +18,12 @@ data class Settings( @PrimaryKey val id: UUID = UUID.randomUUID() -) \ No newline at end of file +) { + fun toDomain(): Settings = Settings( + theme = theme, + baseCurrency = currency, + bufferAmount = bufferAmount.toBigDecimal(), + name = name, + id = id + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/data/entity/Transaction.kt b/app/src/main/java/com/ivy/wallet/io/persistence/data/TransactionEntity.kt similarity index 61% rename from app/src/main/java/com/ivy/wallet/domain/data/entity/Transaction.kt rename to app/src/main/java/com/ivy/wallet/io/persistence/data/TransactionEntity.kt index 8d4107ffa4..749a803622 100644 --- a/app/src/main/java/com/ivy/wallet/domain/data/entity/Transaction.kt +++ b/app/src/main/java/com/ivy/wallet/io/persistence/data/TransactionEntity.kt @@ -1,14 +1,14 @@ -package com.ivy.wallet.domain.data.entity +package com.ivy.wallet.io.persistence.data import androidx.room.Entity import androidx.room.PrimaryKey -import com.ivy.wallet.domain.data.TransactionHistoryItem import com.ivy.wallet.domain.data.TransactionType +import com.ivy.wallet.domain.data.core.Transaction import java.time.LocalDateTime import java.util.* @Entity(tableName = "transactions") -data class Transaction( +data class TransactionEntity( val accountId: UUID, val type: TransactionType, val amount: Double, @@ -28,25 +28,33 @@ data class Transaction( val loanId: UUID? = null, //This refers to the loan record id that is linked with a transaction - val loanRecordId:UUID? = null, - - //SaltEdge integration ------- - val seTransactionId: String? = null, - val seAutoCategoryId: UUID? = null, - //SaltEdge integration ------- + val loanRecordId: UUID? = null, val isSynced: Boolean = false, val isDeleted: Boolean = false, @PrimaryKey val id: UUID = UUID.randomUUID() -) : TransactionHistoryItem { - - fun smartCategoryId(): UUID? { - return categoryId ?: seAutoCategoryId - } +) { + fun toDomain(): Transaction = Transaction( + accountId = accountId, + type = type, + amount = amount.toBigDecimal(), + toAccountId = toAccountId, + toAmount = toAmount?.toBigDecimal() ?: amount.toBigDecimal(), + title = title, + description = description, + dateTime = dateTime, + categoryId = categoryId, + dueDate = dueDate, + recurringRuleId = recurringRuleId, + attachmentUrl = attachmentUrl, + loanId = loanId, + loanRecordId = loanRecordId, + id = id + ) - fun isIdenticalWith(transaction: Transaction?): Boolean { + fun isIdenticalWith(transaction: TransactionEntity?): Boolean { if (transaction == null) return false //Set isSynced && isDeleted to false so they aren't accounted in the equals check diff --git a/app/src/main/java/com/ivy/wallet/domain/data/entity/User.kt b/app/src/main/java/com/ivy/wallet/io/persistence/data/UserEntity.kt similarity index 63% rename from app/src/main/java/com/ivy/wallet/domain/data/entity/User.kt rename to app/src/main/java/com/ivy/wallet/io/persistence/data/UserEntity.kt index e6712cf52a..55f48c6ab9 100644 --- a/app/src/main/java/com/ivy/wallet/domain/data/entity/User.kt +++ b/app/src/main/java/com/ivy/wallet/io/persistence/data/UserEntity.kt @@ -1,39 +1,43 @@ -package com.ivy.wallet.domain.data.entity +package com.ivy.wallet.io.persistence.data import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey -import com.google.gson.annotations.SerializedName import com.ivy.wallet.domain.data.AuthProviderType +import com.ivy.wallet.domain.data.core.User import java.util.* @Entity(tableName = "users") -data class User( - @SerializedName("email") +data class UserEntity( @ColumnInfo(name = "email") val email: String, - @SerializedName("authProviderType") + @ColumnInfo(name = "authProviderType") val authProviderType: AuthProviderType, - @SerializedName("firstName") @ColumnInfo(name = "firstName") var firstName: String, - @SerializedName("lastName") @ColumnInfo(name = "lastName") val lastName: String?, - @SerializedName("profilePictureUrl") @ColumnInfo(name = "profilePicture") val profilePicture: String?, - @SerializedName("color") @ColumnInfo(name = "color") val color: Int, - @SerializedName("testUser") @ColumnInfo(name = "testUser") val testUser: Boolean = false, - @SerializedName("id") @PrimaryKey @ColumnInfo(name = "id") var id: UUID ) { + fun toDomain(): User = User( + email = email, + authProviderType = authProviderType, + firstName = firstName, + lastName = lastName, + profilePicture = profilePicture, + color = color, + testUser = testUser, + id = id + ) + fun names(): String = firstName + if (lastName != null) " $lastName" else "" } \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/io/persistence/migration/Migration120to121_DropWishlistItem.kt b/app/src/main/java/com/ivy/wallet/io/persistence/migration/Migration120to121_DropWishlistItem.kt new file mode 100644 index 0000000000..e982ef60ac --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/io/persistence/migration/Migration120to121_DropWishlistItem.kt @@ -0,0 +1,10 @@ +package com.ivy.wallet.io.persistence.migration + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration120to121_DropWishlistItem : Migration(120, 121) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE wishlist_items") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/ui/IvyComposeApp.kt b/app/src/main/java/com/ivy/wallet/ui/IvyComposeApp.kt index 179a63a434..0cb46087ac 100644 --- a/app/src/main/java/com/ivy/wallet/ui/IvyComposeApp.kt +++ b/app/src/main/java/com/ivy/wallet/ui/IvyComposeApp.kt @@ -1,5 +1,6 @@ package com.ivy.wallet.ui +import android.view.View import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxScope @@ -8,6 +9,8 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalView import com.ivy.design.IvyContext import com.ivy.design.api.IvyDesign import com.ivy.design.api.NavigationRoot @@ -17,12 +20,17 @@ import com.ivy.design.l0_system.Theme import com.ivy.design.l0_system.UI import com.ivy.design.navigation.Navigation import com.ivy.design.utils.IvyPreview +import com.ivy.wallet.IvyAndroidApp @Composable -fun ivyWalletCtx(): IvyWalletCtx { - return ivyContext() as IvyWalletCtx -} +fun ivyWalletCtx(): IvyWalletCtx = ivyContext() as IvyWalletCtx + +@Composable +fun rootView(): View = LocalView.current + +@Composable +fun rootActivity(): RootActivity = LocalContext.current as RootActivity fun appDesign(context: IvyWalletCtx): IvyDesign = object : IvyWalletDesign() { override fun context(): IvyContext = context @@ -52,6 +60,7 @@ fun IvyWalletPreview( theme: Theme = Theme.LIGHT, Content: @Composable BoxWithConstraintsScope.() -> Unit ) { + IvyAndroidApp.appContext = rootView().context IvyPreview( theme = theme, design = appDesign(IvyWalletCtx()), diff --git a/app/src/main/java/com/ivy/wallet/ui/RootActivity.kt b/app/src/main/java/com/ivy/wallet/ui/RootActivity.kt index 71134d91ab..d8eed6d4aa 100644 --- a/app/src/main/java/com/ivy/wallet/ui/RootActivity.kt +++ b/app/src/main/java/com/ivy/wallet/ui/RootActivity.kt @@ -28,7 +28,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.ui.platform.LocalContext import androidx.core.content.ContextCompat import androidx.core.view.WindowCompat import androidx.lifecycle.viewmodel.compose.viewModel @@ -45,12 +44,12 @@ import com.ivy.design.navigation.Navigation import com.ivy.design.navigation.Screen import com.ivy.wallet.BuildConfig import com.ivy.wallet.Constants +import com.ivy.wallet.R import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.logic.CustomerJourneyLogic +import com.ivy.wallet.domain.deprecated.logic.CustomerJourneyLogic import com.ivy.wallet.ui.analytics.AnalyticsReport import com.ivy.wallet.ui.applocked.AppLockedScreen import com.ivy.wallet.ui.balance.BalanceScreen -import com.ivy.wallet.ui.bankintegrations.ConnectBankScreen import com.ivy.wallet.ui.budget.BudgetScreen import com.ivy.wallet.ui.category.CategoriesScreen import com.ivy.wallet.ui.charts.ChartsScreen @@ -203,7 +202,6 @@ class RootActivity : AppCompatActivity() { is Charts -> ChartsScreen(screen = screen) is AnalyticsReport -> AnalyticsReport(screen = screen) is Import -> ImportCSVScreen(screen = screen) - is ConnectBank -> ConnectBankScreen(screen = screen) is Report -> ReportScreen(screen = screen) is BudgetScreen -> BudgetScreen(screen = screen) is Loans -> LoansScreen(screen = screen) @@ -390,10 +388,10 @@ class RootActivity : AppCompatActivity() { val promptInfo = BiometricPrompt.PromptInfo.Builder() .setTitle( - "Authentication required" + getString(R.string.authentication_required) ) .setSubtitle( - "Prove that you have access to this device to unlock the app." + getString(R.string.authentication_required_description) ) .setAllowedAuthenticators( BiometricManager.Authenticators.BIOMETRIC_WEAK or @@ -532,9 +530,4 @@ class RootActivity : AppCompatActivity() { val addTransactionWidget = ComponentName(this, widget) appWidgetManager.requestPinAppWidget(addTransactionWidget, null, null) } -} - -@Composable -fun rootActivity(): RootActivity { - return LocalContext.current as RootActivity } \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/ui/RootViewModel.kt b/app/src/main/java/com/ivy/wallet/ui/RootViewModel.kt index 7967fd8e0a..a55ef7c9a5 100644 --- a/app/src/main/java/com/ivy/wallet/ui/RootViewModel.kt +++ b/app/src/main/java/com/ivy/wallet/ui/RootViewModel.kt @@ -8,14 +8,16 @@ import androidx.lifecycle.viewModelScope import com.ivy.design.l0_system.Theme import com.ivy.design.navigation.Navigation import com.ivy.wallet.Constants +import com.ivy.wallet.R import com.ivy.wallet.android.billing.IvyBilling import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.logic.PaywallLogic -import com.ivy.wallet.domain.logic.notification.TransactionReminderLogic +import com.ivy.wallet.domain.deprecated.logic.PaywallLogic +import com.ivy.wallet.domain.deprecated.logic.notification.TransactionReminderLogic import com.ivy.wallet.io.network.IvyAnalytics import com.ivy.wallet.io.network.IvySession import com.ivy.wallet.io.persistence.SharedPrefs import com.ivy.wallet.io.persistence.dao.SettingsDao +import com.ivy.wallet.stringRes import com.ivy.wallet.utils.TestIdlingResource import com.ivy.wallet.utils.ioThread import com.ivy.wallet.utils.readOnly @@ -116,13 +118,13 @@ class RootViewModel @Inject constructor( ): BiometricPrompt.AuthenticationCallback { return object : BiometricPrompt.AuthenticationCallback() { override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { - Timber.d("Authentication succeeded!") + Timber.d(stringRes(R.string.authentication_succeeded)) unlockApp() onAuthSuccess() } override fun onAuthenticationFailed() { - Timber.d("Authentication failed.") + Timber.d(stringRes(R.string.authentication_failed)) } override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { diff --git a/app/src/main/java/com/ivy/wallet/ui/Screens.kt b/app/src/main/java/com/ivy/wallet/ui/Screens.kt index 2aa1f881f1..d5f5be4a6f 100644 --- a/app/src/main/java/com/ivy/wallet/ui/Screens.kt +++ b/app/src/main/java/com/ivy/wallet/ui/Screens.kt @@ -2,7 +2,7 @@ package com.ivy.wallet.ui import com.ivy.design.navigation.Screen import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.data.entity.Transaction +import com.ivy.wallet.domain.data.core.Transaction import com.ivy.wallet.ui.paywall.PaywallReason import java.util.* diff --git a/app/src/main/java/com/ivy/wallet/ui/accounts/AccountData.kt b/app/src/main/java/com/ivy/wallet/ui/accounts/AccountData.kt index 58ffb4cc36..62336b06e1 100644 --- a/app/src/main/java/com/ivy/wallet/ui/accounts/AccountData.kt +++ b/app/src/main/java/com/ivy/wallet/ui/accounts/AccountData.kt @@ -1,7 +1,7 @@ package com.ivy.wallet.ui.accounts import com.ivy.wallet.domain.data.Reorderable -import com.ivy.wallet.domain.data.entity.Account +import com.ivy.wallet.domain.data.core.Account data class AccountData( val account: Account, diff --git a/app/src/main/java/com/ivy/wallet/ui/accounts/AccountState.kt b/app/src/main/java/com/ivy/wallet/ui/accounts/AccountState.kt new file mode 100644 index 0000000000..963a480ab1 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/ui/accounts/AccountState.kt @@ -0,0 +1,11 @@ +package com.ivy.wallet.ui.accounts + +import com.ivy.wallet.utils.UiText + +data class AccountState( + val baseCurrency: String = "", + val accountsData: List = emptyList(), + val totalBalanceWithExcluded: Double = 0.0, + val totalBalanceWithExcludedText: UiText = UiText.DynamicString(""), + val reorderVisible: Boolean = false +) \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/ui/accounts/AccountsEvent.kt b/app/src/main/java/com/ivy/wallet/ui/accounts/AccountsEvent.kt new file mode 100644 index 0000000000..1ed888b3c3 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/ui/accounts/AccountsEvent.kt @@ -0,0 +1,9 @@ +package com.ivy.wallet.ui.accounts + +import com.ivy.wallet.domain.data.core.Account + +sealed class AccountsEvent { + data class OnReorder(val reorderedList: List) : AccountsEvent() + data class OnEditAccount(val editedAccount: Account, val newBalance: Double) : AccountsEvent() + data class OnReorderModalVisible(val reorderVisible: Boolean) : AccountsEvent() +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/ui/accounts/AccountsTab.kt b/app/src/main/java/com/ivy/wallet/ui/accounts/AccountsTab.kt index d2c22ad2d8..b540b56c8f 100644 --- a/app/src/main/java/com/ivy/wallet/ui/accounts/AccountsTab.kt +++ b/app/src/main/java/com/ivy/wallet/ui/accounts/AccountsTab.kt @@ -1,15 +1,22 @@ package com.ivy.wallet.ui.accounts -import androidx.compose.foundation.* +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items import androidx.compose.material.Text -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -21,7 +28,7 @@ import com.ivy.design.api.navigation import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style import com.ivy.wallet.R -import com.ivy.wallet.domain.data.entity.Account +import com.ivy.wallet.domain.data.core.Account import com.ivy.wallet.ui.ItemStatistic import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.Main @@ -29,55 +36,39 @@ import com.ivy.wallet.ui.ivyWalletCtx import com.ivy.wallet.ui.main.MainTab import com.ivy.wallet.ui.theme.* import com.ivy.wallet.ui.theme.components.* -import com.ivy.wallet.ui.theme.modal.edit.AccountModal -import com.ivy.wallet.ui.theme.modal.edit.AccountModalData +import com.ivy.wallet.utils.UiText import com.ivy.wallet.utils.clickableNoIndication -import com.ivy.wallet.utils.format import com.ivy.wallet.utils.horizontalSwipeListener import com.ivy.wallet.utils.onScreenStart @Composable fun BoxWithConstraintsScope.AccountsTab(screen: Main) { val viewModel: AccountsViewModel = viewModel() - - val baseCurrency by viewModel.baseCurrencyCode.collectAsState() - val accounts by viewModel.accounts.collectAsState() - val totalBalanceWithExcluded by viewModel.totalBalanceWithExcluded.collectAsState() + val state by viewModel.state().collectAsState() onScreenStart { viewModel.start() } UI( - baseCurrency = baseCurrency, - accounts = accounts, - totalBalanceWithExcluded = totalBalanceWithExcluded, - - onReorder = viewModel::reorder, - onEditAccount = viewModel::editAccount, + state = state, + onEventHandler = viewModel::onEvent ) } @Composable private fun BoxWithConstraintsScope.UI( - baseCurrency: String, - accounts: List, - totalBalanceWithExcluded: Double?, - - onReorder: (List) -> Unit, - onEditAccount: (Account, Double) -> Unit, + state: AccountState = AccountState(), + onEventHandler: (AccountsEvent) -> Unit = {} ) { - var reorderVisible by remember { mutableStateOf(false) } - var accountModalData: AccountModalData? by remember { mutableStateOf(null) } - + val nav = navigation() val ivyContext = ivyWalletCtx() - Column( + LazyColumn( modifier = Modifier .fillMaxSize() .statusBarsPadding() .navigationBarsPadding() - .verticalScroll(rememberScrollState()) .horizontalSwipeListener( sensitivity = 200, onSwipeLeft = { @@ -88,89 +79,86 @@ private fun BoxWithConstraintsScope.UI( } ), ) { - Spacer(Modifier.height(32.dp)) - Row( - modifier = Modifier.fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically - ) { - Spacer(Modifier.width(24.dp)) + item { + Spacer(Modifier.height(32.dp)) - Column { - Text( - text = "Accounts", - style = UI.typo.b1.style( - color = UI.colors.pureInverse, - fontWeight = FontWeight.ExtraBold + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Spacer(Modifier.width(24.dp)) + + Column { + Text( + text = stringResource(R.string.accounts), + style = UI.typo.b1.style( + color = UI.colors.pureInverse, + fontWeight = FontWeight.ExtraBold + ) ) - ) - if (totalBalanceWithExcluded != null) { Spacer(Modifier.height(4.dp)) Text( - text = "Total: $baseCurrency ${ - totalBalanceWithExcluded.format( - baseCurrency - ) - }", + text = state.totalBalanceWithExcludedText.asString(), style = UI.typo.nB2.style( color = Gray, fontWeight = FontWeight.Bold ) ) } - } + Spacer(Modifier.weight(1f)) - Spacer(Modifier.weight(1f)) + ReorderButton { + onEventHandler.invoke(AccountsEvent.OnReorderModalVisible(reorderVisible = true)) + } - ReorderButton { - reorderVisible = true + Spacer(Modifier.width(24.dp)) } - Spacer(Modifier.width(24.dp)) + Spacer(Modifier.height(16.dp)) } - - Spacer(Modifier.height(16.dp)) - - val nav = navigation() - for (accountData in accounts) { + items(state.accountsData) { AccountCard( - baseCurrency = baseCurrency, - accountData = accountData, + baseCurrency = state.baseCurrency, + accountData = it, onBalanceClick = { nav.navigateTo( ItemStatistic( - accountId = accountData.account.id, + accountId = it.account.id, categoryId = null ) ) }, onLongClick = { - reorderVisible = true + onEventHandler.invoke(AccountsEvent.OnReorderModalVisible(reorderVisible = true)) } ) { nav.navigateTo( ItemStatistic( - accountId = accountData.account.id, + accountId = it.account.id, categoryId = null ) ) } } - Spacer(Modifier.height(150.dp)) //scroll hack + item { + Spacer(Modifier.height(150.dp)) //scroll hack + } } ReorderModalSingleType( - visible = reorderVisible, - initialItems = accounts, + visible = state.reorderVisible, + initialItems = state.accountsData, dismiss = { - reorderVisible = false + onEventHandler.invoke(AccountsEvent.OnReorderModalVisible(reorderVisible = false)) }, - onReordered = onReorder + onReordered = { + onEventHandler.invoke(AccountsEvent.OnReorder(reorderedList = it)) + } ) { _, item -> Text( modifier = Modifier @@ -184,15 +172,6 @@ private fun BoxWithConstraintsScope.UI( ) ) } - - AccountModal( - modal = accountModalData, - onCreateAccount = { }, - onEditAccount = onEditAccount, - dismiss = { - accountModalData = null - } - ) } @Composable @@ -233,9 +212,9 @@ private fun AccountCard( IncomeExpensesRow( currency = currency, - incomeLabel = "INCOME THIS MONTH", + incomeLabel = stringResource(R.string.month_income), income = accountData.monthlyIncome, - expensesLabel = "EXPENSES THIS MONTH", + expensesLabel = stringResource(R.string.month_expenses), expenses = accountData.monthlyExpenses ) @@ -289,7 +268,7 @@ private fun AccountHeader( modifier = Modifier .align(Alignment.Bottom) .padding(bottom = 4.dp), - text = "(excluded)", + text = stringResource(R.string.excluded), style = UI.typo.c.style( color = account.color.toComposeColor().dynamicContrast() ) @@ -341,9 +320,9 @@ private fun AccountHeader( @Composable private fun PreviewAccountsTab() { IvyWalletPreview { - UI( + val state = AccountState( baseCurrency = "BGN", - accounts = listOf( + accountsData = listOf( AccountData( account = Account("Phyre", color = Green.toArgb()), balance = 2125.0, @@ -384,9 +363,11 @@ private fun PreviewAccountsTab() { ), ), totalBalanceWithExcluded = 25.54, - - onReorder = {}, - onEditAccount = { _, _ -> } + totalBalanceWithExcludedText = UiText.StringResource( + R.string.total, "BGN", "25.54" + ) ) + + UI(state = state) } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/ivy/wallet/ui/accounts/AccountsViewModel.kt b/app/src/main/java/com/ivy/wallet/ui/accounts/AccountsViewModel.kt index b074d3f52f..83b122d838 100644 --- a/app/src/main/java/com/ivy/wallet/ui/accounts/AccountsViewModel.kt +++ b/app/src/main/java/com/ivy/wallet/ui/accounts/AccountsViewModel.kt @@ -1,26 +1,29 @@ package com.ivy.wallet.ui.accounts -import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.ivy.wallet.domain.data.entity.Account +import com.ivy.fp.viewmodel.IvyViewModel +import com.ivy.wallet.R +import com.ivy.wallet.domain.action.account.AccountsAct +import com.ivy.wallet.domain.action.settings.BaseCurrencyAct +import com.ivy.wallet.domain.action.viewmodel.account.AccountDataAct +import com.ivy.wallet.domain.action.wallet.CalcWalletBalanceAct +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.deprecated.logic.AccountCreator +import com.ivy.wallet.domain.deprecated.sync.item.AccountSync import com.ivy.wallet.domain.event.AccountsUpdatedEvent -import com.ivy.wallet.domain.fp.account.calculateAccountBalance -import com.ivy.wallet.domain.fp.account.calculateAccountIncomeExpense -import com.ivy.wallet.domain.fp.data.WalletDAOs -import com.ivy.wallet.domain.fp.exchangeToBaseCurrency -import com.ivy.wallet.domain.fp.wallet.baseCurrencyCode -import com.ivy.wallet.domain.fp.wallet.calculateWalletBalance -import com.ivy.wallet.domain.logic.AccountCreator -import com.ivy.wallet.domain.sync.item.AccountSync +import com.ivy.wallet.domain.pure.data.WalletDAOs +import com.ivy.wallet.io.persistence.SharedPrefs import com.ivy.wallet.io.persistence.dao.AccountDao import com.ivy.wallet.io.persistence.dao.SettingsDao import com.ivy.wallet.ui.IvyWalletCtx import com.ivy.wallet.ui.onboarding.model.TimePeriod import com.ivy.wallet.ui.onboarding.model.toCloseTimeRange import com.ivy.wallet.utils.TestIdlingResource +import com.ivy.wallet.utils.UiText +import com.ivy.wallet.utils.format import com.ivy.wallet.utils.ioThread -import com.ivy.wallet.utils.readOnly import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch import org.greenrobot.eventbus.EventBus @@ -35,7 +38,13 @@ class AccountsViewModel @Inject constructor( private val accountSync: AccountSync, private val accountCreator: AccountCreator, private val ivyContext: IvyWalletCtx, -) : ViewModel() { + private val sharedPrefs: SharedPrefs, + private val accountsAct: AccountsAct, + private val calcWalletBalanceAct: CalcWalletBalanceAct, + private val baseCurrencyAct: BaseCurrencyAct, + private val accountDataAct: AccountDataAct +) : IvyViewModel() { + override val mutableState: MutableStateFlow = MutableStateFlow(AccountState()) @Subscribe fun onAccountsUpdated(event: AccountsUpdatedEvent) { @@ -46,107 +55,88 @@ class AccountsViewModel @Inject constructor( EventBus.getDefault().register(this) } - private val _baseCurrencyCode = MutableStateFlow("") - val baseCurrencyCode = _baseCurrencyCode.readOnly() - - private val _accounts = MutableStateFlow>(emptyList()) - val accounts = _accounts.readOnly() - - private val _totalBalanceWithExcluded = MutableStateFlow(0.0) - val totalBalanceWithExcluded = _totalBalanceWithExcluded.readOnly() - fun start() { - viewModelScope.launch { - TestIdlingResource.increment() - - val period = TimePeriod.currentMonth( - startDayOfMonth = ivyContext.startDayOfMonth - ) //this must be monthly - val range = period.toRange(ivyContext.startDayOfMonth) - - val baseCurrencyCode = ioThread { baseCurrencyCode(settingsDao) } - _baseCurrencyCode.value = baseCurrencyCode - - _accounts.value = ioThread { - accountDao.findAll() - .map { - val balance = calculateAccountBalance( - transactionDao = walletDAOs.transactionDao, - accountId = it.id - ) - val balanceBaseCurrency = if (it.currency != baseCurrencyCode) { - exchangeToBaseCurrency( - exchangeRateDao = walletDAOs.exchangeRateDao, - baseCurrencyCode = baseCurrencyCode, - fromCurrencyCode = it.currency ?: baseCurrencyCode, - fromAmount = balance - ).orNull()?.toDouble() - } else { - null - } - - val incomeExpensePair = calculateAccountIncomeExpense( - transactionDao = walletDAOs.transactionDao, - accountId = it.id, - range = range.toCloseTimeRange() - ) - - AccountData( - account = it, - balance = balance.toDouble(), - balanceBaseCurrency = balanceBaseCurrency, - monthlyIncome = incomeExpensePair.income.toDouble(), - monthlyExpenses = incomeExpensePair.expense.toDouble(), - ) - } - } - - _totalBalanceWithExcluded.value = ioThread { - calculateWalletBalance( - walletDAOs = walletDAOs, - baseCurrencyCode = baseCurrencyCode, - filterExcluded = false - ).value.toDouble() - } - - TestIdlingResource.decrement() + viewModelScope.launch(Dispatchers.Default) { + startInternally() } } - fun reorder(newOrder: List) { - viewModelScope.launch { - TestIdlingResource.increment() - - ioThread { - newOrder.mapIndexed { index, accountData -> - accountDao.save( - accountData.account.copy( - orderNum = index.toDouble(), - isSynced = false - ) + private suspend fun startInternally() { + TestIdlingResource.increment() + + val period = TimePeriod.currentMonth( + startDayOfMonth = ivyContext.startDayOfMonth + ) //this must be monthly + val range = period.toRange(ivyContext.startDayOfMonth) + + val baseCurrencyCode = baseCurrencyAct(Unit) + val accs = accountsAct(Unit) + + val includeTransfersInCalc = + sharedPrefs.getBoolean(SharedPrefs.TRANSFERS_AS_INCOME_EXPENSE, false) + + val accountsDataList = accountDataAct( + AccountDataAct.Input( + accounts = accs, + range = range.toCloseTimeRange(), + baseCurrency = baseCurrencyCode, + includeTransfersInCalc = includeTransfersInCalc + ) + ) + + val totalBalanceWithExcluded = calcWalletBalanceAct( + CalcWalletBalanceAct.Input( + baseCurrency = baseCurrencyCode, + withExcluded = true + ) + ).toDouble() + + updateState { + it.copy( + baseCurrency = baseCurrencyCode, + accountsData = accountsDataList, + totalBalanceWithExcluded = totalBalanceWithExcluded, + totalBalanceWithExcludedText = UiText.StringResource( + R.string.total, baseCurrencyCode, totalBalanceWithExcluded.format( + baseCurrencyCode ) - } - } - start() + ) + ) + } + + TestIdlingResource.decrement() + } + + private suspend fun reorder(newOrder: List) { + TestIdlingResource.increment() - ioThread { - accountSync.sync() + ioThread { + newOrder.mapIndexed { index, accountData -> + accountDao.save( + accountData.account.toEntity().copy( + orderNum = index.toDouble(), + isSynced = false + ) + ) } + } + startInternally() - TestIdlingResource.decrement() + ioThread { + accountSync.sync() } - } - fun editAccount(account: Account, newBalance: Double) { - viewModelScope.launch { - TestIdlingResource.increment() + TestIdlingResource.decrement() + } - accountCreator.editAccount(account, newBalance) { - start() - } + private suspend fun editAccount(account: Account, newBalance: Double) { + TestIdlingResource.increment() - TestIdlingResource.decrement() + accountCreator.editAccount(account, newBalance) { + startInternally() } + + TestIdlingResource.decrement() } override fun onCleared() { @@ -154,4 +144,19 @@ class AccountsViewModel @Inject constructor( super.onCleared() } + private suspend fun reorderModalVisible(reorderVisible: Boolean) { + updateState { + it.copy(reorderVisible = reorderVisible) + } + } + + fun onEvent(event: AccountsEvent) { + viewModelScope.launch(Dispatchers.Default) { + when (event) { + is AccountsEvent.OnReorder -> reorder(event.reorderedList) + is AccountsEvent.OnEditAccount -> editAccount(event.editedAccount, event.newBalance) + is AccountsEvent.OnReorderModalVisible -> reorderModalVisible(event.reorderVisible) + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/ui/accounts/IncomeExpensesRow.kt b/app/src/main/java/com/ivy/wallet/ui/accounts/IncomeExpensesRow.kt index 534e14d203..619e637076 100644 --- a/app/src/main/java/com/ivy/wallet/ui/accounts/IncomeExpensesRow.kt +++ b/app/src/main/java/com/ivy/wallet/ui/accounts/IncomeExpensesRow.kt @@ -7,9 +7,11 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import com.ivy.wallet.R import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style import com.ivy.wallet.ui.theme.wallet.AmountCurrencyB1 @@ -19,9 +21,9 @@ fun IncomeExpensesRow( modifier: Modifier = Modifier, textColor: Color = UI.colors.pureInverse, dividerColor: Color = UI.colors.medium, - incomeLabel: String = "INCOME", + incomeLabel: String = stringResource(R.string.income_uppercase), income: Double, - expensesLabel: String = "EXPENSES", + expensesLabel: String = stringResource(R.string.expenses_uppercase), expenses: Double, currency: String, center: Boolean = true, @@ -113,4 +115,4 @@ private fun LabelAmountColumn( } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/ivy/wallet/ui/applocked/AppLockedScreen.kt b/app/src/main/java/com/ivy/wallet/ui/applocked/AppLockedScreen.kt index e611c422b7..6b040f1fb0 100644 --- a/app/src/main/java/com/ivy/wallet/ui/applocked/AppLockedScreen.kt +++ b/app/src/main/java/com/ivy/wallet/ui/applocked/AppLockedScreen.kt @@ -14,6 +14,7 @@ import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview @@ -46,7 +47,7 @@ fun BoxWithConstraintsScope.AppLockedScreen( .background(UI.colors.medium, UI.shapes.rFull) .padding(vertical = 12.dp) .padding(horizontal = 32.dp), - text = "APP LOCKED", + text = stringResource(R.string.app_locked), style = UI.typo.b2.style( fontWeight = FontWeight.ExtraBold, ) @@ -66,7 +67,7 @@ fun BoxWithConstraintsScope.AppLockedScreen( Spacer(Modifier.weight(1f)) Text( - text = "Authenticate to enter the app", + text = stringResource(R.string.authenticate_text), style = UI.typo.b2.style( fontWeight = FontWeight.SemiBold, color = Gray @@ -80,7 +81,7 @@ fun BoxWithConstraintsScope.AppLockedScreen( modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp), - text = "Unlock", + text = stringResource(R.string.unlock), textStyle = UI.typo.b2.style( color = White, fontWeight = FontWeight.Bold, @@ -128,4 +129,4 @@ private fun Preview_Locked() { onShowOSBiometricsModal = {} ) } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/ivy/wallet/ui/balance/BalanceScreen.kt b/app/src/main/java/com/ivy/wallet/ui/balance/BalanceScreen.kt index f104515347..b16638dfc6 100644 --- a/app/src/main/java/com/ivy/wallet/ui/balance/BalanceScreen.kt +++ b/app/src/main/java/com/ivy/wallet/ui/balance/BalanceScreen.kt @@ -6,6 +6,7 @@ import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.rotate +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -144,7 +145,7 @@ private fun ColumnScope.CurrentBalance( ) { Text( modifier = Modifier.align(Alignment.CenterHorizontally), - text = "CURRENT BALANCE", + text = stringResource(R.string.current_balance), style = UI.typo.b2.style( color = Gray, fontWeight = FontWeight.ExtraBold @@ -170,7 +171,7 @@ private fun ColumnScope.BalanceAfterPlannedPayments( Text( modifier = Modifier .padding(horizontal = 32.dp), - text = "BALANCE AFTER PLANNED PAYMENTS", + text = stringResource(R.string.balance_after_payments), style = UI.typo.b2.style( color = Orange, fontWeight = FontWeight.ExtraBold diff --git a/app/src/main/java/com/ivy/wallet/ui/balance/BalanceViewModel.kt b/app/src/main/java/com/ivy/wallet/ui/balance/BalanceViewModel.kt index e521918d3b..974f06022b 100644 --- a/app/src/main/java/com/ivy/wallet/ui/balance/BalanceViewModel.kt +++ b/app/src/main/java/com/ivy/wallet/ui/balance/BalanceViewModel.kt @@ -2,11 +2,9 @@ package com.ivy.wallet.ui.balance import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.ivy.wallet.domain.fp.data.WalletDAOs -import com.ivy.wallet.domain.fp.wallet.baseCurrencyCode -import com.ivy.wallet.domain.fp.wallet.calculateWalletBalance -import com.ivy.wallet.domain.logic.PlannedPaymentsLogic -import com.ivy.wallet.io.persistence.dao.SettingsDao +import com.ivy.wallet.domain.action.settings.BaseCurrencyAct +import com.ivy.wallet.domain.action.wallet.CalcWalletBalanceAct +import com.ivy.wallet.domain.deprecated.logic.PlannedPaymentsLogic import com.ivy.wallet.ui.IvyWalletCtx import com.ivy.wallet.ui.onboarding.model.TimePeriod import com.ivy.wallet.utils.dateNowUTC @@ -19,10 +17,10 @@ import javax.inject.Inject @HiltViewModel class BalanceViewModel @Inject constructor( - private val walletDAOs: WalletDAOs, - private val settingsDao: SettingsDao, private val plannedPaymentsLogic: PlannedPaymentsLogic, - private val ivyContext: IvyWalletCtx + private val ivyContext: IvyWalletCtx, + private val baseCurrencyAct: BaseCurrencyAct, + private val calcWalletBalanceAct: CalcWalletBalanceAct ) : ViewModel() { private val _period = MutableStateFlow(ivyContext.selectedPeriod) @@ -42,16 +40,14 @@ class BalanceViewModel @Inject constructor( fun start(period: TimePeriod = ivyContext.selectedPeriod) { viewModelScope.launch { - _baseCurrencyCode.value = ioThread { baseCurrencyCode(settingsDao) } + _baseCurrencyCode.value = baseCurrencyAct(Unit) _period.value = period - val currentBalance = ioThread { - calculateWalletBalance( - walletDAOs = walletDAOs, - baseCurrencyCode = baseCurrencyCode.value - ).value.toDouble() - } + val currentBalance = calcWalletBalanceAct( + CalcWalletBalanceAct.Input(baseCurrencyCode.value) + ).toDouble() + _currentBalance.value = currentBalance val plannedPaymentsAmount = ioThread { diff --git a/app/src/main/java/com/ivy/wallet/ui/bankintegrations/ConnectBankScreen.kt b/app/src/main/java/com/ivy/wallet/ui/bankintegrations/ConnectBankScreen.kt deleted file mode 100644 index ae625181c4..0000000000 --- a/app/src/main/java/com/ivy/wallet/ui/bankintegrations/ConnectBankScreen.kt +++ /dev/null @@ -1,131 +0,0 @@ -package com.ivy.wallet.ui.bankintegrations - -import androidx.compose.foundation.layout.* -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.livedata.observeAsState -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.lifecycle.viewmodel.compose.viewModel -import com.google.accompanist.insets.systemBarsPadding -import com.ivy.design.l0_system.UI -import com.ivy.design.l0_system.style -import com.ivy.wallet.ui.ConnectBank -import com.ivy.wallet.ui.IvyWalletPreview -import com.ivy.wallet.ui.RootActivity -import com.ivy.wallet.ui.theme.Orange -import com.ivy.wallet.ui.theme.components.IvyButton -import com.ivy.wallet.ui.theme.components.IvySwitch -import com.ivy.wallet.utils.OpResult -import com.ivy.wallet.utils.onScreenStart - -@Composable -fun BoxWithConstraintsScope.ConnectBankScreen(screen: ConnectBank) { - val viewModel: ConnectBankViewModel = viewModel() - - val opSyncTransactions by viewModel.opSyncTransactions.observeAsState() - val bankSyncEnabled by viewModel.bankSyncEnabled.observeAsState(false) - - onScreenStart { - viewModel.start() - } - - val ivyActivity = LocalContext.current as RootActivity - UI( - opSyncTransactions = opSyncTransactions, - bankSyncEnabled = bankSyncEnabled, - - onConnect = { - viewModel.connectBank( - rootActivity = ivyActivity - ) - }, - onFetchTransactions = viewModel::syncTransactions, - onRemoveCustomer = viewModel::removeCustomer, - onSetBankSyncEnabled = viewModel::setBankSyncEnabled - ) -} - -@Composable -private fun UI( - opSyncTransactions: OpResult?, - bankSyncEnabled: Boolean, - - onConnect: () -> Unit = {}, - onFetchTransactions: () -> Unit = {}, - onRemoveCustomer: () -> Unit = {}, - - onSetBankSyncEnabled: (Boolean) -> Unit = {} -) { - Column( - modifier = Modifier - .fillMaxSize() - .systemBarsPadding(), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center - ) { - IvyButton(text = "Connect") { - onConnect() - } - - Spacer(Modifier.height(24.dp)) - - IvyButton(text = "Sync transactions") { - onFetchTransactions() - } - - if (opSyncTransactions is OpResult.Loading) { - Spacer(Modifier.height(24.dp)) - - Text( - text = "Syncing transactions...", - style = UI.typo.b2.style( - color = Orange, - fontWeight = FontWeight.ExtraBold - ) - ) - } - - Spacer(Modifier.height(24.dp)) - - Row( - verticalAlignment = Alignment.CenterVertically - ) { - Text( - "Bank sync enabled:" - ) - - Spacer(Modifier.width(16.dp)) - - IvySwitch( - enabled = bankSyncEnabled - ) { - onSetBankSyncEnabled(it) - } - } - - Spacer(Modifier.height(24.dp)) - - IvyButton(text = "Remove customer") { - onRemoveCustomer() - } - } -} - -@Preview -@Composable -private fun Preview() { - IvyWalletPreview { - UI( - opSyncTransactions = null, - bankSyncEnabled = true, - - onConnect = {} - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/ui/bankintegrations/ConnectBankViewModel.kt b/app/src/main/java/com/ivy/wallet/ui/bankintegrations/ConnectBankViewModel.kt deleted file mode 100644 index 36a5860c40..0000000000 --- a/app/src/main/java/com/ivy/wallet/ui/bankintegrations/ConnectBankViewModel.kt +++ /dev/null @@ -1,93 +0,0 @@ -package com.ivy.wallet.ui.bankintegrations - -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.ivy.wallet.domain.logic.bankintegrations.BankIntegrationsLogic -import com.ivy.wallet.domain.sync.IvySync -import com.ivy.wallet.io.network.IvySession -import com.ivy.wallet.io.persistence.SharedPrefs -import com.ivy.wallet.io.persistence.dao.UserDao -import com.ivy.wallet.ui.RootActivity -import com.ivy.wallet.utils.OpResult -import com.ivy.wallet.utils.TestIdlingResource -import com.ivy.wallet.utils.asLiveData -import com.ivy.wallet.utils.ioThread -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.launch -import javax.inject.Inject - -@HiltViewModel -class ConnectBankViewModel @Inject constructor( - private val bankIntegrationsLogic: BankIntegrationsLogic, - private val ivySession: IvySession, - private val userDao: UserDao, - private val ivySync: IvySync, - private val sharedPrefs: SharedPrefs -) : ViewModel() { - - private val _opSyncTransactions = MutableLiveData>() - val opSyncTransactions = _opSyncTransactions.asLiveData() - - private val _bankSyncEnabled = MutableLiveData(false) - val bankSyncEnabled = _bankSyncEnabled.asLiveData() - - fun start() { - _bankSyncEnabled.value = sharedPrefs.getBoolean(SharedPrefs.ENABLE_BANK_SYNC, false) - } - - fun connectBank(rootActivity: RootActivity) { - viewModelScope.launch { - TestIdlingResource.increment() - - try { - val user = ivySession.getUserIdSafe()?.let { - ioThread { userDao.findById(it) } - } - - if (user != null) { - bankIntegrationsLogic.connect(rootActivity) - } - } catch (e: Exception) { - e.printStackTrace() - } - - TestIdlingResource.decrement() - } - } - - fun syncTransactions() { - viewModelScope.launch { - TestIdlingResource.increment() - - _opSyncTransactions.value = OpResult.loading() - - try { - bankIntegrationsLogic.sync() - ioThread { - ivySync.sync() - } - - _opSyncTransactions.value = OpResult.success(Unit) - } catch (e: Exception) { - e.printStackTrace() - _opSyncTransactions.value = OpResult.failure(e) - } - - TestIdlingResource.decrement() - } - } - - fun setBankSyncEnabled(enabled: Boolean) { - sharedPrefs.putBoolean(SharedPrefs.ENABLE_BANK_SYNC, enabled) - _bankSyncEnabled.value = enabled - } - - fun removeCustomer() { - viewModelScope.launch { - TestIdlingResource.increment() - bankIntegrationsLogic.removeCustomer() - TestIdlingResource.decrement() - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/ui/budget/BudgetBottomBar.kt b/app/src/main/java/com/ivy/wallet/ui/budget/BudgetBottomBar.kt index dbffc7125c..29f2731c0a 100644 --- a/app/src/main/java/com/ivy/wallet/ui/budget/BudgetBottomBar.kt +++ b/app/src/main/java/com/ivy/wallet/ui/budget/BudgetBottomBar.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import com.ivy.wallet.R import com.ivy.wallet.ui.IvyWalletPreview @@ -20,7 +21,7 @@ internal fun BoxWithConstraintsScope.BudgetBottomBar( ) { BackBottomBar(onBack = onClose) { IvyButton( - text = "Add budget", + text = stringResource(R.string.add_budget), iconStart = R.drawable.ic_plus ) { onAdd() @@ -45,4 +46,4 @@ private fun PreviewBottomBar() { onClose = {} ) } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/ivy/wallet/ui/budget/BudgetScreen.kt b/app/src/main/java/com/ivy/wallet/ui/budget/BudgetScreen.kt index 985f9a1f65..640478ce20 100644 --- a/app/src/main/java/com/ivy/wallet/ui/budget/BudgetScreen.kt +++ b/app/src/main/java/com/ivy/wallet/ui/budget/BudgetScreen.kt @@ -5,10 +5,10 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.Text import androidx.compose.runtime.* -import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview @@ -19,10 +19,10 @@ import com.ivy.design.api.navigation import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style import com.ivy.wallet.R -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.Budget -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.logic.model.CreateBudgetData +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Budget +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.deprecated.logic.model.CreateBudgetData import com.ivy.wallet.ui.BudgetScreen import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.budget.model.DisplayBudget @@ -44,13 +44,13 @@ import com.ivy.wallet.utils.onScreenStart fun BoxWithConstraintsScope.BudgetScreen(screen: BudgetScreen) { val viewModel: BudgetViewModel = viewModel() - val timeRange by viewModel.timeRange.observeAsState() - val baseCurrency by viewModel.baseCurrencyCode.observeAsState("") - val categories by viewModel.categories.observeAsState(emptyList()) - val accounts by viewModel.accounts.observeAsState(emptyList()) - val budgets by viewModel.budgets.observeAsState(emptyList()) - val appBudgetMax by viewModel.appBudgetMax.observeAsState(0.0) - val categoryBudgetsTotal by viewModel.categoryBudgetsTotal.observeAsState(0.0) + val timeRange by viewModel.timeRange.collectAsState() + val baseCurrency by viewModel.baseCurrencyCode.collectAsState() + val categories by viewModel.categories.collectAsState() + val accounts by viewModel.accounts.collectAsState() + val budgets by viewModel.budgets.collectAsState() + val appBudgetMax by viewModel.appBudgetMax.collectAsState() + val categoryBudgetsTotal by viewModel.categoryBudgetsTotal.collectAsState() onScreenStart { viewModel.start() @@ -131,9 +131,8 @@ private fun BoxWithConstraintsScope.UI( Spacer(Modifier.weight(1f)) NoBudgetsEmptyState( - emptyStateTitle = "No budgets", - emptyStateText = "You don't have any budgets set.\n" + - "Tap the \"+ Add budget\" to add one." + emptyStateTitle = stringResource(R.string.no_budgets), + emptyStateText = stringResource(R.string.no_budgets_text) ) Spacer(Modifier.weight(1f)) @@ -208,7 +207,7 @@ private fun Toolbar( .padding(start = 24.dp, end = 16.dp) ) { Text( - text = "Budgets", + text = stringResource(R.string.budgets), style = UI.typo.h2.style( color = UI.colors.pureInverse, fontWeight = FontWeight.ExtraBold @@ -231,11 +230,19 @@ private fun Toolbar( Spacer(Modifier.height(4.dp)) val categoryBudgetText = if (categoryBudgetsTotal > 0) { - "${categoryBudgetsTotal.format(baseCurrency)} $baseCurrency for categories" + stringResource( + R.string.for_categories, + categoryBudgetsTotal.format(baseCurrency), + baseCurrency + ) } else "" val appBudgetMaxText = if (appBudgetMax > 0) { - "${appBudgetMax.format(baseCurrency)} $baseCurrency app budget" + stringResource( + R.string.app_budget, + appBudgetMax.format(baseCurrency), + baseCurrency + ) } else "" val hasBothBudgetTypes = @@ -243,7 +250,12 @@ private fun Toolbar( Text( modifier = Modifier.testTag("budgets_info_text"), text = if (hasBothBudgetTypes) - "Budget info: $categoryBudgetText / $appBudgetMaxText" else "Budget info: $categoryBudgetText$appBudgetMaxText", + stringResource( + R.string.budget_info_both, + categoryBudgetText, + appBudgetMaxText + ) + else stringResource(R.string.budget_info, categoryBudgetText, appBudgetMaxText), style = UI.typo.nC.style( color = Gray, fontWeight = FontWeight.ExtraBold @@ -437,4 +449,4 @@ private fun Preview_Budgets() { onReorder = {} ) } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/ivy/wallet/ui/budget/BudgetViewModel.kt b/app/src/main/java/com/ivy/wallet/ui/budget/BudgetViewModel.kt index 247831cb9d..ddb8a84589 100644 --- a/app/src/main/java/com/ivy/wallet/ui/budget/BudgetViewModel.kt +++ b/app/src/main/java/com/ivy/wallet/ui/budget/BudgetViewModel.kt @@ -1,18 +1,24 @@ package com.ivy.wallet.ui.budget -import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.ivy.fp.sumOfSuspend +import com.ivy.wallet.domain.action.account.AccountsAct +import com.ivy.wallet.domain.action.budget.BudgetsAct +import com.ivy.wallet.domain.action.category.CategoriesAct +import com.ivy.wallet.domain.action.exchange.ExchangeAct +import com.ivy.wallet.domain.action.settings.BaseCurrencyAct +import com.ivy.wallet.domain.action.transaction.HistoryTrnsAct import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.Budget -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.data.entity.Transaction -import com.ivy.wallet.domain.logic.BudgetCreator -import com.ivy.wallet.domain.logic.WalletLogic -import com.ivy.wallet.domain.logic.currency.ExchangeRatesLogic -import com.ivy.wallet.domain.logic.model.CreateBudgetData -import com.ivy.wallet.domain.sync.item.BudgetSync +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Budget +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.data.core.Transaction +import com.ivy.wallet.domain.deprecated.logic.BudgetCreator +import com.ivy.wallet.domain.deprecated.logic.model.CreateBudgetData +import com.ivy.wallet.domain.deprecated.sync.item.BudgetSync +import com.ivy.wallet.domain.pure.exchange.ExchangeData +import com.ivy.wallet.domain.pure.transaction.trnCurrency import com.ivy.wallet.io.persistence.SharedPrefs import com.ivy.wallet.io.persistence.dao.AccountDao import com.ivy.wallet.io.persistence.dao.BudgetDao @@ -20,10 +26,11 @@ import com.ivy.wallet.io.persistence.dao.CategoryDao import com.ivy.wallet.io.persistence.dao.SettingsDao import com.ivy.wallet.ui.IvyWalletCtx import com.ivy.wallet.ui.budget.model.DisplayBudget -import com.ivy.wallet.ui.onboarding.model.FromToTimeRange import com.ivy.wallet.ui.onboarding.model.TimePeriod +import com.ivy.wallet.ui.onboarding.model.toCloseTimeRange import com.ivy.wallet.utils.* import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch import javax.inject.Inject @@ -32,54 +39,50 @@ class BudgetViewModel @Inject constructor( private val sharedPrefs: SharedPrefs, private val settingsDao: SettingsDao, private val budgetDao: BudgetDao, - private val walletLogic: WalletLogic, private val categoryDao: CategoryDao, private val accountDao: AccountDao, - private val exchangeRatesLogic: ExchangeRatesLogic, private val budgetCreator: BudgetCreator, private val budgetSync: BudgetSync, - private val ivyContext: IvyWalletCtx + private val ivyContext: IvyWalletCtx, + private val accountsAct: AccountsAct, + private val categoriesAct: CategoriesAct, + private val budgetsAct: BudgetsAct, + private val baseCurrencyAct: BaseCurrencyAct, + private val historyTrnsAct: HistoryTrnsAct, + private val exchangeAct: ExchangeAct ) : ViewModel() { - private val _timeRange = MutableLiveData() - val timeRange = _timeRange.asLiveData() + private val _timeRange = MutableStateFlow(ivyContext.selectedPeriod.toRange(1)) + val timeRange = _timeRange.readOnly() - private val _baseCurrencyCode = MutableLiveData(getDefaultFIATCurrency().currencyCode) - val baseCurrencyCode = _baseCurrencyCode.asLiveData() + private val _baseCurrencyCode = MutableStateFlow(getDefaultFIATCurrency().currencyCode) + val baseCurrencyCode = _baseCurrencyCode.readOnly() - private val _budgets = MutableLiveData>() - val budgets = _budgets.asLiveData() + private val _budgets = MutableStateFlow>(emptyList()) + val budgets = _budgets.readOnly() - private val _categories = MutableLiveData>() - val categories = _categories.asLiveData() + private val _categories = MutableStateFlow>(emptyList()) + val categories = _categories.readOnly() - private val _accounts = MutableLiveData>() - val accounts = _accounts.asLiveData() + private val _accounts = MutableStateFlow>(emptyList()) + val accounts = _accounts.readOnly() - private val _categoryBudgetsTotal = MutableLiveData() - val categoryBudgetsTotal = _categoryBudgetsTotal.asLiveData() + private val _categoryBudgetsTotal = MutableStateFlow(0.0) + val categoryBudgetsTotal = _categoryBudgetsTotal.readOnly() - private val _appBudgetMax = MutableLiveData() - val appBudgetMax = _appBudgetMax.asLiveData() + private val _appBudgetMax = MutableStateFlow(0.0) + val appBudgetMax = _appBudgetMax.readOnly() fun start() { viewModelScope.launch { TestIdlingResource.increment() - _categories.value = ioThread { - categoryDao.findAll() - }!! + _categories.value = categoriesAct(Unit) - val accounts = ioThread { - accountDao.findAll() - } + val accounts = accountsAct(Unit) _accounts.value = accounts - val settings = ioThread { - settingsDao.findFirst() - } - - val baseCurrency = settings.currency + val baseCurrency = baseCurrencyAct(Unit) _baseCurrencyCode.value = baseCurrency val startDateOfMonth = ivyContext.initStartDayOfMonthInMemory(sharedPrefs = sharedPrefs) @@ -88,13 +91,7 @@ class BudgetViewModel @Inject constructor( ).toRange(startDateOfMonth = startDateOfMonth) _timeRange.value = timeRange - val transactions = ioThread { - walletLogic.history(range = timeRange) - }.filterIsInstance(Transaction::class.java) - - val budgets = ioThread { - budgetDao.findAll() - } + val budgets = budgetsAct(Unit) _appBudgetMax.value = budgets .filter { it.categoryIdsSerialized.isNullOrBlank() } @@ -110,7 +107,7 @@ class BudgetViewModel @Inject constructor( budget = it, spentAmount = calculateSpentAmount( budget = it, - transactions = transactions, + transactions = historyTrnsAct(timeRange.toCloseTimeRange()), accounts = accounts, baseCurrencyCode = baseCurrency ) @@ -122,25 +119,20 @@ class BudgetViewModel @Inject constructor( } } - private fun calculateSpentAmount( + private suspend fun calculateSpentAmount( budget: Budget, transactions: List, baseCurrencyCode: String, accounts: List ): Double { + //TODO: Re-work this by creating an FPAction for it val accountsFilter = budget.parseAccountIds() val categoryFilter = budget.parseCategoryIds() return transactions .filter { accountsFilter.isEmpty() || accountsFilter.contains(it.accountId) } .filter { categoryFilter.isEmpty() || categoryFilter.contains(it.categoryId) } - .sumOf { - val amountBaseCurrency = exchangeRatesLogic.amountBaseCurrency( - transaction = it, - baseCurrency = baseCurrencyCode, - accounts = accounts - ) - + .sumOfSuspend { when (it.type) { TransactionType.INCOME -> { //decrement spent amount if it's not global budget @@ -149,7 +141,15 @@ class BudgetViewModel @Inject constructor( } TransactionType.EXPENSE -> { //increment spent amount - amountBaseCurrency + exchangeAct( + ExchangeAct.Input( + data = ExchangeData( + baseCurrency = baseCurrencyCode, + fromCurrency = trnCurrency(it, accounts, baseCurrencyCode) + ), + amount = it.amount + ) + ).orNull()?.toDouble() ?: 0.0 } TransactionType.TRANSFER -> { //ignore transfers for simplicity @@ -203,7 +203,7 @@ class BudgetViewModel @Inject constructor( ioThread { newOrder.forEachIndexed { index, item -> budgetDao.save( - item.budget.copy( + item.budget.toEntity().copy( orderId = index.toDouble(), isSynced = false ) diff --git a/app/src/main/java/com/ivy/wallet/ui/budget/model/DisplayBudget.kt b/app/src/main/java/com/ivy/wallet/ui/budget/model/DisplayBudget.kt index 89cfec2c6a..c7a69d18a3 100644 --- a/app/src/main/java/com/ivy/wallet/ui/budget/model/DisplayBudget.kt +++ b/app/src/main/java/com/ivy/wallet/ui/budget/model/DisplayBudget.kt @@ -1,7 +1,7 @@ package com.ivy.wallet.ui.budget.model import com.ivy.wallet.domain.data.Reorderable -import com.ivy.wallet.domain.data.entity.Budget +import com.ivy.wallet.domain.data.core.Budget data class DisplayBudget( val budget: Budget, diff --git a/app/src/main/java/com/ivy/wallet/ui/category/CategoriesBottomBar.kt b/app/src/main/java/com/ivy/wallet/ui/category/CategoriesBottomBar.kt index cc12efca00..32efc856ab 100644 --- a/app/src/main/java/com/ivy/wallet/ui/category/CategoriesBottomBar.kt +++ b/app/src/main/java/com/ivy/wallet/ui/category/CategoriesBottomBar.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import com.ivy.wallet.R import com.ivy.wallet.ui.IvyWalletPreview @@ -20,7 +21,7 @@ internal fun BoxWithConstraintsScope.CategoriesBottomBar( ) { BackBottomBar(onBack = onClose) { IvyButton( - text = "Add category", + text = stringResource(R.string.add_category), iconStart = R.drawable.ic_plus ) { onAddCategory() @@ -45,4 +46,4 @@ private fun PreviewBottomBar() { onClose = {} ) } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/ivy/wallet/ui/category/CategoriesScreen.kt b/app/src/main/java/com/ivy/wallet/ui/category/CategoriesScreen.kt index da474882b1..2de0618bd3 100644 --- a/app/src/main/java/com/ivy/wallet/ui/category/CategoriesScreen.kt +++ b/app/src/main/java/com/ivy/wallet/ui/category/CategoriesScreen.kt @@ -4,12 +4,12 @@ import androidx.compose.foundation.* import androidx.compose.foundation.layout.* import androidx.compose.material.Text import androidx.compose.runtime.* -import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp @@ -22,8 +22,8 @@ import com.ivy.design.api.navigation import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style import com.ivy.wallet.R -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.logic.model.CreateCategoryData +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.deprecated.logic.model.CreateCategoryData import com.ivy.wallet.ui.Categories import com.ivy.wallet.ui.ItemStatistic import com.ivy.wallet.ui.IvyWalletPreview @@ -42,8 +42,8 @@ import com.ivy.wallet.utils.onScreenStart fun BoxWithConstraintsScope.CategoriesScreen(screen: Categories) { val viewModel: CategoriesViewModel = viewModel() - val currency by viewModel.currency.observeAsState("") - val categories by viewModel.categories.observeAsState(emptyList()) + val currency by viewModel.currency.collectAsState() + val categories by viewModel.categories.collectAsState() onScreenStart { viewModel.start() @@ -85,7 +85,7 @@ private fun BoxWithConstraintsScope.UI( Spacer(Modifier.width(24.dp)) Text( - text = "Categories", + text = stringResource(R.string.categories), style = UI.typo.h2.style( color = UI.colors.pureInverse, fontWeight = FontWeight.ExtraBold @@ -230,7 +230,7 @@ fun AddedSpent( LabelAmount( textColor = textColor, - label = "EXPENSES THIS MONTH", + label = stringResource(R.string.month_expenses), amount = monthlyExpenses, currency = currency, center = center @@ -262,7 +262,7 @@ fun AddedSpent( LabelAmount( textColor = textColor, - label = "INCOME THIS MONTH", + label = stringResource(R.string.month_income), amount = monthlyIncome, currency = currency, center = center diff --git a/app/src/main/java/com/ivy/wallet/ui/category/CategoriesViewModel.kt b/app/src/main/java/com/ivy/wallet/ui/category/CategoriesViewModel.kt index 1ec260669d..e50e62ef6f 100644 --- a/app/src/main/java/com/ivy/wallet/ui/category/CategoriesViewModel.kt +++ b/app/src/main/java/com/ivy/wallet/ui/category/CategoriesViewModel.kt @@ -1,20 +1,22 @@ package com.ivy.wallet.ui.category -import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.ivy.wallet.domain.logic.CategoryCreator -import com.ivy.wallet.domain.logic.WalletCategoryLogic -import com.ivy.wallet.domain.logic.model.CreateCategoryData -import com.ivy.wallet.domain.sync.item.CategorySync +import com.ivy.wallet.domain.action.category.CategoriesAct +import com.ivy.wallet.domain.action.settings.BaseCurrencyAct +import com.ivy.wallet.domain.deprecated.logic.CategoryCreator +import com.ivy.wallet.domain.deprecated.logic.WalletCategoryLogic +import com.ivy.wallet.domain.deprecated.logic.model.CreateCategoryData +import com.ivy.wallet.domain.deprecated.sync.item.CategorySync import com.ivy.wallet.io.persistence.dao.CategoryDao import com.ivy.wallet.io.persistence.dao.SettingsDao import com.ivy.wallet.ui.IvyWalletCtx import com.ivy.wallet.ui.onboarding.model.TimePeriod import com.ivy.wallet.utils.TestIdlingResource -import com.ivy.wallet.utils.asLiveData import com.ivy.wallet.utils.ioThread +import com.ivy.wallet.utils.readOnly import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch import javax.inject.Inject @@ -25,14 +27,16 @@ class CategoriesViewModel @Inject constructor( private val categoryLogic: WalletCategoryLogic, private val categorySync: CategorySync, private val categoryCreator: CategoryCreator, - private val ivyContext: IvyWalletCtx + private val categoriesAct: CategoriesAct, + private val ivyContext: IvyWalletCtx, + private val baseCurrencyAct: BaseCurrencyAct ) : ViewModel() { - private val _currency = MutableLiveData() - val currency = _currency.asLiveData() + private val _currency = MutableStateFlow("") + val currency = _currency.readOnly() - private val _categories = MutableLiveData>() - val categories = _categories.asLiveData() + private val _categories = MutableStateFlow>(emptyList()) + val categories = _categories.readOnly() fun start() { viewModelScope.launch { @@ -42,11 +46,10 @@ class CategoriesViewModel @Inject constructor( startDayOfMonth = ivyContext.startDayOfMonth ).toRange(ivyContext.startDayOfMonth) //this must be monthly - _currency.value = ioThread { settingsDao.findFirst().currency }!! + _currency.value = baseCurrencyAct(Unit) _categories.value = ioThread { - categoryDao - .findAll() + categoriesAct(Unit) .map { CategoryData( category = it, @@ -77,7 +80,7 @@ class CategoriesViewModel @Inject constructor( ioThread { newOrder.forEachIndexed { index, categoryData -> categoryDao.save( - categoryData.category.copy( + categoryData.category.toEntity().copy( orderNum = index.toDouble(), isSynced = false ) diff --git a/app/src/main/java/com/ivy/wallet/ui/category/CategoryData.kt b/app/src/main/java/com/ivy/wallet/ui/category/CategoryData.kt index 0bfc6f6aa5..5e0b3900f1 100644 --- a/app/src/main/java/com/ivy/wallet/ui/category/CategoryData.kt +++ b/app/src/main/java/com/ivy/wallet/ui/category/CategoryData.kt @@ -1,7 +1,7 @@ package com.ivy.wallet.ui.category import com.ivy.wallet.domain.data.Reorderable -import com.ivy.wallet.domain.data.entity.Category +import com.ivy.wallet.domain.data.core.Category data class CategoryData( val category: Category, diff --git a/app/src/main/java/com/ivy/wallet/ui/charts/CategoryValues.kt b/app/src/main/java/com/ivy/wallet/ui/charts/CategoryValues.kt index a93a489b03..500d318587 100644 --- a/app/src/main/java/com/ivy/wallet/ui/charts/CategoryValues.kt +++ b/app/src/main/java/com/ivy/wallet/ui/charts/CategoryValues.kt @@ -1,6 +1,6 @@ package com.ivy.wallet.ui.charts -import com.ivy.wallet.domain.data.entity.Category +import com.ivy.wallet.domain.data.core.Category class CategoryValues( val category: Category, diff --git a/app/src/main/java/com/ivy/wallet/ui/charts/ChartsScreen.kt b/app/src/main/java/com/ivy/wallet/ui/charts/ChartsScreen.kt index c40ddfebd6..2c6b781c01 100644 --- a/app/src/main/java/com/ivy/wallet/ui/charts/ChartsScreen.kt +++ b/app/src/main/java/com/ivy/wallet/ui/charts/ChartsScreen.kt @@ -10,6 +10,7 @@ import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview @@ -19,10 +20,11 @@ import com.google.accompanist.insets.systemBarsPadding import com.ivy.design.api.navigation import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.fp.charts.ChartPeriod -import com.ivy.wallet.domain.fp.charts.IncomeExpenseChartPoint -import com.ivy.wallet.domain.fp.charts.SingleChartPoint +import com.ivy.wallet.R +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.pure.charts.ChartPeriod +import com.ivy.wallet.domain.pure.charts.IncomeExpenseChartPoint +import com.ivy.wallet.domain.pure.charts.SingleChartPoint import com.ivy.wallet.ui.Charts import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.charts.charts.accountCharts @@ -169,7 +171,7 @@ private fun Toolbar() { Spacer(Modifier.width(32.dp)) Text( - text = "Charts", + text = stringResource(R.string.charts), style = UI.typo.h2.style( fontWeight = FontWeight.ExtraBold ) @@ -202,7 +204,7 @@ private fun Period( modifier = Modifier.clickable { togglePeriod() }, - text = "Period:", + text = stringResource(R.string.period), style = UI.typo.b1 ) @@ -275,4 +277,4 @@ private fun Preview() { baseCurrencyCode = "BGN", ) } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/ivy/wallet/ui/charts/ChartsViewModel.kt b/app/src/main/java/com/ivy/wallet/ui/charts/ChartsViewModel.kt index f8374fd98a..8e011bcbcb 100644 --- a/app/src/main/java/com/ivy/wallet/ui/charts/ChartsViewModel.kt +++ b/app/src/main/java/com/ivy/wallet/ui/charts/ChartsViewModel.kt @@ -2,12 +2,17 @@ package com.ivy.wallet.ui.charts import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.ivy.fp.action.then +import com.ivy.wallet.domain.action.charts.BalanceChartAct +import com.ivy.wallet.domain.action.settings.BaseCurrencyAct import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.fp.charts.* -import com.ivy.wallet.domain.fp.data.WalletDAOs -import com.ivy.wallet.domain.fp.wallet.baseCurrencyCode -import com.ivy.wallet.domain.logic.WalletCategoryLogic +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.deprecated.logic.WalletCategoryLogic +import com.ivy.wallet.domain.pure.charts.ChartPeriod +import com.ivy.wallet.domain.pure.charts.IncomeExpenseChartPoint +import com.ivy.wallet.domain.pure.charts.SingleChartPoint +import com.ivy.wallet.domain.pure.charts.incomeExpenseChart +import com.ivy.wallet.domain.pure.data.WalletDAOs import com.ivy.wallet.io.persistence.dao.CategoryDao import com.ivy.wallet.io.persistence.dao.SettingsDao import com.ivy.wallet.ui.onboarding.model.FromToTimeRange @@ -19,14 +24,15 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch import javax.inject.Inject -import kotlin.math.absoluteValue @HiltViewModel class ChartsViewModel @Inject constructor( private val walletDAOs: WalletDAOs, private val settingsDao: SettingsDao, private val categoryDao: CategoryDao, - private val walletCategoryLogic: WalletCategoryLogic + private val walletCategoryLogic: WalletCategoryLogic, + private val baseCurrencyAct: BaseCurrencyAct, + private val balanceChartAct: BalanceChartAct ) : ViewModel() { private val _period = MutableStateFlow(ChartPeriod.LAST_12_MONTHS) @@ -68,7 +74,7 @@ class ChartsViewModel @Inject constructor( fun start() { viewModelScope.launch { - _baseCurrencyCode.value = ioThread { baseCurrencyCode(settingsDao) } +// _baseCurrencyCode.value = ioThread { baseCurrencyCode(settingsDao) } walletCharts(period = period.value) } @@ -79,13 +85,15 @@ class ChartsViewModel @Inject constructor( _incomeExpenseChart.value = generateIncomeExpenseChart(period) } - private suspend fun generateBalanceChart(period: ChartPeriod) = ioThread { - balanceChart( - walletDAOs = walletDAOs, - baseCurrencyCode = baseCurrencyCode.value, - period = period - ) - } + private suspend fun generateBalanceChart(period: ChartPeriod) = + (baseCurrencyAct then { baseCurrency -> + balanceChartAct( + BalanceChartAct.Input( + baseCurrency = baseCurrency, + period = period + ) + ) + })(Unit) private suspend fun generateIncomeExpenseChart(period: ChartPeriod) = ioThread { incomeExpenseChart( @@ -128,16 +136,16 @@ class ChartsViewModel @Inject constructor( period: ChartPeriod, category: Category ) { - _categoryExpenseValues.value = categoryExpenseValues.loadCategoryValue( - period = period, - category = category, - calculateValue = { range -> - walletCategoryLogic.calculateCategoryExpenses( - category = category, - range = range - ).absoluteValue - } - ) +// _categoryExpenseValues.value = categoryExpenseValues.loadCategoryValue( +// period = period, +// category = category, +// calculateValue = { range -> +// walletCategoryLogic.calculateCategoryExpenses( +// category = category, +// range = range +// ).absoluteValue +// } +// ) } private suspend fun loadCategoryExpenseCount( @@ -147,7 +155,7 @@ class ChartsViewModel @Inject constructor( _categoryExpenseCount.value = categoryExpenseCount.loadCategoryValue( period = period, category = category, - calculateValue = { range -> + calculateValue = { range -> walletCategoryLogic.historyByCategory( category = category, range = range @@ -192,7 +200,7 @@ class ChartsViewModel @Inject constructor( private suspend fun StateFlow>.loadCategoryValue( period: ChartPeriod, category: Category, - calculateValue: (range: FromToTimeRange) -> Double + calculateValue: suspend (range: FromToTimeRange) -> Double ): List { TODO() // val values = ioThread { diff --git a/app/src/main/java/com/ivy/wallet/ui/charts/TimeValue.kt b/app/src/main/java/com/ivy/wallet/ui/charts/TimeValue.kt index 5205ae623d..ba1ff83729 100644 --- a/app/src/main/java/com/ivy/wallet/ui/charts/TimeValue.kt +++ b/app/src/main/java/com/ivy/wallet/ui/charts/TimeValue.kt @@ -1,7 +1,7 @@ package com.ivy.wallet.ui.charts -import com.ivy.wallet.domain.fp.charts.ChartPeriod -import com.ivy.wallet.domain.fp.charts.SingleChartPoint +import com.ivy.wallet.domain.pure.charts.ChartPeriod +import com.ivy.wallet.domain.pure.charts.SingleChartPoint import com.ivy.wallet.ui.onboarding.model.FromToTimeRange import com.ivy.wallet.ui.theme.components.charts.linechart.Value diff --git a/app/src/main/java/com/ivy/wallet/ui/charts/charts/AccountCharts.kt b/app/src/main/java/com/ivy/wallet/ui/charts/charts/AccountCharts.kt index a0ddefe7d6..c752d4ad19 100644 --- a/app/src/main/java/com/ivy/wallet/ui/charts/charts/AccountCharts.kt +++ b/app/src/main/java/com/ivy/wallet/ui/charts/charts/AccountCharts.kt @@ -1,7 +1,7 @@ package com.ivy.wallet.ui.charts.charts import androidx.compose.foundation.lazy.LazyListScope -import com.ivy.wallet.domain.fp.charts.ChartPeriod +import com.ivy.wallet.domain.pure.charts.ChartPeriod fun LazyListScope.accountCharts(period: ChartPeriod) { diff --git a/app/src/main/java/com/ivy/wallet/ui/charts/charts/CategoryCharts.kt b/app/src/main/java/com/ivy/wallet/ui/charts/charts/CategoryCharts.kt index 27946ea082..637886d048 100644 --- a/app/src/main/java/com/ivy/wallet/ui/charts/charts/CategoryCharts.kt +++ b/app/src/main/java/com/ivy/wallet/ui/charts/charts/CategoryCharts.kt @@ -16,8 +16,9 @@ import androidx.compose.ui.unit.dp import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style import com.ivy.wallet.R -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.fp.charts.ChartPeriod +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.pure.charts.ChartPeriod +import com.ivy.wallet.stringRes import com.ivy.wallet.ui.charts.CategoryValues import com.ivy.wallet.ui.charts.toValue import com.ivy.wallet.ui.onboarding.model.toCloseTimeRangeUnsafe @@ -81,7 +82,7 @@ fun LazyListScope.categoryCharts( item { CategoriesChart( period = period, - title = "Expenses", + title = stringRes(R.string.expenses), baseCurrencyCode = baseCurrencyCode, categoryValues = categoryExpenseValues, countChart = false @@ -91,7 +92,7 @@ fun LazyListScope.categoryCharts( item { CategoriesChart( period = period, - title = "Expenses count", + title = stringRes(R.string.expenses_count), baseCurrencyCode = baseCurrencyCode, categoryValues = categoryExpenseCount, countChart = true @@ -101,7 +102,7 @@ fun LazyListScope.categoryCharts( item { CategoriesChart( period = period, - title = "Income", + title = stringRes(R.string.income), titleColor = Green, baseCurrencyCode = baseCurrencyCode, categoryValues = categoryIncomeValues, @@ -112,7 +113,7 @@ fun LazyListScope.categoryCharts( item { CategoriesChart( period = period, - title = "Income count", + title = stringRes(R.string.income_count), titleColor = Green, baseCurrencyCode = baseCurrencyCode, categoryValues = categoryIncomeCount, @@ -192,4 +193,4 @@ private fun CategoriesChart( // ) } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/ivy/wallet/ui/charts/charts/WalletCharts.kt b/app/src/main/java/com/ivy/wallet/ui/charts/charts/WalletCharts.kt index 8ed2e7595c..30b6e2f712 100644 --- a/app/src/main/java/com/ivy/wallet/ui/charts/charts/WalletCharts.kt +++ b/app/src/main/java/com/ivy/wallet/ui/charts/charts/WalletCharts.kt @@ -8,11 +8,13 @@ import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style -import com.ivy.wallet.domain.fp.charts.ChartPeriod -import com.ivy.wallet.domain.fp.charts.SingleChartPoint +import com.ivy.wallet.R +import com.ivy.wallet.domain.pure.charts.ChartPeriod +import com.ivy.wallet.domain.pure.charts.SingleChartPoint import com.ivy.wallet.ui.charts.toValues2 import com.ivy.wallet.ui.theme.Green import com.ivy.wallet.ui.theme.Ivy @@ -107,7 +109,7 @@ fun BalanceChart( Text( modifier = Modifier.padding(start = 24.dp), - text = "Balance chart", + text = stringResource(R.string.balance_chart), style = UI.typo.b1 ) @@ -118,7 +120,7 @@ fun BalanceChart( IvyLineChart( modifier = Modifier.padding(horizontal = 8.dp), height = 400.dp, - title = "BALANCE ${period.display().uppercase()}", + title = stringResource(R.string.balance, period.display().uppercase()), functions = listOf( Function( values = values, @@ -186,4 +188,4 @@ fun ChartInfoCard( Spacer(Modifier.width(24.dp)) } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/ivy/wallet/ui/csvimport/ImportScreen.kt b/app/src/main/java/com/ivy/wallet/ui/csvimport/ImportScreen.kt index da82d745f5..fa7a30423e 100644 --- a/app/src/main/java/com/ivy/wallet/ui/csvimport/ImportScreen.kt +++ b/app/src/main/java/com/ivy/wallet/ui/csvimport/ImportScreen.kt @@ -8,8 +8,8 @@ import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.tooling.preview.Preview import androidx.lifecycle.viewmodel.compose.viewModel -import com.ivy.wallet.domain.logic.csv.model.ImportResult -import com.ivy.wallet.domain.logic.csv.model.ImportType +import com.ivy.wallet.domain.deprecated.logic.csv.model.ImportResult +import com.ivy.wallet.domain.deprecated.logic.csv.model.ImportType import com.ivy.wallet.ui.Import import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.csvimport.flow.ImportFrom diff --git a/app/src/main/java/com/ivy/wallet/ui/csvimport/ImportViewModel.kt b/app/src/main/java/com/ivy/wallet/ui/csvimport/ImportViewModel.kt index 1087eb0fc5..4d278ea971 100644 --- a/app/src/main/java/com/ivy/wallet/ui/csvimport/ImportViewModel.kt +++ b/app/src/main/java/com/ivy/wallet/ui/csvimport/ImportViewModel.kt @@ -6,20 +6,17 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.ivy.design.navigation.Navigation -import com.ivy.wallet.domain.logic.csv.CSVImporter -import com.ivy.wallet.domain.logic.csv.CSVMapper -import com.ivy.wallet.domain.logic.csv.CSVNormalizer -import com.ivy.wallet.domain.logic.csv.IvyFileReader -import com.ivy.wallet.domain.logic.csv.model.ImportResult -import com.ivy.wallet.domain.logic.csv.model.ImportType -import com.ivy.wallet.domain.logic.zip.ExportZipLogic +import com.ivy.wallet.domain.deprecated.logic.csv.CSVImporter +import com.ivy.wallet.domain.deprecated.logic.csv.CSVMapper +import com.ivy.wallet.domain.deprecated.logic.csv.CSVNormalizer +import com.ivy.wallet.domain.deprecated.logic.csv.IvyFileReader +import com.ivy.wallet.domain.deprecated.logic.csv.model.ImportResult +import com.ivy.wallet.domain.deprecated.logic.csv.model.ImportType +import com.ivy.wallet.domain.deprecated.logic.zip.ExportZipLogic import com.ivy.wallet.ui.Import import com.ivy.wallet.ui.IvyWalletCtx import com.ivy.wallet.ui.onboarding.viewmodel.OnboardingViewModel -import com.ivy.wallet.utils.TestIdlingResource -import com.ivy.wallet.utils.asLiveData -import com.ivy.wallet.utils.ioThread -import com.ivy.wallet.utils.uiThread +import com.ivy.wallet.utils.* import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch import timber.log.Timber @@ -79,7 +76,7 @@ class ImportViewModel @Inject constructor( _importStep.value = ImportStep.LOADING - _importResult.value = if (hasCSVExtension(fileUri)) + _importResult.value = if (hasCSVExtension(context, fileUri)) restoreCSVFile(fileUri = fileUri, importType = importType) else { exportZipLogic.import( @@ -196,10 +193,11 @@ class ImportViewModel @Inject constructor( _importStep.value = ImportStep.IMPORT_FROM } - private fun hasCSVExtension(fileUri: Uri): Boolean { - var ex = fileUri.toString() - ex = ex.substring(ex.lastIndexOf(".")) - - return ex.equals(".csv", ignoreCase = true) + private suspend fun hasCSVExtension( + context: Context, + fileUri: Uri + ): Boolean = ioThread { + val fileName = context.getFileName(fileUri) + fileName?.endsWith(suffix = ".csv", ignoreCase = true) ?: false } } \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/ImportFrom.kt b/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/ImportFrom.kt index d0d2d61d75..5666223427 100644 --- a/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/ImportFrom.kt +++ b/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/ImportFrom.kt @@ -1,5 +1,6 @@ package com.ivy.wallet.ui.csvimport.flow + import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -12,6 +13,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -20,11 +22,10 @@ import com.google.accompanist.insets.statusBarsPadding import com.ivy.design.api.navigation import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style -import com.ivy.wallet.domain.logic.csv.model.ImportType +import com.ivy.wallet.R +import com.ivy.wallet.domain.deprecated.logic.csv.model.ImportType import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.onboarding.components.OnboardingToolbar - - import com.ivy.wallet.ui.theme.components.GradientCutBottom import com.ivy.wallet.ui.theme.components.IvyIcon @@ -59,7 +60,7 @@ fun BoxWithConstraintsScope.ImportFrom( Text( modifier = Modifier.padding(start = 32.dp), - text = "Import from", + text = stringResource(R.string.import_from), style = UI.typo.h2.style( fontWeight = FontWeight.Black ) diff --git a/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/ImportProcessing.kt b/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/ImportProcessing.kt index 8d36810d75..5bd807090e 100644 --- a/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/ImportProcessing.kt +++ b/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/ImportProcessing.kt @@ -6,6 +6,7 @@ import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -15,6 +16,7 @@ import com.ivy.design.l0_system.style import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.theme.GradientGreen import com.ivy.wallet.ui.theme.Gray +import com.ivy.wallet.R import com.ivy.wallet.ui.theme.components.IvyDividerLine @@ -32,7 +34,7 @@ fun ImportProcessing( Spacer(Modifier.height(80.dp)) Text( - text = "Please wait", + text = stringResource(R.string.please_wait), style = UI.typo.h2.style( fontWeight = FontWeight.Black ) @@ -57,7 +59,7 @@ fun ImportProcessing( Spacer(modifier = Modifier.weight(1f)) Text( - text = "Importing the CSV file", + text = stringResource(R.string.importing_the_csv_file), style = UI.typo.b2.style( fontWeight = FontWeight.Bold ) diff --git a/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/ImportResultUI.kt b/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/ImportResultUI.kt index 2117579198..8e90c0bb68 100644 --- a/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/ImportResultUI.kt +++ b/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/ImportResultUI.kt @@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.* import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -11,7 +12,8 @@ import com.google.accompanist.insets.systemBarsPadding import com.ivy.design.api.navigation import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style -import com.ivy.wallet.domain.logic.csv.model.ImportResult +import com.ivy.wallet.R +import com.ivy.wallet.domain.deprecated.logic.csv.model.ImportResult import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.theme.* import com.ivy.wallet.ui.theme.components.BackButton @@ -45,7 +47,7 @@ fun ImportResultUI( result.transactionsImported > result.rowsFound / 2 Text( modifier = Modifier.padding(horizontal = 32.dp), - text = if (importSuccess) "Success" else "Failure", + text = if (importSuccess) stringResource(R.string.success) else stringResource(R.string.failure), style = UI.typo.h2.style( fontWeight = FontWeight.Black, color = if (importSuccess) UI.colors.pureInverse else Red @@ -56,7 +58,7 @@ fun ImportResultUI( Text( modifier = Modifier.padding(horizontal = 32.dp), - text = "Imported", + text = stringResource(R.string.imported), style = UI.typo.b1.style( color = Green, fontWeight = FontWeight.Black @@ -77,7 +79,7 @@ fun ImportResultUI( Text( modifier = Modifier.padding(horizontal = 32.dp), - text = "${result.transactionsImported} transactions", + text = stringResource(R.string.transactions_imported, result.transactionsImported), style = UI.typo.nB2.style( fontWeight = FontWeight.Bold, color = Gray @@ -88,7 +90,7 @@ fun ImportResultUI( Text( modifier = Modifier.padding(horizontal = 32.dp), - text = "${result.accountsImported} accounts", + text = stringResource(R.string.accounts_imported, result.accountsImported), style = UI.typo.nB2.style( fontWeight = FontWeight.Bold, color = Gray @@ -99,7 +101,7 @@ fun ImportResultUI( Text( modifier = Modifier.padding(horizontal = 32.dp), - text = "${result.categoriesImported} categories", + text = stringResource(R.string.categories_imported, result.categoriesImported), style = UI.typo.nB2.style( fontWeight = FontWeight.Bold, color = Gray @@ -118,7 +120,7 @@ fun ImportResultUI( Text( modifier = Modifier.padding(horizontal = 32.dp), - text = "Failed", + text = stringResource(R.string.failed), style = UI.typo.b1.style( fontWeight = FontWeight.Black, color = Red @@ -137,7 +139,7 @@ fun ImportResultUI( Text( modifier = Modifier.padding(horizontal = 32.dp), - text = "${result.rowsFound - result.transactionsImported} rows from CSV file not recognized", + text = stringResource(R.string.rows_from_csv_not_recognized, result.rowsFound - result.transactionsImported), style = UI.typo.nB2.style( fontWeight = FontWeight.Bold, color = Gray @@ -152,7 +154,7 @@ fun ImportResultUI( Modifier .fillMaxWidth() .padding(horizontal = 24.dp), - text = "Finish", + text = stringResource(R.string.finish), textColor = White, backgroundGradient = GradientIvy, hasNext = true, diff --git a/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/instructions/DefaultImportSteps.kt b/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/instructions/DefaultImportSteps.kt index 722cd877df..58a7ebeec9 100644 --- a/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/instructions/DefaultImportSteps.kt +++ b/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/instructions/DefaultImportSteps.kt @@ -4,7 +4,9 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import com.ivy.wallet.R @Composable fun DefaultImportSteps( @@ -17,7 +19,7 @@ fun DefaultImportSteps( StepTitle( number = 1, - title = "Export CSV file" + title = stringResource(R.string.export_csv_file) ) Spacer(Modifier.height(12.dp)) diff --git a/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/instructions/FinancistoSteps.kt b/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/instructions/FinancistoSteps.kt index 1764652df6..3659143cf4 100644 --- a/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/instructions/FinancistoSteps.kt +++ b/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/instructions/FinancistoSteps.kt @@ -4,7 +4,9 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import com.ivy.wallet.R @Composable fun FinancistoSteps( @@ -14,8 +16,8 @@ fun FinancistoSteps( StepTitle( number = 1, - title = "Export CSV file with standard options", - description = "Please use the standard options and make sure to include headers." + title = stringResource(R.string.export_csv_file_standard), + description = stringResource(R.string.export_csv_file_standard_description) ) Spacer(Modifier.height(24.dp)) diff --git a/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/instructions/ImportInstructions.kt b/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/instructions/ImportInstructions.kt index 7c308f63b7..d17772e14d 100644 --- a/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/instructions/ImportInstructions.kt +++ b/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/instructions/ImportInstructions.kt @@ -14,6 +14,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight.Companion.Bold import androidx.compose.ui.text.style.TextAlign @@ -25,7 +26,7 @@ import com.ivy.design.api.navigation import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style import com.ivy.wallet.R -import com.ivy.wallet.domain.logic.csv.model.ImportType +import com.ivy.wallet.domain.deprecated.logic.csv.model.ImportType import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.RootActivity import com.ivy.wallet.ui.onboarding.components.OnboardingToolbar @@ -68,7 +69,7 @@ fun BoxWithConstraintsScope.ImportInstructions( Text( modifier = Modifier.padding(start = 32.dp), - text = "How to import", + text = stringResource(R.string.how_to_import), style = UI.typo.h2.style( fontWeight = FontWeight.Black ) @@ -78,7 +79,7 @@ fun BoxWithConstraintsScope.ImportInstructions( Text( modifier = Modifier.padding(start = 32.dp), - text = "open", + text = stringResource(R.string.open), style = UI.typo.b2.style( color = Gray, fontWeight = Bold @@ -103,7 +104,7 @@ fun BoxWithConstraintsScope.ImportInstructions( Text( modifier = Modifier.padding(start = 32.dp), - text = "Steps", + text = stringResource(R.string.steps), style = UI.typo.b1.style( fontWeight = FontWeight.Black ) @@ -169,8 +170,8 @@ fun VideoButton( InstructionButton( modifier = modifier, icon = R.drawable.ic_import_video, - caption = "How to", - text = "Video" + caption = stringResource(R.string.how_to), + text = stringResource(R.string.video) ) { onClick() } @@ -184,8 +185,8 @@ fun ArticleButton( InstructionButton( modifier = modifier, icon = R.drawable.ic_import_web, - caption = "How to", - text = "Article" + caption = stringResource(R.string.how_to), + text = stringResource(R.string.article) ) { onClick() } @@ -246,7 +247,7 @@ fun InstructionButton( @Composable fun UploadFileStep( stepNumber: Int, - text: String = "Upload CSV file", + text: String = stringResource(R.string.upload_csv_file), onUploadClick: () -> Unit ) { StepTitle( diff --git a/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/instructions/IvyWalletSteps.kt b/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/instructions/IvyWalletSteps.kt index 1539ed6151..3f265587c1 100644 --- a/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/instructions/IvyWalletSteps.kt +++ b/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/instructions/IvyWalletSteps.kt @@ -4,7 +4,9 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import com.ivy.wallet.R @Composable fun IvyWalletSteps( @@ -14,7 +16,7 @@ fun IvyWalletSteps( StepTitle( number = 1, - title = "Export Data" + title = stringResource(R.string.export_data) ) Spacer(Modifier.height(12.dp)) @@ -28,7 +30,7 @@ fun IvyWalletSteps( UploadFileStep( stepNumber = 2, - text = "Upload CSV/ZIP file", + text = stringResource(R.string.upload_csv_zip_file), onUploadClick = onUploadClick ) } \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/instructions/MonefySteps.kt b/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/instructions/MonefySteps.kt index a32953f21b..a3ea6f994b 100644 --- a/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/instructions/MonefySteps.kt +++ b/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/instructions/MonefySteps.kt @@ -4,7 +4,9 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import com.ivy.wallet.R @Composable fun MonefySteps( @@ -14,8 +16,8 @@ fun MonefySteps( StepTitle( number = 1, - title = "Export to file", - description = "Character set: UTF-8\nDecimal separator: Decimal point '.'\nDelimiter character: Comma ','" + title = stringResource(R.string.export_to_file), + description = stringResource(R.string.export_to_file_description) ) Spacer(Modifier.height(24.dp)) diff --git a/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/instructions/MoneyManagerPraseSteps.kt b/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/instructions/MoneyManagerPraseSteps.kt index 06f1d5e27f..1396bf0b62 100644 --- a/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/instructions/MoneyManagerPraseSteps.kt +++ b/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/instructions/MoneyManagerPraseSteps.kt @@ -6,7 +6,9 @@ import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import com.ivy.wallet.R import com.ivy.wallet.ui.RootActivity @Composable @@ -17,7 +19,7 @@ fun MoneyManagerPraseSteps( StepTitle( number = 1, - title = "Export Excel file", + title = stringResource(R.string.export_excel_file), ) Spacer(Modifier.height(12.dp)) @@ -32,8 +34,8 @@ fun MoneyManagerPraseSteps( StepTitle( number = 2, - title = "Convert XLS to CSV", - description = "!NOTE: If the exported file doesn't have \".xls\" extension, add it by renaming the file manually." + title = stringResource(R.string.convert_xls_to_csv), + description = stringResource(R.string.convert_xls_to_csv_description) ) Spacer(Modifier.height(12.dp)) @@ -42,7 +44,7 @@ fun MoneyManagerPraseSteps( InstructionButton( modifier = Modifier.padding(horizontal = 16.dp), icon = null, - caption = "Online CSV converter FREE", + caption = stringResource(R.string.online_csv_converter_free), text = "https://www.zamzar.com/converters/document/xls-to-csv/" ) { ivyActivity.openUrlInBrowser("https://www.zamzar.com/converters/document/xls-to-csv/") diff --git a/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/instructions/SpendeeSteps.kt b/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/instructions/SpendeeSteps.kt index a79fa71e7d..5da267e1df 100644 --- a/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/instructions/SpendeeSteps.kt +++ b/app/src/main/java/com/ivy/wallet/ui/csvimport/flow/instructions/SpendeeSteps.kt @@ -4,7 +4,9 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import com.ivy.wallet.R @Composable fun SpendeeSteps( @@ -14,7 +16,7 @@ fun SpendeeSteps( StepTitle( number = 1, - title = "Export CSV file" + title = stringResource(R.string.export_csv_file) ) Spacer(Modifier.height(12.dp)) @@ -28,15 +30,15 @@ fun SpendeeSteps( StepTitle( number = 2, - title = "Check your email's \"Promotions\" and \"Spam\" folders" + title = stringResource(R.string.check_email_spam) ) Spacer(Modifier.height(24.dp)) StepTitle( number = 3, - title = "Download the \"transactions_export...\" file attached to the email.", - description = "If you have more than one currency you'll have to download each \"transactions_export...\" file and import it in Ivy." + title = stringResource(R.string.download_email_file), + description = stringResource(R.string.download_email_file_description) ) Spacer(Modifier.height(24.dp)) diff --git a/app/src/main/java/com/ivy/wallet/ui/edit/EditTransactionScreen.kt b/app/src/main/java/com/ivy/wallet/ui/edit/EditTransactionScreen.kt index 3a9e655b5f..45705eb179 100644 --- a/app/src/main/java/com/ivy/wallet/ui/edit/EditTransactionScreen.kt +++ b/app/src/main/java/com/ivy/wallet/ui/edit/EditTransactionScreen.kt @@ -11,6 +11,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.layout.positionInParent +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.tooling.preview.Preview @@ -21,18 +22,16 @@ import com.google.accompanist.insets.statusBarsPadding import com.ivy.design.api.navigation import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style +import com.ivy.design.utils.hideKeyboard import com.ivy.wallet.R import com.ivy.wallet.domain.data.CustomExchangeRateState import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.logic.model.CreateAccountData -import com.ivy.wallet.domain.logic.model.CreateCategoryData -import com.ivy.wallet.ui.EditPlanned -import com.ivy.wallet.ui.EditTransaction -import com.ivy.wallet.ui.IvyWalletPreview +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.deprecated.logic.model.CreateAccountData +import com.ivy.wallet.domain.deprecated.logic.model.CreateCategoryData +import com.ivy.wallet.ui.* import com.ivy.wallet.ui.edit.core.* -import com.ivy.wallet.ui.ivyWalletCtx import com.ivy.wallet.ui.loan.data.EditTransactionDisplayLoan import com.ivy.wallet.ui.theme.components.AddPrimaryAttributeButton import com.ivy.wallet.ui.theme.components.ChangeTransactionTypeModal @@ -53,29 +52,31 @@ fun BoxWithConstraintsScope.EditTransactionScreen(screen: EditTransaction) { val viewModel: EditTransactionViewModel = viewModel() val transactionType by viewModel.transactionType.observeAsState(screen.type) - val initialTitle by viewModel.initialTitle.observeAsState() + val initialTitle by viewModel.initialTitle.collectAsState() val titleSuggestions by viewModel.titleSuggestions.collectAsState() - val currency by viewModel.currency.observeAsState("") - val description by viewModel.description.observeAsState() - val dateTime by viewModel.dateTime.observeAsState() - val category by viewModel.category.observeAsState() - val account by viewModel.account.observeAsState() - val toAccount by viewModel.toAccount.observeAsState() - val dueDate by viewModel.dueDate.observeAsState() - val amount by viewModel.amount.observeAsState(0.0) + val currency by viewModel.currency.collectAsState() + val description by viewModel.description.collectAsState() + val dateTime by viewModel.dateTime.collectAsState() + val category by viewModel.category.collectAsState() + val account by viewModel.account.collectAsState() + val toAccount by viewModel.toAccount.collectAsState() + val dueDate by viewModel.dueDate.collectAsState() + val amount by viewModel.amount.collectAsState() val loanData by viewModel.displayLoanHelper.collectAsState() val backgroundProcessing by viewModel.backgroundProcessingStarted.collectAsState() val customExchangeRateState by viewModel.customExchangeRateState.collectAsState() - val categories by viewModel.categories.observeAsState(emptyList()) - val accounts by viewModel.accounts.observeAsState(emptyList()) + val categories by viewModel.categories.collectAsState(emptyList()) + val accounts by viewModel.accounts.collectAsState(emptyList()) - val hasChanges by viewModel.hasChanges.observeAsState(false) + val hasChanges by viewModel.hasChanges.collectAsState(false) onScreenStart { viewModel.start(screen) } + val view = rootView() + UI( screen = screen, transactionType = transactionType, @@ -111,7 +112,10 @@ fun BoxWithConstraintsScope.EditTransactionScreen(screen: EditTransaction) { onCreateCategory = viewModel::createCategory, onEditCategory = viewModel::editCategory, onPayPlannedPayment = viewModel::onPayPlannedPayment, - onSave = viewModel::save, + onSave = { + view.hideKeyboard() + viewModel.save() + }, onSetHasChanges = viewModel::setHasChanges, onDelete = viewModel::delete, onCreateAccount = viewModel::createAccount, @@ -335,7 +339,7 @@ private fun BoxWithConstraintsScope.UI( val nav = navigation() AddPrimaryAttributeButton( icon = R.drawable.ic_planned_payments, - text = "Add planned date of payment", + text = stringResource(R.string.add_planned_date_payment), onClick = { nav.back() nav.navigateTo( @@ -386,7 +390,11 @@ private fun BoxWithConstraintsScope.UI( } } else { //no changes, pay - ModalCheck(label = if (transactionType == TransactionType.EXPENSE) "Pay" else "Get") { + ModalCheck( + label = if (transactionType == TransactionType.EXPENSE) stringResource( + R.string.pay + ) else stringResource(R.string.get) + ) { onPayPlannedPayment() } } @@ -485,8 +493,8 @@ private fun BoxWithConstraintsScope.UI( DeleteModal( visible = deleteTrnModalVisible, - title = "Confirm deletion", - description = "Deleting this transaction will remove it from the transaction history and update the balance accordingly.", + title = stringResource(R.string.confirm_deletion), + description = stringResource(R.string.transaction_confirm_deletion_description), dismiss = { deleteTrnModalVisible = false } ) { onDelete() @@ -505,10 +513,9 @@ private fun BoxWithConstraintsScope.UI( DeleteModal( visible = accountChangeModal, - title = "Confirm Account Change", - description = "Note: You are trying to change the account associated with the loan with an account of different currency, " + - "\nAll the loan records will be re-calculated based on today's exchanges rates ", - buttonText = "Confirm", + title = stringResource(R.string.confirm_account_change), + description = stringResource(R.string.confirm_account_change_description), + buttonText = stringResource(R.string.confirm), iconStart = R.drawable.ic_agreed, dismiss = { accountChangeModal = false @@ -519,8 +526,8 @@ private fun BoxWithConstraintsScope.UI( } ProgressModal( - title = "Confirm Account Change", - description = "Please wait, re-calculating all loan records", + title = stringResource(R.string.confirm_account_change), + description = stringResource(R.string.account_change_recalculating), visible = waitModalVisible ) diff --git a/app/src/main/java/com/ivy/wallet/ui/edit/EditTransactionViewModel.kt b/app/src/main/java/com/ivy/wallet/ui/edit/EditTransactionViewModel.kt index 69c1faa53c..d6e32d5fbd 100644 --- a/app/src/main/java/com/ivy/wallet/ui/edit/EditTransactionViewModel.kt +++ b/app/src/main/java/com/ivy/wallet/ui/edit/EditTransactionViewModel.kt @@ -4,18 +4,23 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.ivy.design.navigation.Navigation +import com.ivy.wallet.domain.action.account.AccountByIdAct +import com.ivy.wallet.domain.action.account.AccountsAct +import com.ivy.wallet.domain.action.category.CategoriesAct +import com.ivy.wallet.domain.action.category.CategoryByIdAct +import com.ivy.wallet.domain.action.transaction.TrnByIdAct import com.ivy.wallet.domain.data.CustomExchangeRateState import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.data.entity.Transaction +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.data.core.Transaction +import com.ivy.wallet.domain.deprecated.logic.* +import com.ivy.wallet.domain.deprecated.logic.currency.ExchangeRatesLogic +import com.ivy.wallet.domain.deprecated.logic.loantrasactions.LoanTransactionsLogic +import com.ivy.wallet.domain.deprecated.logic.model.CreateAccountData +import com.ivy.wallet.domain.deprecated.logic.model.CreateCategoryData +import com.ivy.wallet.domain.deprecated.sync.uploader.TransactionUploader import com.ivy.wallet.domain.event.AccountsUpdatedEvent -import com.ivy.wallet.domain.logic.* -import com.ivy.wallet.domain.logic.currency.ExchangeRatesLogic -import com.ivy.wallet.domain.logic.loantrasactions.LoanTransactionsLogic -import com.ivy.wallet.domain.logic.model.CreateAccountData -import com.ivy.wallet.domain.logic.model.CreateCategoryData -import com.ivy.wallet.domain.sync.uploader.TransactionUploader import com.ivy.wallet.io.persistence.SharedPrefs import com.ivy.wallet.io.persistence.dao.* import com.ivy.wallet.ui.EditTransaction @@ -28,6 +33,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch import org.greenrobot.eventbus.EventBus +import java.math.BigDecimal import java.time.LocalDateTime import java.util.* import javax.inject.Inject @@ -49,50 +55,55 @@ class EditTransactionViewModel @Inject constructor( private val paywallLogic: PaywallLogic, private val plannedPaymentsLogic: PlannedPaymentsLogic, private val smartTitleSuggestionsLogic: SmartTitleSuggestionsLogic, - private val loanTransactionsLogic: LoanTransactionsLogic + private val loanTransactionsLogic: LoanTransactionsLogic, + private val accountsAct: AccountsAct, + private val categoriesAct: CategoriesAct, + private val trnByIdAct: TrnByIdAct, + private val categoryByIdAct: CategoryByIdAct, + private val accountByIdAct: AccountByIdAct ) : ViewModel() { private val _transactionType = MutableLiveData() val transactionType = _transactionType - private val _initialTitle = MutableLiveData() - val initialTitle = _initialTitle.asLiveData() + private val _initialTitle = MutableStateFlow(null) + val initialTitle = _initialTitle.readOnly() private val _titleSuggestions = MutableStateFlow(emptySet()) val titleSuggestions = _titleSuggestions.asStateFlow() - private val _currency = MutableLiveData() - val currency = _currency.asLiveData() + private val _currency = MutableStateFlow("") + val currency = _currency.readOnly() - private val _description = MutableLiveData() - val description = _description.asLiveData() + private val _description = MutableStateFlow(null) + val description = _description.readOnly() - private val _dateTime = MutableLiveData() - val dateTime = _dateTime.asLiveData() + private val _dateTime = MutableStateFlow(null) + val dateTime = _dateTime.readOnly() - private val _dueDate = MutableLiveData() - val dueDate = _dueDate.asLiveData() + private val _dueDate = MutableStateFlow(null) + val dueDate = _dueDate.readOnly() - private val _accounts = MutableLiveData>() - val accounts = _accounts.asLiveData() + private val _accounts = MutableStateFlow>(emptyList()) + val accounts = _accounts.readOnly() - private val _categories = MutableLiveData>() - val categories = _categories.asLiveData() + private val _categories = MutableStateFlow>(emptyList()) + val categories = _categories.readOnly() - private val _account = MutableLiveData() - val account = _account.asLiveData() + private val _account = MutableStateFlow(null) + val account = _account.readOnly() - private val _toAccount = MutableLiveData() - val toAccount = _toAccount.asLiveData() + private val _toAccount = MutableStateFlow(null) + val toAccount = _toAccount.readOnly() - private val _category = MutableLiveData() - val category = _category.asLiveData() + private val _category = MutableStateFlow(null) + val category = _category.readOnly() - private val _amount = MutableLiveData(0.0) - val amount = _amount.asLiveData() + private val _amount = MutableStateFlow(0.0) + val amount = _amount.readOnly() - private val _hasChanges = MutableLiveData(false) - val hasChanges = _hasChanges.asLiveData() + private val _hasChanges = MutableStateFlow(false) + val hasChanges = _hasChanges.readOnly() private val _displayLoanHelper: MutableStateFlow = MutableStateFlow(EditTransactionDisplayLoan()) @@ -123,19 +134,19 @@ class EditTransactionViewModel @Inject constructor( baseUserCurrency = baseCurrency() - val accounts = ioThread { accountDao.findAll() }!! + val accounts = accountsAct(Unit) if (accounts.isEmpty()) { closeScreen() return@launch } _accounts.value = accounts - _categories.value = ioThread { categoryDao.findAll() }!! + _categories.value = categoriesAct(Unit) reset() loadedTransaction = screen.initialTransactionId?.let { - ioThread { transactionDao.findById(it)!! } + trnByIdAct(it) } ?: Transaction( accountId = defaultAccountId( screen = screen, @@ -143,7 +154,8 @@ class EditTransactionViewModel @Inject constructor( ), categoryId = screen.categoryId, type = screen.type, - amount = 0.0, + amount = BigDecimal.ZERO, + toAmount = BigDecimal.ZERO ) display(loadedTransaction!!) @@ -208,32 +220,31 @@ class EditTransactionViewModel @Inject constructor( _dateTime.value = transaction.dateTime _description.value = transaction.description _dueDate.value = transaction.dueDate - val selectedAccount = ioThread { accountDao.findById(transaction.accountId)!! } + val selectedAccount = accountByIdAct(transaction.accountId)!! _account.value = selectedAccount _toAccount.value = transaction.toAccountId?.let { - ioThread { accountDao.findById(it) } + accountByIdAct(it) } - _category.value = transaction.smartCategoryId()?.let { - ioThread { categoryDao.findById(it) } + _category.value = transaction.categoryId?.let { + categoryByIdAct(it) } - _amount.value = transaction.amount + _amount.value = transaction.amount.toDouble() updateCurrency(account = selectedAccount) - transaction.toAmount?.let { - val exchangeRate = it / transaction.amount + _customExchangeRateState.value = if (transaction.toAccountId == null) + CustomExchangeRateState() + else { + val exchangeRate = transaction.toAmount / transaction.amount val toAccountCurrency = - _accounts.value?.find { acc -> acc.id == transaction.toAccountId }?.currency - _customExchangeRateState.value = - _customExchangeRateState.value.copy( - showCard = toAccountCurrency != account.value?.currency, - exchangeRate = exchangeRate, - convertedAmount = it, - toCurrencyCode = toAccountCurrency, - fromCurrencyCode = currency.value - ) - } ?: let { - _customExchangeRateState.value = CustomExchangeRateState() + _accounts.value.find { acc -> acc.id == transaction.toAccountId }?.currency + CustomExchangeRateState( + showCard = toAccountCurrency != account.value?.currency, + exchangeRate = exchangeRate.toDouble(), + convertedAmount = transaction.toAmount.toDouble(), + toCurrencyCode = toAccountCurrency, + fromCurrencyCode = currency.value + ) } _displayLoanHelper.value = getDisplayLoanHelper(trans = transaction) @@ -248,7 +259,7 @@ class EditTransactionViewModel @Inject constructor( fun onAmountChanged(newAmount: Double) { viewModelScope.launch { loadedTransaction = loadedTransaction().copy( - amount = newAmount + amount = newAmount.toBigDecimal() ) _amount.value = newAmount updateCustomExchangeRateState(amt = newAmount) @@ -418,7 +429,7 @@ class EditTransactionViewModel @Inject constructor( TestIdlingResource.increment() categoryCreator.createCategory(data) { - _categories.value = ioThread { categoryDao.findAll() }!! + _categories.value = categoriesAct(Unit) //Select the newly created category onCategoryChanged(it) @@ -433,7 +444,7 @@ class EditTransactionViewModel @Inject constructor( TestIdlingResource.increment() categoryCreator.editCategory(updatedCategory) { - _categories.value = ioThread { categoryDao.findAll() }!! + _categories.value = categoriesAct(Unit) } TestIdlingResource.decrement() @@ -446,7 +457,7 @@ class EditTransactionViewModel @Inject constructor( accountCreator.createAccount(data) { EventBus.getDefault().post(AccountsUpdatedEvent()) - _accounts.value = ioThread { accountDao.findAll() }!! + _accounts.value = accountsAct(Unit) } TestIdlingResource.decrement() @@ -484,12 +495,13 @@ class EditTransactionViewModel @Inject constructor( private suspend fun saveInternal(closeScreen: Boolean) { try { ioThread { - val amount = amount.value ?: error("no amount") + val amount = amount.value.toBigDecimal() loadedTransaction = loadedTransaction().copy( accountId = account.value?.id ?: error("no accountId"), toAccountId = toAccount.value?.id, - toAmount = _customExchangeRateState.value.convertedAmount, + toAmount = _customExchangeRateState.value.convertedAmount?.toBigDecimal() + ?: amount, title = title?.trim(), description = description.value?.trim(), amount = amount, @@ -522,7 +534,7 @@ class EditTransactionViewModel @Inject constructor( accountsChanged = false } - transactionDao.save(loadedTransaction()) + transactionDao.save(loadedTransaction().toEntity()) } if (closeScreen) { diff --git a/app/src/main/java/com/ivy/wallet/ui/edit/PrimaryAttributeColumn.kt b/app/src/main/java/com/ivy/wallet/ui/edit/PrimaryAttributeColumn.kt index 476c201c43..13d046949a 100644 --- a/app/src/main/java/com/ivy/wallet/ui/edit/PrimaryAttributeColumn.kt +++ b/app/src/main/java/com/ivy/wallet/ui/edit/PrimaryAttributeColumn.kt @@ -9,6 +9,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -66,7 +67,7 @@ private fun PreviewPrimaryAttributeColumn() { IvyWalletComponentPreview { PrimaryAttributeColumn( icon = R.drawable.ic_description, - title = "Description", + title = stringResource(R.string.description), onClick = { } ) { Spacer(Modifier.height(12.dp)) diff --git a/app/src/main/java/com/ivy/wallet/ui/edit/TransactionDateTime.kt b/app/src/main/java/com/ivy/wallet/ui/edit/TransactionDateTime.kt index a10a7379b3..dd7ac93b27 100644 --- a/app/src/main/java/com/ivy/wallet/ui/edit/TransactionDateTime.kt +++ b/app/src/main/java/com/ivy/wallet/ui/edit/TransactionDateTime.kt @@ -9,6 +9,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -50,7 +51,7 @@ fun TransactionDateTime( Spacer(Modifier.width(8.dp)) Text( - text = "Created on", + text = stringResource(R.string.created_on), style = UI.typo.b2.style( color = UI.colors.gray, fontWeight = FontWeight.Bold diff --git a/app/src/main/java/com/ivy/wallet/ui/edit/core/Category.kt b/app/src/main/java/com/ivy/wallet/ui/edit/core/Category.kt index 1b5817ee56..bcf2dcfa05 100644 --- a/app/src/main/java/com/ivy/wallet/ui/edit/core/Category.kt +++ b/app/src/main/java/com/ivy/wallet/ui/edit/core/Category.kt @@ -3,12 +3,13 @@ package com.ivy.wallet.ui.edit.core import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style import com.ivy.wallet.R -import com.ivy.wallet.domain.data.entity.Category +import com.ivy.wallet.domain.data.core.Category import com.ivy.wallet.ui.theme.Gradient import com.ivy.wallet.ui.theme.components.IvyBorderButton @@ -31,7 +32,7 @@ fun Category( modifier = Modifier.padding(start = 24.dp), iconStart = R.drawable.ic_plus, iconTint = UI.colors.pureInverse, - text = "Add category" + text = stringResource(R.string.add_category) ) { onChooseCategory() } diff --git a/app/src/main/java/com/ivy/wallet/ui/edit/core/Description.kt b/app/src/main/java/com/ivy/wallet/ui/edit/core/Description.kt index fd676f1fa4..66d939c7d6 100644 --- a/app/src/main/java/com/ivy/wallet/ui/edit/core/Description.kt +++ b/app/src/main/java/com/ivy/wallet/ui/edit/core/Description.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -33,7 +34,7 @@ fun Description( } else { AddPrimaryAttributeButton( icon = R.drawable.ic_description, - text = "Add description", + text = stringResource(R.string.add_description), onClick = onAddDescription ) } @@ -46,7 +47,7 @@ private fun DescriptionText( ) { PrimaryAttributeColumn( icon = R.drawable.ic_description, - title = "Description", + title = stringResource(R.string.description), onClick = onClick ) { Spacer(Modifier.height(12.dp)) diff --git a/app/src/main/java/com/ivy/wallet/ui/edit/core/DueDate.kt b/app/src/main/java/com/ivy/wallet/ui/edit/core/DueDate.kt index a9b3c0ecab..340ef8db74 100644 --- a/app/src/main/java/com/ivy/wallet/ui/edit/core/DueDate.kt +++ b/app/src/main/java/com/ivy/wallet/ui/edit/core/DueDate.kt @@ -9,6 +9,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -56,7 +57,7 @@ private fun DueDateCard( Spacer(Modifier.width(8.dp)) Text( - text = "Planned for", + text = stringResource(R.string.planned_for), style = UI.typo.b2.style( fontWeight = FontWeight.ExtraBold, color = UI.colors.pureInverse diff --git a/app/src/main/java/com/ivy/wallet/ui/edit/core/EditBottomSheet.kt b/app/src/main/java/com/ivy/wallet/ui/edit/core/EditBottomSheet.kt index aecf19e906..e1d68b5a99 100644 --- a/app/src/main/java/com/ivy/wallet/ui/edit/core/EditBottomSheet.kt +++ b/app/src/main/java/com/ivy/wallet/ui/edit/core/EditBottomSheet.kt @@ -22,6 +22,7 @@ import androidx.compose.ui.layout.layout import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.platform.LocalView import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp @@ -33,7 +34,7 @@ import com.ivy.design.l0_system.style import com.ivy.wallet.Constants import com.ivy.wallet.R import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.data.entity.Account +import com.ivy.wallet.domain.data.core.Account import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.ivyWalletCtx import com.ivy.wallet.ui.theme.* @@ -135,9 +136,9 @@ fun BoxWithConstraintsScope.EditBottomSheet( ) { //Accounts label val label = when (type) { - TransactionType.INCOME -> "Add money to" - TransactionType.EXPENSE -> "Pay with" - TransactionType.TRANSFER -> "From" + TransactionType.INCOME -> stringResource(R.string.add_money_to) + TransactionType.EXPENSE -> stringResource(R.string.pay_with) + TransactionType.TRANSFER -> stringResource(R.string.from) } SheetHeader( @@ -223,7 +224,7 @@ fun BoxWithConstraintsScope.EditBottomSheet( Text( modifier = Modifier.padding(start = 32.dp), - text = "Account", + text = stringResource(R.string.account), style = UI.typo.b1.style( color = UI.colors.pureInverse, fontWeight = FontWeight.ExtraBold @@ -439,7 +440,7 @@ private fun SheetHeader( Text( modifier = Modifier.padding(start = 32.dp), - text = "To", + text = stringResource(R.string.to), style = UI.typo.b1.style( color = UI.colors.pureInverse, fontWeight = FontWeight.ExtraBold @@ -588,7 +589,7 @@ private fun AddAccount( Text( modifier = Modifier.padding(vertical = 10.dp), - text = "Add account", + text = stringResource(R.string.add_account), style = UI.typo.b2.style( color = UI.colors.pureInverse, fontWeight = FontWeight.ExtraBold diff --git a/app/src/main/java/com/ivy/wallet/ui/edit/core/Title.kt b/app/src/main/java/com/ivy/wallet/ui/edit/core/Title.kt index d590db58ad..a2d17635fe 100644 --- a/app/src/main/java/com/ivy/wallet/ui/edit/core/Title.kt +++ b/app/src/main/java/com/ivy/wallet/ui/edit/core/Title.kt @@ -15,6 +15,7 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardCapitalization @@ -24,6 +25,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style +import com.ivy.wallet.R import com.ivy.wallet.domain.data.TransactionType import com.ivy.wallet.ui.IvyWalletComponentPreview import com.ivy.wallet.ui.theme.components.IvyTitleTextField @@ -56,9 +58,9 @@ fun ColumnScope.Title( .padding(horizontal = 24.dp), value = titleTextFieldValue, hint = when (type) { - TransactionType.INCOME -> "Income title" - TransactionType.EXPENSE -> "Expense title" - TransactionType.TRANSFER -> "Transfer title" + TransactionType.INCOME -> stringResource(R.string.income_title) + TransactionType.EXPENSE -> stringResource(R.string.expense_title) + TransactionType.TRANSFER -> stringResource(R.string.transfer_title) }, keyboardOptions = KeyboardOptions( autoCorrect = true, diff --git a/app/src/main/java/com/ivy/wallet/ui/edit/core/Toolbar.kt b/app/src/main/java/com/ivy/wallet/ui/edit/core/Toolbar.kt index 611cff02ac..9f7e69824e 100644 --- a/app/src/main/java/com/ivy/wallet/ui/edit/core/Toolbar.kt +++ b/app/src/main/java/com/ivy/wallet/ui/edit/core/Toolbar.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.width import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.ivy.design.api.navigation import com.ivy.wallet.R @@ -38,7 +39,7 @@ fun Toolbar( when (type) { TransactionType.INCOME -> { IvyOutlinedButton( - text = "Income", + text = stringResource(R.string.income), iconStart = R.drawable.ic_income ) { onChangeTransactionTypeModal() @@ -48,7 +49,7 @@ fun Toolbar( } TransactionType.EXPENSE -> { IvyOutlinedButton( - text = "Expense", + text = stringResource(R.string.expense), iconStart = R.drawable.ic_expense ) { onChangeTransactionTypeModal() diff --git a/app/src/main/java/com/ivy/wallet/ui/home/CustomerJourney.kt b/app/src/main/java/com/ivy/wallet/ui/home/CustomerJourney.kt index 2579bb6d15..9ffc97c915 100644 --- a/app/src/main/java/com/ivy/wallet/ui/home/CustomerJourney.kt +++ b/app/src/main/java/com/ivy/wallet/ui/home/CustomerJourney.kt @@ -17,8 +17,8 @@ import com.ivy.design.api.navigation import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style import com.ivy.wallet.R -import com.ivy.wallet.domain.logic.CustomerJourneyLogic -import com.ivy.wallet.domain.logic.model.CustomerJourneyCardData +import com.ivy.wallet.domain.deprecated.logic.CustomerJourneyLogic +import com.ivy.wallet.domain.deprecated.logic.model.CustomerJourneyCardData import com.ivy.wallet.ui.IvyWalletComponentPreview import com.ivy.wallet.ui.RootActivity import com.ivy.wallet.ui.ivyWalletCtx diff --git a/app/src/main/java/com/ivy/wallet/ui/home/HomeHeader.kt b/app/src/main/java/com/ivy/wallet/ui/home/HomeHeader.kt index fe4b31cfd1..d4ed55d3f5 100644 --- a/app/src/main/java/com/ivy/wallet/ui/home/HomeHeader.kt +++ b/app/src/main/java/com/ivy/wallet/ui/home/HomeHeader.kt @@ -16,6 +16,7 @@ import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import com.ivy.design.api.navigation @@ -120,7 +121,7 @@ private fun HeaderStickyRow( modifier = Modifier .alpha(percentExpanded) .testTag("home_greeting_text"), - text = if (name.isNotNullOrBlank()) "Hi $name" else "Hi", + text = if (name.isNotNullOrBlank()) stringResource(R.string.hi_name, name) else stringResource(R.string.hi), style = UI.typo.b1.style( fontWeight = FontWeight.ExtraBold, color = UI.colors.pureInverse @@ -234,7 +235,7 @@ fun CashFlowInfo( modifier = Modifier.padding( start = 24.dp ), - text = "Cashflow: ${if (cashflow > 0) "+" else ""}${cashflow.format(currency)} $currency", + text = stringResource(R.string.cashflow, (if (cashflow > 0) "+" else ""), cashflow.format(currency), currency), style = UI.typo.nB2.style( color = if (cashflow < 0) Gray else Green ) @@ -268,9 +269,10 @@ private fun IncomeExpenses( icon = R.drawable.ic_income, backgroundGradient = GradientGreen, textColor = White, - label = "Income", + label = stringResource(R.string.income), currency = currency, - amount = monthlyIncome + amount = monthlyIncome, + testTag = "home_card_income" ) { nav.navigateTo( PieChartStatistic( @@ -286,9 +288,10 @@ private fun IncomeExpenses( icon = R.drawable.ic_expense, backgroundGradient = Gradient(UI.colors.pureInverse, UI.colors.gray), textColor = UI.colors.pure, - label = "Expenses", + label = stringResource(R.string.expenses), currency = currency, - amount = monthlyExpenses.absoluteValue + amount = monthlyExpenses.absoluteValue, + testTag = "home_card_expense" ) { nav.navigateTo( PieChartStatistic( @@ -310,6 +313,7 @@ private fun RowScope.HeaderCard( label: String, currency: String, amount: Double, + testTag: String, onClick: () -> Unit ) { Column( @@ -320,6 +324,7 @@ private fun RowScope.HeaderCard( } .clip(UI.shapes.r4) .background(backgroundGradient.asHorizontalBrush()) + .testTag(testTag) .clickable( onClick = onClick ) diff --git a/app/src/main/java/com/ivy/wallet/ui/home/HomeMoreMenu.kt b/app/src/main/java/com/ivy/wallet/ui/home/HomeMoreMenu.kt index a84948bd97..a7427ab022 100644 --- a/app/src/main/java/com/ivy/wallet/ui/home/HomeMoreMenu.kt +++ b/app/src/main/java/com/ivy/wallet/ui/home/HomeMoreMenu.kt @@ -18,6 +18,7 @@ import androidx.compose.ui.layout.layout import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview @@ -250,7 +251,7 @@ private fun SearchButton( modifier = Modifier.padding( vertical = 12.dp, ), - text = "Search transactions", + text = stringResource(R.string.search_transactions), style = UI.typo.b2.style( fontWeight = FontWeight.SemiBold, color = UI.colors.pureInverse @@ -291,7 +292,7 @@ private fun ColumnScope.OpenSource() { .padding(start = 16.dp, end = 24.dp) ) { Text( - text = "Ivy Wallet is open-source", + text = stringResource(R.string.ivy_wallet_open_source), style = UI.typo.b2.style( fontWeight = FontWeight.ExtraBold ) @@ -330,7 +331,7 @@ private fun ColumnScope.Buffer( Spacer(Modifier.width(24.dp)) Text( - text = "Savings goal", + text = stringResource(R.string.savings_goal), style = UI.typo.b1.style( color = UI.colors.pureInverse, fontWeight = FontWeight.ExtraBold @@ -369,7 +370,7 @@ private fun QuickAccess( Text( modifier = Modifier.padding(start = 24.dp), - text = "Quick access" + text = stringResource(R.string.quick_access) ) Spacer(Modifier.height(16.dp)) @@ -383,7 +384,7 @@ private fun QuickAccess( MoreMenuButton( icon = R.drawable.home_more_menu_settings, - label = "Settings" + label = stringResource(R.string.settings) ) { nav.navigateTo(Settings) } @@ -392,7 +393,7 @@ private fun QuickAccess( MoreMenuButton( icon = R.drawable.home_more_menu_categories, - label = "Categories" + label = stringResource(R.string.categories) ) { nav.navigateTo(Categories) } @@ -406,9 +407,9 @@ private fun QuickAccess( Theme.AUTO -> R.drawable.home_more_menu_auto_mode }, label = when (theme) { - Theme.LIGHT -> "Light mode" - Theme.DARK -> "Dark mode" - Theme.AUTO -> "Auto mode" + Theme.LIGHT -> stringResource(R.string.light_mode) + Theme.DARK -> stringResource(R.string.dark_mode) + Theme.AUTO -> stringResource(R.string.auto_mode) }, backgroundColor = when (theme) { Theme.LIGHT -> UI.colors.pure @@ -428,7 +429,7 @@ private fun QuickAccess( MoreMenuButton( icon = R.drawable.home_more_menu_planned_payments, - label = "Planned\nPayments" + label = stringResource(R.string.planned_payments) ) { nav.navigateTo(PlannedPayments) } @@ -456,7 +457,7 @@ private fun QuickAccess( MoreMenuButton( icon = R.drawable.home_more_menu_share, - label = "Share Ivy" + label = stringResource(R.string.share_ivy) ) { (context as RootActivity).shareIvyWallet() } @@ -465,7 +466,7 @@ private fun QuickAccess( MoreMenuButton( icon = R.drawable.home_more_menu_reports, - label = "Reports", + label = stringResource(R.string.reports), ) { nav.navigateTo(Report) } @@ -474,7 +475,7 @@ private fun QuickAccess( MoreMenuButton( icon = R.drawable.home_more_menu_budgets, - label = "Budgets", + label = stringResource(R.string.budgets), ) { nav.navigateTo(BudgetScreen) } @@ -483,7 +484,7 @@ private fun QuickAccess( MoreMenuButton( icon = R.drawable.home_more_menu_loans, - label = "Loans", + label = stringResource(R.string.loans), ) { nav.navigateTo(Loans) } diff --git a/app/src/main/java/com/ivy/wallet/ui/home/HomeState.kt b/app/src/main/java/com/ivy/wallet/ui/home/HomeState.kt index 1aab9a51ac..2a87bca951 100644 --- a/app/src/main/java/com/ivy/wallet/ui/home/HomeState.kt +++ b/app/src/main/java/com/ivy/wallet/ui/home/HomeState.kt @@ -2,11 +2,11 @@ package com.ivy.wallet.ui.home import com.ivy.design.l0_system.Theme import com.ivy.wallet.domain.data.TransactionHistoryItem -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.data.entity.Transaction -import com.ivy.wallet.domain.fp.data.IncomeExpensePair -import com.ivy.wallet.domain.logic.model.CustomerJourneyCardData +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.data.core.Transaction +import com.ivy.wallet.domain.deprecated.logic.model.CustomerJourneyCardData +import com.ivy.wallet.domain.pure.data.IncomeExpensePair import com.ivy.wallet.ui.IvyWalletCtx import com.ivy.wallet.ui.onboarding.model.TimePeriod import java.math.BigDecimal @@ -15,20 +15,25 @@ data class HomeState( val theme: Theme, val name: String, val baseCurrencyCode: String, + val buffer: BigDecimal, + val period: TimePeriod, + val accounts: List, val categories: List, + + val history: List, + val monthly: IncomeExpensePair, val balance: BigDecimal, - val buffer: BigDecimal, val bufferDiff: BigDecimal, - val monthly: IncomeExpensePair, + val upcoming: IncomeExpensePair, val upcomingTrns: List, val upcomingExpanded: Boolean, val overdue: IncomeExpensePair, val overdueTrns: List, val overdueExpanded: Boolean, - val history: List, + val customerJourneyCards: List, val hideCurrentBalance: Boolean ) { diff --git a/app/src/main/java/com/ivy/wallet/ui/home/HomeTab.kt b/app/src/main/java/com/ivy/wallet/ui/home/HomeTab.kt index 2ec26733aa..c2518c2968 100644 --- a/app/src/main/java/com/ivy/wallet/ui/home/HomeTab.kt +++ b/app/src/main/java/com/ivy/wallet/ui/home/HomeTab.kt @@ -12,6 +12,7 @@ import androidx.compose.ui.geometry.Offset import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.NestedScrollSource import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Velocity import androidx.compose.ui.unit.dp @@ -20,12 +21,14 @@ import com.google.accompanist.insets.systemBarsPadding import com.ivy.design.api.navigation import com.ivy.design.l0_system.Theme import com.ivy.wallet.Constants +import com.ivy.wallet.R import com.ivy.wallet.domain.data.IvyCurrency import com.ivy.wallet.domain.data.TransactionHistoryItem -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.data.entity.Transaction -import com.ivy.wallet.domain.logic.model.CustomerJourneyCardData +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.data.core.Transaction +import com.ivy.wallet.domain.deprecated.logic.model.CustomerJourneyCardData +import com.ivy.wallet.stringRes import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.Main import com.ivy.wallet.ui.ivyWalletCtx @@ -287,7 +290,7 @@ private fun BoxWithConstraintsScope.UI( } CurrencyModal( - title = "Set currency", + title = stringResource(R.string.set_currency), initialCurrency = IvyCurrency.fromCode(currencyCode), visible = currencyModalVisible, dismiss = { currencyModalVisible = false } @@ -441,10 +444,11 @@ fun HomeLazyColumn( overdueExpenses = overdueExpenses, history = history, onPayOrGet = onPayOrGet, - emptyStateTitle = "No transactions", - emptyStateText = "You don't have any transactions for ${ + emptyStateTitle = stringRes(R.string.no_transactions), + emptyStateText = stringRes( + R.string.no_transactions_description, period.toDisplayLong(ivyContext.startDayOfMonth) - }.\nYou can add one by tapping the \"+\" button." + ) ) } } diff --git a/app/src/main/java/com/ivy/wallet/ui/home/HomeViewModel.kt b/app/src/main/java/com/ivy/wallet/ui/home/HomeViewModel.kt index 21a8254080..a609de10e7 100644 --- a/app/src/main/java/com/ivy/wallet/ui/home/HomeViewModel.kt +++ b/app/src/main/java/com/ivy/wallet/ui/home/HomeViewModel.kt @@ -3,21 +3,23 @@ package com.ivy.wallet.ui.home import androidx.lifecycle.viewModelScope import com.ivy.design.l0_system.Theme import com.ivy.design.navigation.Navigation -import com.ivy.design.viewmodel.IvyViewModel -import com.ivy.wallet.domain.action.CalcOverdueAct -import com.ivy.wallet.domain.action.CalcUpcomingAct -import com.ivy.wallet.domain.action.CalcWalletBalanceAct -import com.ivy.wallet.domain.action.HistoryWithDateDivAct -import com.ivy.wallet.domain.data.entity.Transaction -import com.ivy.wallet.domain.fp.data.WalletDAOs -import com.ivy.wallet.domain.fp.wallet.calculateWalletIncomeExpense -import com.ivy.wallet.domain.fp.wallet.walletBufferDiff -import com.ivy.wallet.domain.logic.CustomerJourneyLogic -import com.ivy.wallet.domain.logic.PlannedPaymentsLogic -import com.ivy.wallet.domain.logic.currency.ExchangeRatesLogic -import com.ivy.wallet.domain.logic.model.CustomerJourneyCardData +import com.ivy.fp.viewmodel.IvyViewModel +import com.ivy.wallet.domain.action.account.AccountsAct +import com.ivy.wallet.domain.action.category.CategoriesAct +import com.ivy.wallet.domain.action.settings.CalcBufferDiffAct +import com.ivy.wallet.domain.action.settings.SettingsAct +import com.ivy.wallet.domain.action.transaction.HistoryWithDateDivsAct +import com.ivy.wallet.domain.action.viewmodel.home.HasTrnsAct +import com.ivy.wallet.domain.action.viewmodel.home.OverdueAct +import com.ivy.wallet.domain.action.viewmodel.home.UpcomingAct +import com.ivy.wallet.domain.action.wallet.CalcIncomeExpenseAct +import com.ivy.wallet.domain.action.wallet.CalcWalletBalanceAct +import com.ivy.wallet.domain.data.core.Transaction +import com.ivy.wallet.domain.deprecated.logic.CustomerJourneyLogic +import com.ivy.wallet.domain.deprecated.logic.PlannedPaymentsLogic +import com.ivy.wallet.domain.deprecated.logic.currency.ExchangeRatesLogic +import com.ivy.wallet.domain.deprecated.logic.model.CustomerJourneyCardData import com.ivy.wallet.io.persistence.SharedPrefs -import com.ivy.wallet.io.persistence.dao.CategoryDao import com.ivy.wallet.io.persistence.dao.SettingsDao import com.ivy.wallet.ui.BalanceScreen import com.ivy.wallet.ui.IvyWalletCtx @@ -37,19 +39,23 @@ import javax.inject.Inject @HiltViewModel class HomeViewModel @Inject constructor( - private val walletDAOs: WalletDAOs, - private val settingsDao: SettingsDao, - private val categoryDao: CategoryDao, private val ivyContext: IvyWalletCtx, private val nav: Navigation, + private val settingsDao: SettingsDao, private val exchangeRatesLogic: ExchangeRatesLogic, private val plannedPaymentsLogic: PlannedPaymentsLogic, private val customerJourneyLogic: CustomerJourneyLogic, private val sharedPrefs: SharedPrefs, + private val historyWithDateDivsAct: HistoryWithDateDivsAct, + private val calcIncomeExpenseAct: CalcIncomeExpenseAct, private val calcWalletBalanceAct: CalcWalletBalanceAct, - private val calcUpcomingAct: CalcUpcomingAct, - private val calcOverdueAct: CalcOverdueAct, - private val historyWithDateDivAct: HistoryWithDateDivAct, + private val settingsAct: SettingsAct, + private val accountsAct: AccountsAct, + private val categoriesAct: CategoriesAct, + private val calcBufferDiffAct: CalcBufferDiffAct, + private val upcomingAct: UpcomingAct, + private val overdueAct: OverdueAct, + private val hasTrnsAct: HasTrnsAct ) : IvyViewModel() { override val mutableState: MutableStateFlow = MutableStateFlow( HomeState.initial(ivyWalletCtx = ivyContext) @@ -74,15 +80,19 @@ class HomeViewModel @Inject constructor( viewModelScope.launch { TestIdlingResource.increment() - val settings = ioThread { settingsDao.findFirst() } + val settings = settingsAct(Unit) + val baseCurrency = settings.baseCurrency - val hideCurrentBalance = sharedPrefs.getBoolean(SharedPrefs.HIDE_CURRENT_BALANCE, false) + val hideCurrentBalance = sharedPrefs.getBoolean( + SharedPrefs.HIDE_CURRENT_BALANCE, + false + ) updateState { it.copy( theme = settings.theme, name = settings.name, - baseCurrencyCode = settings.currency, + baseCurrencyCode = baseCurrency, period = period, hideCurrentBalance = hideCurrentBalance ) @@ -91,47 +101,55 @@ class HomeViewModel @Inject constructor( //This method is used to restore the theme when user imports locally backed up data loadNewTheme(settings.theme) + val accounts = accountsAct(Unit) + updateState { it.copy( - categories = ioThread { categoryDao.findAll() }, - accounts = ioThread { walletDAOs.accountDao.findAll() } + categories = categoriesAct(Unit), + accounts = accounts ) } val timeRange = period.toRange(ivyContext.startDayOfMonth) + .toCloseTimeRange() - updateState { - it.copy( - balance = calcWalletBalanceAct(settings.currency) + val monthlyIncomeExpense = calcIncomeExpenseAct( + CalcIncomeExpenseAct.Input( + baseCurrency = baseCurrency, + accounts = accounts, + range = timeRange ) - } + ) + + val balance = calcWalletBalanceAct( + CalcWalletBalanceAct.Input(baseCurrency = baseCurrency) + ) updateState { it.copy( - buffer = settings.bufferAmount.toBigDecimal(), - bufferDiff = ioThread { - walletBufferDiff( - settings = settings, - balance = stateVal().balance - ) - } + balance = balance, + monthly = monthlyIncomeExpense ) } updateState { it.copy( - monthly = ioThread { - calculateWalletIncomeExpense( - walletDAOs = walletDAOs, - baseCurrencyCode = stateVal().baseCurrencyCode, - range = timeRange.toCloseTimeRange() - ).value - } + history = historyWithDateDivsAct( + HistoryWithDateDivsAct.Input( + range = timeRange, + baseCurrency = baseCurrency + ) + ) ) } updateState { - val result = calcUpcomingAct(timeRange) + val result = upcomingAct( + UpcomingAct.Input( + range = timeRange, + baseCurrency = baseCurrency + ) + ) it.copy( upcoming = result.upcoming, upcomingTrns = result.upcomingTrns @@ -139,7 +157,12 @@ class HomeViewModel @Inject constructor( } updateState { - val result = calcOverdueAct(timeRange) + val result = overdueAct( + OverdueAct.Input( + range = timeRange, + baseCurrency = baseCurrency + ) + ) it.copy( overdue = result.overdue, overdueTrns = result.overdueTrns @@ -148,18 +171,19 @@ class HomeViewModel @Inject constructor( updateState { it.copy( - history = historyWithDateDivAct( - HistoryWithDateDivAct.Input( - timeRange = timeRange.toCloseTimeRange(), - baseCurrencyCode = stateVal().baseCurrencyCode - ) - ) + customerJourneyCards = ioThread { customerJourneyLogic.loadCards() } ) } updateState { it.copy( - customerJourneyCards = ioThread { customerJourneyLogic.loadCards() } + buffer = settings.bufferAmount, + bufferDiff = calcBufferDiffAct( + CalcBufferDiffAct.Input( + balance = balance, + buffer = settings.bufferAmount + ) + ) ) } @@ -183,9 +207,7 @@ class HomeViewModel @Inject constructor( viewModelScope.launch { TestIdlingResource.increment() - val hasTransactions = ioThread { - walletDAOs.transactionDao.findAll_LIMIT_1().isNotEmpty() - } + val hasTransactions = hasTrnsAct(Unit) if (hasTransactions) { //has transactions show him "Balance" screen nav.navigateTo(BalanceScreen) diff --git a/app/src/main/java/com/ivy/wallet/ui/loan/LoanBottomBar.kt b/app/src/main/java/com/ivy/wallet/ui/loan/LoanBottomBar.kt index fefa1b63e4..1da0afa3d2 100644 --- a/app/src/main/java/com/ivy/wallet/ui/loan/LoanBottomBar.kt +++ b/app/src/main/java/com/ivy/wallet/ui/loan/LoanBottomBar.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import com.ivy.wallet.R import com.ivy.wallet.ui.IvyWalletPreview @@ -20,7 +21,7 @@ internal fun BoxWithConstraintsScope.LoanBottomBar( ) { BackBottomBar(onBack = onClose) { IvyButton( - text = "Add loan", + text = stringResource(R.string.add_loan), iconStart = R.drawable.ic_plus ) { onAdd() diff --git a/app/src/main/java/com/ivy/wallet/ui/loan/LoanViewModel.kt b/app/src/main/java/com/ivy/wallet/ui/loan/LoanViewModel.kt index 8f377521ee..4bb3f115d7 100644 --- a/app/src/main/java/com/ivy/wallet/ui/loan/LoanViewModel.kt +++ b/app/src/main/java/com/ivy/wallet/ui/loan/LoanViewModel.kt @@ -2,15 +2,18 @@ package com.ivy.wallet.ui.loan import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.Loan +import com.ivy.wallet.domain.action.account.AccountsAct +import com.ivy.wallet.domain.action.category.CategoriesAct +import com.ivy.wallet.domain.action.loan.LoansAct +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Loan +import com.ivy.wallet.domain.deprecated.logic.AccountCreator +import com.ivy.wallet.domain.deprecated.logic.LoanCreator +import com.ivy.wallet.domain.deprecated.logic.loantrasactions.LoanTransactionsLogic +import com.ivy.wallet.domain.deprecated.logic.model.CreateAccountData +import com.ivy.wallet.domain.deprecated.logic.model.CreateLoanData +import com.ivy.wallet.domain.deprecated.sync.item.LoanSync import com.ivy.wallet.domain.event.AccountsUpdatedEvent -import com.ivy.wallet.domain.logic.AccountCreator -import com.ivy.wallet.domain.logic.LoanCreator -import com.ivy.wallet.domain.logic.loantrasactions.LoanTransactionsLogic -import com.ivy.wallet.domain.logic.model.CreateAccountData -import com.ivy.wallet.domain.logic.model.CreateLoanData -import com.ivy.wallet.domain.sync.item.LoanSync import com.ivy.wallet.io.persistence.SharedPrefs import com.ivy.wallet.io.persistence.dao.AccountDao import com.ivy.wallet.io.persistence.dao.LoanDao @@ -42,7 +45,10 @@ class LoanViewModel @Inject constructor( private val sharedPrefs: SharedPrefs, private val accountDao: AccountDao, private val accountCreator: AccountCreator, - private val loanTransactionsLogic: LoanTransactionsLogic + private val loanTransactionsLogic: LoanTransactionsLogic, + private val loansAct: LoansAct, + private val accountsAct: AccountsAct, + private val categoriesAct: CategoriesAct ) : ViewModel() { private val _baseCurrencyCode = MutableStateFlow(getDefaultFIATCurrency().currencyCode) @@ -75,7 +81,7 @@ class LoanViewModel @Inject constructor( initialiseAccounts() _loans.value = ioThread { - loanDao.findAll() + loansAct(Unit) .map { loan -> val amountPaid = calculateAmountPaid(loan) val loanAmount = loan.amount @@ -111,7 +117,7 @@ class LoanViewModel @Inject constructor( } private suspend fun initialiseAccounts() { - val accounts = ioThread { accountDao.findAll() } + val accounts = accountsAct(Unit) _accounts.value = accounts _selectedAccount.value = defaultAccountId(accounts) _selectedAccount.value?.let { @@ -142,7 +148,7 @@ class LoanViewModel @Inject constructor( ioThread { newOrder.forEachIndexed { index, item -> loanDao.save( - item.loan.copy( + item.loan.toEntity().copy( orderNum = index.toDouble(), isSynced = false ) @@ -165,7 +171,7 @@ class LoanViewModel @Inject constructor( accountCreator.createAccount(data) { EventBus.getDefault().post(AccountsUpdatedEvent()) - _accounts.value = ioThread { accountDao.findAll() }!! + _accounts.value = accountsAct(Unit) _state.value = state.value.copy(accounts = _accounts.value) } diff --git a/app/src/main/java/com/ivy/wallet/ui/loan/LoansScreen.kt b/app/src/main/java/com/ivy/wallet/ui/loan/LoansScreen.kt index a5694cfcce..d4eb917f48 100644 --- a/app/src/main/java/com/ivy/wallet/ui/loan/LoansScreen.kt +++ b/app/src/main/java/com/ivy/wallet/ui/loan/LoansScreen.kt @@ -12,6 +12,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview @@ -24,7 +25,7 @@ import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style import com.ivy.wallet.R import com.ivy.wallet.domain.data.LoanType -import com.ivy.wallet.domain.data.entity.Loan +import com.ivy.wallet.domain.data.core.Loan import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.LoanDetails import com.ivy.wallet.ui.Loans @@ -92,9 +93,8 @@ private fun BoxWithConstraintsScope.UI( Spacer(Modifier.weight(1f)) NoLoansEmptyState( - emptyStateTitle = "No loans", - emptyStateText = "You don't have any loans.\n" + - "Tap the \"+ Add loan\" to add one." + emptyStateTitle = stringResource(R.string.no_loans), + emptyStateText = stringResource(R.string.no_loans_description) ) Spacer(Modifier.weight(1f)) @@ -166,7 +166,7 @@ private fun Toolbar( .padding(start = 24.dp, end = 16.dp) ) { Text( - text = "Loans", + text = stringResource(R.string.loans), style = UI.typo.h2.style( color = UI.colors.pureInverse, fontWeight = FontWeight.ExtraBold diff --git a/app/src/main/java/com/ivy/wallet/ui/loan/data/DisplayLoan.kt b/app/src/main/java/com/ivy/wallet/ui/loan/data/DisplayLoan.kt index 54a61b7e24..00655cc7b6 100644 --- a/app/src/main/java/com/ivy/wallet/ui/loan/data/DisplayLoan.kt +++ b/app/src/main/java/com/ivy/wallet/ui/loan/data/DisplayLoan.kt @@ -1,7 +1,7 @@ package com.ivy.wallet.ui.loan.data import com.ivy.wallet.domain.data.Reorderable -import com.ivy.wallet.domain.data.entity.Loan +import com.ivy.wallet.domain.data.core.Loan import com.ivy.wallet.utils.getDefaultFIATCurrency data class DisplayLoan( diff --git a/app/src/main/java/com/ivy/wallet/ui/loan/data/DisplayLoanRecord.kt b/app/src/main/java/com/ivy/wallet/ui/loan/data/DisplayLoanRecord.kt index 936613c09e..4f73ac75cb 100644 --- a/app/src/main/java/com/ivy/wallet/ui/loan/data/DisplayLoanRecord.kt +++ b/app/src/main/java/com/ivy/wallet/ui/loan/data/DisplayLoanRecord.kt @@ -1,7 +1,7 @@ package com.ivy.wallet.ui.loan.data -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.LoanRecord +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.LoanRecord data class DisplayLoanRecord( val loanRecord: LoanRecord, diff --git a/app/src/main/java/com/ivy/wallet/ui/loandetails/LoanDetailsScreen.kt b/app/src/main/java/com/ivy/wallet/ui/loandetails/LoanDetailsScreen.kt index 664946a5b3..26c9b9a328 100644 --- a/app/src/main/java/com/ivy/wallet/ui/loandetails/LoanDetailsScreen.kt +++ b/app/src/main/java/com/ivy/wallet/ui/loandetails/LoanDetailsScreen.kt @@ -16,6 +16,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview @@ -29,12 +30,12 @@ import com.ivy.wallet.R import com.ivy.wallet.domain.data.IvyCurrency import com.ivy.wallet.domain.data.LoanType import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.Loan -import com.ivy.wallet.domain.data.entity.LoanRecord -import com.ivy.wallet.domain.logic.model.CreateAccountData -import com.ivy.wallet.domain.logic.model.CreateLoanRecordData -import com.ivy.wallet.domain.logic.model.EditLoanRecordData +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Loan +import com.ivy.wallet.domain.data.core.LoanRecord +import com.ivy.wallet.domain.deprecated.logic.model.CreateAccountData +import com.ivy.wallet.domain.deprecated.logic.model.CreateLoanRecordData +import com.ivy.wallet.domain.deprecated.logic.model.EditLoanRecordData import com.ivy.wallet.ui.ItemStatistic import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.LoanDetails @@ -242,16 +243,16 @@ private fun BoxWithConstraintsScope.UI( DeleteModal( visible = deleteModalVisible, - title = "Confirm deletion", - description = "Note: Deleting this loan will remove it permanently and delete all associated loan records with it.", + title = stringResource(R.string.confirm_deletion), + description = stringResource(R.string.loan_confirm_deletion_description), dismiss = { deleteModalVisible = false } ) { onDeleteLoan() } ProgressModal( - title = "Confirm Account Change", - description = "Please wait, re-calculating all loan records", + title = stringResource(R.string.confirm_account_change), + description = stringResource(R.string.confirm_account_loan_change), visible = waitModalVisible ) } @@ -404,7 +405,7 @@ private fun LoanInfoCard( ) { Text( modifier = Modifier.padding(top = 8.dp, start = 24.dp), - text = "Paid", + text = stringResource(R.string.paid), style = UI.typo.c.style( color = contrastColor, fontWeight = FontWeight.ExtraBold @@ -484,7 +485,7 @@ private fun LoanInfoCard( Text( modifier = Modifier .testTag("left_to_pay"), - text = "${leftToPay.format(baseCurrency)} $baseCurrency left", + text = stringResource(R.string.left_to_pay, leftToPay.format(baseCurrency), baseCurrency), style = UI.typo.nB2.style( color = Gray, fontWeight = FontWeight.ExtraBold @@ -515,7 +516,7 @@ private fun LoanInfoCard( Text( modifier = Modifier.padding(horizontal = 24.dp), - text = "Loan Interest", + text = stringResource(R.string.loan_interest), style = UI.typo.c.style( color = contrastColor, fontWeight = FontWeight.ExtraBold @@ -545,7 +546,7 @@ private fun LoanInfoCard( Text( modifier = Modifier .testTag("interest_paid"), - text = "${loanAmountPaid.format(baseCurrency)} $baseCurrency paid", + text = stringResource(R.string.interest_paid, loanAmountPaid.format(baseCurrency), baseCurrency), style = UI.typo.nB2.style( color = Gray, fontWeight = FontWeight.ExtraBold @@ -572,7 +573,7 @@ private fun LoanInfoCard( .fillMaxWidth() .padding(horizontal = 16.dp) .align(Alignment.CenterHorizontally), - text = "Add record", + text = stringResource(R.string.add_record), shadowAlpha = 0.1f, backgroundGradient = Gradient.solid(contrastColor), textStyle = UI.typo.b2.style( @@ -671,7 +672,7 @@ private fun LoanRecordItem( backgroundGradient = Gradient.solid(loan.color.toComposeColor()), hasGlow = false, iconTint = textIconColor, - text = "Interest", + text = stringResource(R.string.interest), iconStart = getCustomIconIdS( iconName = "currency", defaultIcon = R.drawable.ic_currency @@ -754,7 +755,7 @@ private fun NoLoanRecordsEmptyState() { Spacer(Modifier.height(24.dp)) Text( - text = "No records", + text = stringResource(R.string.no_records), style = UI.typo.b1.style( color = Gray, fontWeight = FontWeight.ExtraBold @@ -765,7 +766,7 @@ private fun NoLoanRecordsEmptyState() { Text( modifier = Modifier.padding(horizontal = 32.dp), - text = "You don't have any records for this loan. Tap \"Add record\" to create one.", + text = stringResource(R.string.no_records_for_the_loan), style = UI.typo.b2.style( color = Gray, fontWeight = FontWeight.Medium, diff --git a/app/src/main/java/com/ivy/wallet/ui/loandetails/LoanDetailsViewModel.kt b/app/src/main/java/com/ivy/wallet/ui/loandetails/LoanDetailsViewModel.kt index 78381571b8..b8f3b7eb0f 100644 --- a/app/src/main/java/com/ivy/wallet/ui/loandetails/LoanDetailsViewModel.kt +++ b/app/src/main/java/com/ivy/wallet/ui/loandetails/LoanDetailsViewModel.kt @@ -3,18 +3,20 @@ package com.ivy.wallet.ui.loandetails import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.ivy.design.navigation.Navigation -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.Loan -import com.ivy.wallet.domain.data.entity.LoanRecord -import com.ivy.wallet.domain.data.entity.Transaction +import com.ivy.wallet.domain.action.account.AccountsAct +import com.ivy.wallet.domain.action.loan.LoanByIdAct +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Loan +import com.ivy.wallet.domain.data.core.LoanRecord +import com.ivy.wallet.domain.data.core.Transaction +import com.ivy.wallet.domain.deprecated.logic.AccountCreator +import com.ivy.wallet.domain.deprecated.logic.LoanCreator +import com.ivy.wallet.domain.deprecated.logic.LoanRecordCreator +import com.ivy.wallet.domain.deprecated.logic.loantrasactions.LoanTransactionsLogic +import com.ivy.wallet.domain.deprecated.logic.model.CreateAccountData +import com.ivy.wallet.domain.deprecated.logic.model.CreateLoanRecordData +import com.ivy.wallet.domain.deprecated.logic.model.EditLoanRecordData import com.ivy.wallet.domain.event.AccountsUpdatedEvent -import com.ivy.wallet.domain.logic.AccountCreator -import com.ivy.wallet.domain.logic.LoanCreator -import com.ivy.wallet.domain.logic.LoanRecordCreator -import com.ivy.wallet.domain.logic.loantrasactions.LoanTransactionsLogic -import com.ivy.wallet.domain.logic.model.CreateAccountData -import com.ivy.wallet.domain.logic.model.CreateLoanRecordData -import com.ivy.wallet.domain.logic.model.EditLoanRecordData import com.ivy.wallet.io.persistence.dao.* import com.ivy.wallet.ui.IvyWalletCtx import com.ivy.wallet.ui.LoanDetails @@ -42,7 +44,9 @@ class LoanDetailsViewModel @Inject constructor( private val accountDao: AccountDao, private val accountCreator: AccountCreator, private val loanTransactionsLogic: LoanTransactionsLogic, - private val nav: Navigation + private val nav: Navigation, + private val accountsAct: AccountsAct, + private val loanByIdAct: LoanByIdAct ) : ViewModel() { private val _baseCurrency = MutableStateFlow("") @@ -87,13 +91,9 @@ class LoanDetailsViewModel @Inject constructor( _baseCurrency.value = it } - _accounts.value = ioThread { - accountDao.findAll() - } + _accounts.value = accountsAct(Unit) - _loan.value = ioThread { - loanDao.findById(id = loanId) - } + _loan.value = loanByIdAct(loanId) loan.value?.let { loan -> _selectedLoanAccount.value = accounts.value.find { @@ -120,7 +120,7 @@ class LoanDetailsViewModel @Inject constructor( ) DisplayLoanRecord( - it, + it.toDomain(), account = account, loanRecordTransaction = trans != null, loanRecordCurrencyCode = account?.currency ?: defaultCurrencyCode, @@ -148,7 +148,7 @@ class LoanDetailsViewModel @Inject constructor( } associatedTransaction = ioThread { - transactionDao.findLoanTransaction(loanId = loan.value!!.id) + transactionDao.findLoanTransaction(loanId = loan.value!!.id)?.toDomain() } associatedTransaction?.let { @@ -296,7 +296,7 @@ class LoanDetailsViewModel @Inject constructor( accountCreator.createAccount(data) { EventBus.getDefault().post(AccountsUpdatedEvent()) - _accounts.value = ioThread { accountDao.findAll() } + _accounts.value = accountsAct(Unit) } TestIdlingResource.decrement() diff --git a/app/src/main/java/com/ivy/wallet/ui/main/MainBottomBar.kt b/app/src/main/java/com/ivy/wallet/ui/main/MainBottomBar.kt index e3ffd78416..df946ad9eb 100644 --- a/app/src/main/java/com/ivy/wallet/ui/main/MainBottomBar.kt +++ b/app/src/main/java/com/ivy/wallet/ui/main/MainBottomBar.kt @@ -19,6 +19,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.layout.layout import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp @@ -97,7 +98,7 @@ fun BoxWithConstraintsScope.BottomBar( ) { Tab( icon = R.drawable.ic_home, - name = "Home", + name = stringResource(R.string.home), selected = tab == MainTab.HOME, selectedColor = Ivy ) { @@ -108,7 +109,7 @@ fun BoxWithConstraintsScope.BottomBar( Tab( icon = R.drawable.ic_accounts, - name = "Accounts", + name = stringResource(R.string.accounts), selected = tab == MainTab.ACCOUNTS, selectedColor = Green ) { @@ -272,7 +273,7 @@ private fun TransactionButtons( .alpha(buttonsShownPercent) .zIndex(200f), iconStart = R.drawable.ic_planned_payments, - text = "Add planned payment", + text = stringResource(R.string.add_planned_payment), solidBackground = true ) { onAddPlannedPayment() @@ -371,7 +372,7 @@ private fun AddIncomeButton( onAddIncome() } .zIndex(200f), - text = "ADD INCOME", + text = stringResource(R.string.add_income_uppercase), style = UI.typo.c.style( color = UI.colors.pureInverse, fontWeight = FontWeight.ExtraBold, @@ -457,7 +458,7 @@ private fun AddExpenseButton( onAddExpense() } .zIndex(200f), - text = "ADD EXPENSE", + text = stringResource(R.string.add_expense_uppercase), style = UI.typo.c.style( color = UI.colors.pureInverse, fontWeight = FontWeight.ExtraBold, @@ -543,7 +544,7 @@ private fun AddTransferButton( onAddTransfer() } .zIndex(200f), - text = "ACCOUNT TRANSFER", + text = stringResource(R.string.account_transfer), style = UI.typo.c.style( color = UI.colors.pureInverse, fontWeight = FontWeight.ExtraBold, diff --git a/app/src/main/java/com/ivy/wallet/ui/main/MainScreen.kt b/app/src/main/java/com/ivy/wallet/ui/main/MainScreen.kt index beb290d435..c56d23df65 100644 --- a/app/src/main/java/com/ivy/wallet/ui/main/MainScreen.kt +++ b/app/src/main/java/com/ivy/wallet/ui/main/MainScreen.kt @@ -9,7 +9,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.lifecycle.viewmodel.compose.viewModel import com.ivy.design.api.navigation import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.logic.model.CreateAccountData +import com.ivy.wallet.domain.deprecated.logic.model.CreateAccountData import com.ivy.wallet.ui.* import com.ivy.wallet.ui.accounts.AccountsTab import com.ivy.wallet.ui.home.HomeTab diff --git a/app/src/main/java/com/ivy/wallet/ui/main/MainViewModel.kt b/app/src/main/java/com/ivy/wallet/ui/main/MainViewModel.kt index 4068c1da28..e4e7906d7c 100644 --- a/app/src/main/java/com/ivy/wallet/ui/main/MainViewModel.kt +++ b/app/src/main/java/com/ivy/wallet/ui/main/MainViewModel.kt @@ -4,11 +4,11 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.ivy.design.navigation.Navigation +import com.ivy.wallet.domain.deprecated.logic.AccountCreator +import com.ivy.wallet.domain.deprecated.logic.currency.ExchangeRatesLogic +import com.ivy.wallet.domain.deprecated.logic.model.CreateAccountData +import com.ivy.wallet.domain.deprecated.sync.IvySync import com.ivy.wallet.domain.event.AccountsUpdatedEvent -import com.ivy.wallet.domain.logic.AccountCreator -import com.ivy.wallet.domain.logic.currency.ExchangeRatesLogic -import com.ivy.wallet.domain.logic.model.CreateAccountData -import com.ivy.wallet.domain.sync.IvySync import com.ivy.wallet.io.persistence.dao.SettingsDao import com.ivy.wallet.ui.IvyWalletCtx import com.ivy.wallet.ui.Main diff --git a/app/src/main/java/com/ivy/wallet/ui/onboarding/OnboardingScreen.kt b/app/src/main/java/com/ivy/wallet/ui/onboarding/OnboardingScreen.kt index 3d16df6d0d..05cced0598 100644 --- a/app/src/main/java/com/ivy/wallet/ui/onboarding/OnboardingScreen.kt +++ b/app/src/main/java/com/ivy/wallet/ui/onboarding/OnboardingScreen.kt @@ -9,10 +9,10 @@ import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.tooling.preview.Preview import androidx.lifecycle.viewmodel.compose.viewModel import com.ivy.wallet.domain.data.IvyCurrency -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.logic.model.CreateAccountData -import com.ivy.wallet.domain.logic.model.CreateCategoryData +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.deprecated.logic.model.CreateAccountData +import com.ivy.wallet.domain.deprecated.logic.model.CreateCategoryData import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.Onboarding import com.ivy.wallet.ui.onboarding.model.AccountBalance diff --git a/app/src/main/java/com/ivy/wallet/ui/onboarding/components/OnboardingToolbar.kt b/app/src/main/java/com/ivy/wallet/ui/onboarding/components/OnboardingToolbar.kt index 851550c730..0971f3c9f1 100644 --- a/app/src/main/java/com/ivy/wallet/ui/onboarding/components/OnboardingToolbar.kt +++ b/app/src/main/java/com/ivy/wallet/ui/onboarding/components/OnboardingToolbar.kt @@ -8,11 +8,13 @@ import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style +import com.ivy.wallet.R import com.ivy.wallet.ui.IvyWalletComponentPreview import com.ivy.wallet.ui.theme.Gray import com.ivy.wallet.ui.theme.components.IvyToolbar @@ -35,7 +37,7 @@ fun OnboardingToolbar( onSkip() } .padding(all = 16.dp), //enlarge click area - text = "Skip", + text = stringResource(R.string.skip), style = UI.typo.b2.style( color = Gray, fontWeight = FontWeight.Bold diff --git a/app/src/main/java/com/ivy/wallet/ui/onboarding/components/Suggestions.kt b/app/src/main/java/com/ivy/wallet/ui/onboarding/components/Suggestions.kt index 549cc6666a..01239cf54b 100644 --- a/app/src/main/java/com/ivy/wallet/ui/onboarding/components/Suggestions.kt +++ b/app/src/main/java/com/ivy/wallet/ui/onboarding/components/Suggestions.kt @@ -9,15 +9,16 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style import com.ivy.wallet.R -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.logic.model.CreateAccountData -import com.ivy.wallet.domain.logic.model.CreateCategoryData +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.deprecated.logic.model.CreateAccountData +import com.ivy.wallet.domain.deprecated.logic.model.CreateCategoryData import com.ivy.wallet.ui.IvyWalletComponentPreview import com.ivy.wallet.ui.theme.components.IvyIcon import com.ivy.wallet.ui.theme.components.WrapContentRow @@ -117,7 +118,7 @@ private fun AddNewButton( Text( modifier = Modifier.padding(vertical = 16.dp), - text = "Add new", + text = stringResource(R.string.add_new), style = UI.typo.b2.style( color = UI.colors.pure, fontWeight = FontWeight.Bold diff --git a/app/src/main/java/com/ivy/wallet/ui/onboarding/model/AccountBalance.kt b/app/src/main/java/com/ivy/wallet/ui/onboarding/model/AccountBalance.kt index cbf19e34d7..36049d07d4 100644 --- a/app/src/main/java/com/ivy/wallet/ui/onboarding/model/AccountBalance.kt +++ b/app/src/main/java/com/ivy/wallet/ui/onboarding/model/AccountBalance.kt @@ -1,6 +1,6 @@ package com.ivy.wallet.ui.onboarding.model -import com.ivy.wallet.domain.data.entity.Account +import com.ivy.wallet.domain.data.core.Account data class AccountBalance( val account: Account, diff --git a/app/src/main/java/com/ivy/wallet/ui/onboarding/model/FromToTimeRange.kt b/app/src/main/java/com/ivy/wallet/ui/onboarding/model/FromToTimeRange.kt index 87495eefc9..f99c454cc0 100644 --- a/app/src/main/java/com/ivy/wallet/ui/onboarding/model/FromToTimeRange.kt +++ b/app/src/main/java/com/ivy/wallet/ui/onboarding/model/FromToTimeRange.kt @@ -1,7 +1,7 @@ package com.ivy.wallet.ui.onboarding.model -import com.ivy.wallet.domain.data.entity.Transaction -import com.ivy.wallet.domain.fp.data.ClosedTimeRange +import com.ivy.wallet.domain.data.core.Transaction +import com.ivy.wallet.domain.pure.data.ClosedTimeRange import com.ivy.wallet.utils.* import java.time.LocalDateTime diff --git a/app/src/main/java/com/ivy/wallet/ui/onboarding/steps/OnboardingAccounts.kt b/app/src/main/java/com/ivy/wallet/ui/onboarding/steps/OnboardingAccounts.kt index ec857356e8..cb68f637ad 100644 --- a/app/src/main/java/com/ivy/wallet/ui/onboarding/steps/OnboardingAccounts.kt +++ b/app/src/main/java/com/ivy/wallet/ui/onboarding/steps/OnboardingAccounts.kt @@ -12,6 +12,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -21,8 +22,8 @@ import com.ivy.design.api.navigation import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style import com.ivy.wallet.R -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.logic.model.CreateAccountData +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.deprecated.logic.model.CreateAccountData import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.Paywall import com.ivy.wallet.ui.ivyWalletCtx @@ -78,7 +79,7 @@ fun BoxWithConstraintsScope.OnboardingAccounts( Text( modifier = Modifier.padding(horizontal = 32.dp), - text = "Add accounts", + text = stringResource(R.string.add_accounts), style = UI.typo.h2.style( fontWeight = FontWeight.Black ) @@ -130,7 +131,7 @@ fun BoxWithConstraintsScope.OnboardingAccounts( Text( modifier = Modifier.padding(horizontal = 32.dp), - text = "Suggestions", + text = stringResource(R.string.suggestion), style = UI.typo.b1.style( fontWeight = FontWeight.ExtraBold ) @@ -173,7 +174,7 @@ fun BoxWithConstraintsScope.OnboardingAccounts( .navigationBarsPadding() .padding(bottom = 20.dp), - text = "Next", + text = stringResource(R.string.next), textColor = White, backgroundGradient = GradientIvy, hasNext = true, diff --git a/app/src/main/java/com/ivy/wallet/ui/onboarding/steps/OnboardingCategories.kt b/app/src/main/java/com/ivy/wallet/ui/onboarding/steps/OnboardingCategories.kt index d30ecd1840..345e4ed417 100644 --- a/app/src/main/java/com/ivy/wallet/ui/onboarding/steps/OnboardingCategories.kt +++ b/app/src/main/java/com/ivy/wallet/ui/onboarding/steps/OnboardingCategories.kt @@ -15,6 +15,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -24,8 +25,8 @@ import com.ivy.design.api.navigation import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style import com.ivy.wallet.R -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.logic.model.CreateCategoryData +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.deprecated.logic.model.CreateCategoryData import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.onboarding.components.OnboardingProgressSlider import com.ivy.wallet.ui.onboarding.components.OnboardingToolbar @@ -73,7 +74,7 @@ fun BoxWithConstraintsScope.OnboardingCategories( Text( modifier = Modifier.padding(horizontal = 32.dp), - text = "Add categories", + text = stringResource(R.string.add_categories), style = UI.typo.h2.style( fontWeight = FontWeight.Black ) @@ -121,7 +122,7 @@ fun BoxWithConstraintsScope.OnboardingCategories( Text( modifier = Modifier.padding(horizontal = 32.dp), - text = "Suggestions", + text = stringResource(R.string.suggestions), style = UI.typo.b1.style( fontWeight = FontWeight.ExtraBold ) @@ -162,7 +163,7 @@ fun BoxWithConstraintsScope.OnboardingCategories( .navigationBarsPadding() .padding(bottom = 20.dp), - text = "Finish", + text = stringResource(R.string.finish), textColor = White, backgroundGradient = GradientIvy, hasNext = false, diff --git a/app/src/main/java/com/ivy/wallet/ui/onboarding/steps/OnboardingSetCurrency.kt b/app/src/main/java/com/ivy/wallet/ui/onboarding/steps/OnboardingSetCurrency.kt index 0646629ffc..8c4c140f27 100644 --- a/app/src/main/java/com/ivy/wallet/ui/onboarding/steps/OnboardingSetCurrency.kt +++ b/app/src/main/java/com/ivy/wallet/ui/onboarding/steps/OnboardingSetCurrency.kt @@ -5,6 +5,7 @@ import androidx.compose.material.Text import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -13,6 +14,7 @@ import com.google.accompanist.insets.statusBarsPadding import com.ivy.design.api.navigation import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style +import com.ivy.wallet.R import com.ivy.wallet.domain.data.IvyCurrency import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.theme.GradientIvy @@ -56,7 +58,7 @@ fun BoxWithConstraintsScope.OnboardingSetCurrency( Text( modifier = Modifier.padding(horizontal = 32.dp), - text = "Set currency", + text = stringResource(R.string.set_currency), style = UI.typo.h2.style( fontWeight = FontWeight.Black ) @@ -92,7 +94,7 @@ fun BoxWithConstraintsScope.OnboardingSetCurrency( .navigationBarsPadding() .padding(bottom = 20.dp), - text = "Set", + text = stringResource(R.string.set), textColor = White, backgroundGradient = GradientIvy, hasNext = true, diff --git a/app/src/main/java/com/ivy/wallet/ui/onboarding/steps/OnboardingSplashLogin.kt b/app/src/main/java/com/ivy/wallet/ui/onboarding/steps/OnboardingSplashLogin.kt index 75d1cae573..640bce1293 100644 --- a/app/src/main/java/com/ivy/wallet/ui/onboarding/steps/OnboardingSplashLogin.kt +++ b/app/src/main/java/com/ivy/wallet/ui/onboarding/steps/OnboardingSplashLogin.kt @@ -20,6 +20,7 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.layout import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.FontWeight @@ -181,7 +182,7 @@ fun BoxWithConstraintsScope.OnboardingSplashLogin( ivyContext = ivyContext, percentTransition = percentTransition ), - text = "Your personal money manager", + text = stringResource(R.string.your_personal_money_manager), style = UI.typo.b2.style( color = UI.colors.pureInverse, fontWeight = FontWeight.SemiBold @@ -203,7 +204,7 @@ fun BoxWithConstraintsScope.OnboardingSplashLogin( } .padding(vertical = 8.dp) .padding(end = 8.dp), - text = "#opensource", + text = stringResource(R.string.opensource), style = UI.typo.c.style( color = Green, fontWeight = FontWeight.Bold @@ -261,10 +262,10 @@ private fun LoginSection( LoginButton( text = when (opGoogleSignIn) { - is OpResult.Failure -> "Error. Try again: ${opGoogleSignIn.error()}" - OpResult.Loading -> "Signing in..." - is OpResult.Success -> "Success!" - null -> "Login with Google" + is OpResult.Failure -> stringResource(R.string.google_error_try_again, opGoogleSignIn.error()) + OpResult.Loading -> stringResource(R.string.google_signing_in) + is OpResult.Success -> stringResource(R.string.google_signing_in_success) + null -> stringResource(R.string.login_with_google) }, textColor = White, backgroundGradient = GradientRed, @@ -288,7 +289,7 @@ private fun LoginSection( LoginButton( icon = R.drawable.ic_local_account, - text = "Offline account", + text = stringResource(R.string.offline_account), textColor = UI.colors.pureInverse, backgroundGradient = Gradient.solid(UI.colors.medium), hasShadow = false @@ -322,7 +323,7 @@ private fun LoginWithGoogleExplanation() { Column { Text( - text = "SYNC YOUR DATA ON THE IVY CLOUD", + text = stringResource(R.string.sync_data_ivy_cloud), style = UI.typo.c.style( color = Green, fontWeight = FontWeight.ExtraBold @@ -332,7 +333,7 @@ private fun LoginWithGoogleExplanation() { Spacer(Modifier.height(2.dp)) Text( - text = "Data integrity and protection aren't guaranteed!", + text = stringResource(R.string.data_integrity_protection_warning), style = UI.typo.c.style( color = UI.colors.pureInverse, fontWeight = FontWeight.Medium @@ -346,7 +347,7 @@ private fun LoginWithGoogleExplanation() { private fun LocalAccountExplanation() { Text( modifier = Modifier.padding(start = 32.dp), - text = "OR ENTER WITH OFFLINE ACCOUNT", + text = stringResource(R.string.or_enter_with_offline_account), style = UI.typo.c.style( color = Gray, fontWeight = FontWeight.ExtraBold @@ -357,7 +358,7 @@ private fun LocalAccountExplanation() { Text( modifier = Modifier.padding(start = 32.dp, end = 32.dp), - text = "Your data will be saved locally (only on your phone) and won't be synced with the cloud. You risk losing it if you uninstall the app or change your device. You can always activate sync later if you decide to.", + text = stringResource(R.string.offline_warning), style = UI.typo.c.style( color = Gray, fontWeight = FontWeight.Medium @@ -367,9 +368,9 @@ private fun LocalAccountExplanation() { @Composable private fun PrivacyPolicyAndTC() { - val terms = "Terms & Conditions" - val privacy = "Privacy Policy" - val text = "By signing in, you agree with our $terms and $privacy." + val terms = stringResource(R.string.terms_conditions) + val privacy = stringResource(R.string.privacy_policy) + val text = stringResource(R.string.by_signing_in, terms, privacy) val tcStart = text.indexOf(terms) val tcEnd = tcStart + terms.length diff --git a/app/src/main/java/com/ivy/wallet/ui/onboarding/steps/OnboardingType.kt b/app/src/main/java/com/ivy/wallet/ui/onboarding/steps/OnboardingType.kt index e90c616dca..1a0504715d 100644 --- a/app/src/main/java/com/ivy/wallet/ui/onboarding/steps/OnboardingType.kt +++ b/app/src/main/java/com/ivy/wallet/ui/onboarding/steps/OnboardingType.kt @@ -7,6 +7,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -48,7 +49,7 @@ fun OnboardingType( Text( modifier = Modifier.padding(horizontal = 32.dp), - text = "Import CSV file", + text = stringResource(R.string.import_csv_file), style = UI.typo.h2.style( fontWeight = FontWeight.Black ) @@ -58,7 +59,7 @@ fun OnboardingType( Text( modifier = Modifier.padding(horizontal = 32.dp), - text = "from Ivy or another app", + text = stringResource(R.string.from_ivy_or_another_app), style = UI.typo.nB2.style( fontWeight = FontWeight.Bold, color = Gray @@ -84,7 +85,7 @@ fun OnboardingType( Text( modifier = Modifier.padding(horizontal = 32.dp), - text = "Importing a backup file from another can take up to 5 min. You can always import your data later if you want to.", + text = stringResource(R.string.importing_another_time_warning), style = UI.typo.b2.style( fontWeight = FontWeight.Bold ) @@ -95,7 +96,7 @@ fun OnboardingType( IvyOutlinedButtonFillMaxWidth( modifier = Modifier .padding(horizontal = 16.dp), - text = "Import backup file", + text = stringResource(R.string.import_backup_file), iconStart = R.drawable.ic_export_csv, iconTint = Green, textColor = Green @@ -109,7 +110,7 @@ fun OnboardingType( modifier = Modifier .padding(horizontal = 16.dp) .fillMaxWidth(), - text = "Start fresh", + text = stringResource(R.string.start_fresh), textColor = White, backgroundGradient = GradientIvy, hasNext = true, diff --git a/app/src/main/java/com/ivy/wallet/ui/onboarding/steps/archived/OnboardingPrivacyTC.kt b/app/src/main/java/com/ivy/wallet/ui/onboarding/steps/archived/OnboardingPrivacyTC.kt index a6d4496ee2..2a88e58e3f 100644 --- a/app/src/main/java/com/ivy/wallet/ui/onboarding/steps/archived/OnboardingPrivacyTC.kt +++ b/app/src/main/java/com/ivy/wallet/ui/onboarding/steps/archived/OnboardingPrivacyTC.kt @@ -17,6 +17,7 @@ import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -67,7 +68,7 @@ fun OnboardingPrivacyTC( Text( modifier = Modifier.padding(start = 32.dp), - text = "Privacy and\ndata collection", + text = stringResource(R.string.privacy_and_data_collection), style = UI.typo.h2.style( fontWeight = FontWeight.ExtraBold ) @@ -87,8 +88,8 @@ fun OnboardingPrivacyTC( var privacyAccepted by remember { mutableStateOf(false) } SwipeToAgree( - swipeToAgreeText = "Swipe to agree with our Terms and conditions", - agreedText = "Agreed with our Terms and conditions" + swipeToAgreeText = stringResource(R.string.swipe_to_agree_terms_conditions), + agreedText = stringResource(R.string.agreed_terms_conditions) ) { tcAccepted = it } @@ -96,8 +97,8 @@ fun OnboardingPrivacyTC( Spacer(Modifier.height(24.dp)) SwipeToAgree( - swipeToAgreeText = "Swipe to agree with our Privacy policy", - agreedText = "Agreed with our Privacy policy" + swipeToAgreeText = stringResource(R.string.swipe_to_agree_privacy_policy), + agreedText = stringResource(R.string.agreed_privacy_policy) ) { privacyAccepted = it } @@ -116,14 +117,14 @@ private fun URLsRow() { Spacer(Modifier.width(32.dp)) TextLink( - text = "Terms and conditions", + text = stringResource(R.string.terms_and_conditions), url = Constants.URL_TC ) Spacer(Modifier.width(36.dp)) TextLink( - text = "Privacy policy", + text = stringResource(R.string.privacy_policy), url = Constants.URL_PRIVACY_POLICY ) @@ -138,7 +139,7 @@ private fun LongText() { start = 32.dp, end = 48.dp ), - text = "Track your income, expenses and budget with Ivy.\n\nIntuitive UI, recurring and planned payments, manage multiple accounts, organize transactions in categories, meaningful statistics, export to CSV and so much more.", + text = stringResource(R.string.wallet_description), style = UI.typo.b2.style( fontWeight = FontWeight.Medium ) diff --git a/app/src/main/java/com/ivy/wallet/ui/onboarding/steps/archived/OnboardingSetName.kt b/app/src/main/java/com/ivy/wallet/ui/onboarding/steps/archived/OnboardingSetName.kt index fa299f4d88..eeb41e6449 100644 --- a/app/src/main/java/com/ivy/wallet/ui/onboarding/steps/archived/OnboardingSetName.kt +++ b/app/src/main/java/com/ivy/wallet/ui/onboarding/steps/archived/OnboardingSetName.kt @@ -13,6 +13,7 @@ import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardCapitalization @@ -76,7 +77,7 @@ fun OnboardingSetName( Text( modifier = Modifier.padding(horizontal = 32.dp), - text = "Enter your name\nto personalize your\nwallet", + text = stringResource(R.string.enter_your_name), style = UI.typo.h2.style( fontWeight = FontWeight.ExtraBold ) @@ -99,7 +100,7 @@ fun OnboardingSetName( .fillMaxWidth() .focusRequester(nameFocus), value = nameTextField, - hint = "What's your name?", + hint = stringResource(R.string.what_is_your_name), keyboardOptions = KeyboardOptions( capitalization = KeyboardCapitalization.Words, autoCorrect = false, @@ -123,7 +124,7 @@ fun OnboardingSetName( Modifier .padding(horizontal = 24.dp) .fillMaxWidth(), - text = "Enter", + text = stringResource(R.string.enter), textColor = White, backgroundGradient = GradientIvy, hasNext = true, diff --git a/app/src/main/java/com/ivy/wallet/ui/onboarding/viewmodel/OnboardingRouter.kt b/app/src/main/java/com/ivy/wallet/ui/onboarding/viewmodel/OnboardingRouter.kt index 04a36e53d7..0c24b9a501 100644 --- a/app/src/main/java/com/ivy/wallet/ui/onboarding/viewmodel/OnboardingRouter.kt +++ b/app/src/main/java/com/ivy/wallet/ui/onboarding/viewmodel/OnboardingRouter.kt @@ -4,14 +4,14 @@ import androidx.lifecycle.MutableLiveData import com.ivy.design.navigation.Navigation import com.ivy.wallet.domain.data.IvyCurrency import com.ivy.wallet.domain.data.analytics.AnalyticsEvent -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.logic.LogoutLogic -import com.ivy.wallet.domain.logic.PreloadDataLogic -import com.ivy.wallet.domain.logic.currency.ExchangeRatesLogic -import com.ivy.wallet.domain.logic.model.CreateAccountData -import com.ivy.wallet.domain.logic.model.CreateCategoryData -import com.ivy.wallet.domain.logic.notification.TransactionReminderLogic -import com.ivy.wallet.domain.sync.IvySync +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.deprecated.logic.LogoutLogic +import com.ivy.wallet.domain.deprecated.logic.PreloadDataLogic +import com.ivy.wallet.domain.deprecated.logic.currency.ExchangeRatesLogic +import com.ivy.wallet.domain.deprecated.logic.model.CreateAccountData +import com.ivy.wallet.domain.deprecated.logic.model.CreateCategoryData +import com.ivy.wallet.domain.deprecated.logic.notification.TransactionReminderLogic +import com.ivy.wallet.domain.deprecated.sync.IvySync import com.ivy.wallet.io.network.IvyAnalytics import com.ivy.wallet.io.persistence.SharedPrefs import com.ivy.wallet.io.persistence.dao.AccountDao @@ -246,7 +246,7 @@ class OnboardingRouter( } private suspend fun routeToCategories() { - _categories.value = ioThread { categoryDao.findAll() }!! + _categories.value = ioThread { categoryDao.findAll().map { it.toDomain() } }!! _categorySuggestions.value = preloadDataLogic.categorySuggestions() _state.value = OnboardingState.CATEGORIES diff --git a/app/src/main/java/com/ivy/wallet/ui/onboarding/viewmodel/OnboardingViewModel.kt b/app/src/main/java/com/ivy/wallet/ui/onboarding/viewmodel/OnboardingViewModel.kt index 2ecd126936..42b65c3d1d 100644 --- a/app/src/main/java/com/ivy/wallet/ui/onboarding/viewmodel/OnboardingViewModel.kt +++ b/app/src/main/java/com/ivy/wallet/ui/onboarding/viewmodel/OnboardingViewModel.kt @@ -5,16 +5,18 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.ivy.design.l0_system.Theme import com.ivy.design.navigation.Navigation +import com.ivy.wallet.domain.action.account.AccountsAct +import com.ivy.wallet.domain.action.category.CategoriesAct import com.ivy.wallet.domain.data.IvyCurrency -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.data.entity.Settings -import com.ivy.wallet.domain.logic.* -import com.ivy.wallet.domain.logic.currency.ExchangeRatesLogic -import com.ivy.wallet.domain.logic.model.CreateAccountData -import com.ivy.wallet.domain.logic.model.CreateCategoryData -import com.ivy.wallet.domain.logic.notification.TransactionReminderLogic -import com.ivy.wallet.domain.sync.IvySync +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.data.core.Settings +import com.ivy.wallet.domain.deprecated.logic.* +import com.ivy.wallet.domain.deprecated.logic.currency.ExchangeRatesLogic +import com.ivy.wallet.domain.deprecated.logic.model.CreateAccountData +import com.ivy.wallet.domain.deprecated.logic.model.CreateCategoryData +import com.ivy.wallet.domain.deprecated.logic.notification.TransactionReminderLogic +import com.ivy.wallet.domain.deprecated.sync.IvySync import com.ivy.wallet.io.network.FCMClient import com.ivy.wallet.io.network.IvyAnalytics import com.ivy.wallet.io.network.IvySession @@ -48,6 +50,9 @@ class OnboardingViewModel @Inject constructor( private val categoryDao: CategoryDao, private val accountCreator: AccountCreator, + private val accountsAct: AccountsAct, + private val categoriesAct: CategoriesAct, + //Only OnboardingRouter stuff sharedPrefs: SharedPrefs, ivySync: IvySync, @@ -132,9 +137,9 @@ class OnboardingViewModel @Inject constructor( Settings( theme = if (isSystemDarkMode) Theme.DARK else Theme.LIGHT, name = "", - currency = defaultCurrency.code, - bufferAmount = 1000.0 - ) + baseCurrency = defaultCurrency.code, + bufferAmount = 1000.0.toBigDecimal() + ).toEntity() ) } @@ -284,11 +289,11 @@ class OnboardingViewModel @Inject constructor( } private suspend fun accountsWithBalance(): List = ioThread { - accountDao.findAll() + accountsAct(Unit) .map { AccountBalance( account = it, - balance = accountLogic.calculateAccountBalance(it) + balance = ioThread { accountLogic.calculateAccountBalance(it) } ) } } @@ -320,7 +325,7 @@ class OnboardingViewModel @Inject constructor( TestIdlingResource.increment() categoryCreator.editCategory(updatedCategory) { - _categories.value = ioThread { categoryDao.findAll() }!! + _categories.value = categoriesAct(Unit)!! } TestIdlingResource.decrement() @@ -332,7 +337,7 @@ class OnboardingViewModel @Inject constructor( TestIdlingResource.increment() categoryCreator.createCategory(data) { - _categories.value = ioThread { categoryDao.findAll() }!! + _categories.value = categoriesAct(Unit)!! } TestIdlingResource.decrement() diff --git a/app/src/main/java/com/ivy/wallet/ui/paywall/PaywallScreen.kt b/app/src/main/java/com/ivy/wallet/ui/paywall/PaywallScreen.kt index e76c588830..650c1730fb 100644 --- a/app/src/main/java/com/ivy/wallet/ui/paywall/PaywallScreen.kt +++ b/app/src/main/java/com/ivy/wallet/ui/paywall/PaywallScreen.kt @@ -25,10 +25,10 @@ import com.ivy.wallet.Constants import com.ivy.wallet.R import com.ivy.wallet.android.billing.Plan import com.ivy.wallet.android.billing.PlanType -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.Budget -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.data.entity.Loan +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Budget +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.data.core.Loan import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.Paywall import com.ivy.wallet.ui.RootActivity diff --git a/app/src/main/java/com/ivy/wallet/ui/paywall/PaywallViewModel.kt b/app/src/main/java/com/ivy/wallet/ui/paywall/PaywallViewModel.kt index b76148cc13..e91de323f9 100644 --- a/app/src/main/java/com/ivy/wallet/ui/paywall/PaywallViewModel.kt +++ b/app/src/main/java/com/ivy/wallet/ui/paywall/PaywallViewModel.kt @@ -7,21 +7,20 @@ import com.android.billingclient.api.Purchase import com.ivy.wallet.android.billing.IvyBilling import com.ivy.wallet.android.billing.Plan import com.ivy.wallet.android.billing.PlanType +import com.ivy.wallet.domain.action.account.AccountsAct +import com.ivy.wallet.domain.action.budget.BudgetsAct +import com.ivy.wallet.domain.action.category.CategoriesAct +import com.ivy.wallet.domain.action.loan.LoansAct import com.ivy.wallet.domain.data.analytics.AnalyticsEvent -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.Budget -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.data.entity.Loan -import com.ivy.wallet.domain.logic.PaywallLogic +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Budget +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.data.core.Loan +import com.ivy.wallet.domain.deprecated.logic.PaywallLogic import com.ivy.wallet.io.network.IvyAnalytics -import com.ivy.wallet.io.persistence.dao.AccountDao -import com.ivy.wallet.io.persistence.dao.BudgetDao -import com.ivy.wallet.io.persistence.dao.CategoryDao -import com.ivy.wallet.io.persistence.dao.LoanDao import com.ivy.wallet.ui.Paywall import com.ivy.wallet.ui.RootActivity import com.ivy.wallet.utils.asLiveData -import com.ivy.wallet.utils.ioThread import com.ivy.wallet.utils.sendToCrashlytics import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch @@ -33,10 +32,10 @@ class PaywallViewModel @Inject constructor( private val ivyBilling: IvyBilling, private val paywallLogic: PaywallLogic, private val ivyAnalytics: IvyAnalytics, - private val categoryDao: CategoryDao, - private val accountDao: AccountDao, - private val budgetDao: BudgetDao, - private val loanDao: LoanDao + private val categoriesAct: CategoriesAct, + private val accountsAct: AccountsAct, + private val budgetsAct: BudgetsAct, + private val loansAct: LoansAct ) : ViewModel() { private val _plans = MutableLiveData>() @@ -90,10 +89,10 @@ class PaywallViewModel @Inject constructor( ) viewModelScope.launch { - _categories.value = ioThread { categoryDao.findAll() }!! - _accounts.value = ioThread { accountDao.findAll() }!! - _budgets.value = ioThread { budgetDao.findAll() }!! - _loans.value = ioThread { loanDao.findAll() }!! + _categories.value = categoriesAct(Unit)!! + _accounts.value = accountsAct(Unit)!! + _budgets.value = budgetsAct(Unit)!! + _loans.value = loansAct(Unit)!! ivyAnalytics.logEvent( when (screen.paywallReason) { diff --git a/app/src/main/java/com/ivy/wallet/ui/planned/edit/EditPlannedScreen.kt b/app/src/main/java/com/ivy/wallet/ui/planned/edit/EditPlannedScreen.kt index 4fcc8b3b5b..5c59e1b4d1 100644 --- a/app/src/main/java/com/ivy/wallet/ui/planned/edit/EditPlannedScreen.kt +++ b/app/src/main/java/com/ivy/wallet/ui/planned/edit/EditPlannedScreen.kt @@ -9,18 +9,20 @@ import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.google.accompanist.insets.navigationBarsPadding import com.google.accompanist.insets.statusBarsPadding +import com.ivy.wallet.R import com.ivy.wallet.domain.data.IntervalType import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.logic.model.CreateAccountData -import com.ivy.wallet.domain.logic.model.CreateCategoryData +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.deprecated.logic.model.CreateAccountData +import com.ivy.wallet.domain.deprecated.logic.model.CreateCategoryData import com.ivy.wallet.ui.EditPlanned import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.edit.core.* @@ -357,15 +359,15 @@ private fun BoxWithConstraintsScope.UI( DeleteModal( visible = deleteTrnModalVisible, - title = "Confirm deletion", - description = "Deleting this planned payment will delete all non-paid upcoming or overdue transactions associated with it.", + title = stringResource(R.string.confirm_deletion), + description = stringResource(R.string.planned_payment_confirm_deletion_description), dismiss = { deleteTrnModalVisible = false } ) { onDelete() } ChangeTransactionTypeModal( - title = "Set payment type", + title = stringResource(R.string.set_payment_type), visible = changeTransactionTypeModalVisible, includeTransferType = false, initialType = type, diff --git a/app/src/main/java/com/ivy/wallet/ui/planned/edit/EditPlannedViewModel.kt b/app/src/main/java/com/ivy/wallet/ui/planned/edit/EditPlannedViewModel.kt index 3c30048c24..5273de8870 100644 --- a/app/src/main/java/com/ivy/wallet/ui/planned/edit/EditPlannedViewModel.kt +++ b/app/src/main/java/com/ivy/wallet/ui/planned/edit/EditPlannedViewModel.kt @@ -4,19 +4,21 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.ivy.design.navigation.Navigation +import com.ivy.wallet.domain.action.account.AccountsAct +import com.ivy.wallet.domain.action.category.CategoriesAct import com.ivy.wallet.domain.data.IntervalType import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.data.entity.PlannedPaymentRule +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.data.core.PlannedPaymentRule +import com.ivy.wallet.domain.deprecated.logic.AccountCreator +import com.ivy.wallet.domain.deprecated.logic.CategoryCreator +import com.ivy.wallet.domain.deprecated.logic.PlannedPaymentsGenerator +import com.ivy.wallet.domain.deprecated.logic.model.CreateAccountData +import com.ivy.wallet.domain.deprecated.logic.model.CreateCategoryData +import com.ivy.wallet.domain.deprecated.sync.item.TransactionSync +import com.ivy.wallet.domain.deprecated.sync.uploader.PlannedPaymentRuleUploader import com.ivy.wallet.domain.event.AccountsUpdatedEvent -import com.ivy.wallet.domain.logic.AccountCreator -import com.ivy.wallet.domain.logic.CategoryCreator -import com.ivy.wallet.domain.logic.PlannedPaymentsGenerator -import com.ivy.wallet.domain.logic.model.CreateAccountData -import com.ivy.wallet.domain.logic.model.CreateCategoryData -import com.ivy.wallet.domain.sync.item.TransactionSync -import com.ivy.wallet.domain.sync.uploader.PlannedPaymentRuleUploader import com.ivy.wallet.io.persistence.dao.* import com.ivy.wallet.ui.EditPlanned import com.ivy.wallet.ui.IvyWalletCtx @@ -42,7 +44,9 @@ class EditPlannedViewModel @Inject constructor( private val plannedPaymentRuleUploader: PlannedPaymentRuleUploader, private val plannedPaymentsGenerator: PlannedPaymentsGenerator, private val categoryCreator: CategoryCreator, - private val accountCreator: AccountCreator + private val accountCreator: AccountCreator, + private val accountsAct: AccountsAct, + private val categoriesAct: CategoriesAct ) : ViewModel() { private val _transactionType = MutableLiveData() @@ -95,19 +99,19 @@ class EditPlannedViewModel @Inject constructor( editMode = screen.plannedPaymentRuleId != null - val accounts = ioThread { accountDao.findAll() }!! + val accounts = accountsAct(Unit) if (accounts.isEmpty()) { nav.back() return@launch } _accounts.value = accounts - _categories.value = ioThread { categoryDao.findAll() }!! + _categories.value = categoriesAct(Unit)!! reset() loadedRule = screen.plannedPaymentRuleId?.let { - ioThread { plannedPaymentRuleDao.findById(it)!! } + ioThread { plannedPaymentRuleDao.findById(it)!!.toDomain() } } ?: PlannedPaymentRule( startDate = null, intervalN = null, @@ -137,10 +141,10 @@ class EditPlannedViewModel @Inject constructor( _intervalType.value = rule.intervalType _initialTitle.value = rule.title _description.value = rule.description - val selectedAccount = ioThread { accountDao.findById(rule.accountId)!! } + val selectedAccount = ioThread { accountDao.findById(rule.accountId)!!.toDomain() } _account.value = selectedAccount _category.value = rule.categoryId?.let { - ioThread { categoryDao.findById(rule.categoryId) } + ioThread { categoryDao.findById(rule.categoryId)?.toDomain() } } _amount.value = rule.amount @@ -262,7 +266,7 @@ class EditPlannedViewModel @Inject constructor( isSynced = false ) - plannedPaymentRuleDao.save(loadedRule()) + plannedPaymentRuleDao.save(loadedRule().toEntity()) plannedPaymentsGenerator.generate(loadedRule()) } @@ -327,7 +331,7 @@ class EditPlannedViewModel @Inject constructor( fun createCategory(data: CreateCategoryData) { viewModelScope.launch { categoryCreator.createCategory(data) { - _categories.value = ioThread { categoryDao.findAll() }!! + _categories.value = categoriesAct(Unit)!! onCategoryChanged(it) } @@ -338,7 +342,7 @@ class EditPlannedViewModel @Inject constructor( viewModelScope.launch { accountCreator.createAccount(data) { EventBus.getDefault().post(AccountsUpdatedEvent()) - _accounts.value = ioThread { accountDao.findAll() }!! + _accounts.value = accountsAct(Unit)!! } } } diff --git a/app/src/main/java/com/ivy/wallet/ui/planned/edit/RecurringRule.kt b/app/src/main/java/com/ivy/wallet/ui/planned/edit/RecurringRule.kt index eef5fdc675..f8fe902beb 100644 --- a/app/src/main/java/com/ivy/wallet/ui/planned/edit/RecurringRule.kt +++ b/app/src/main/java/com/ivy/wallet/ui/planned/edit/RecurringRule.kt @@ -8,6 +8,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -52,7 +53,7 @@ fun RecurringRule( } else { AddPrimaryAttributeButton( icon = R.drawable.ic_planned_payments, - text = "Add planned date of payment", + text = stringResource(R.string.add_planned_date_payment), onClick = onShowRecurringRuleModal ) } @@ -94,7 +95,7 @@ private fun RecurringRuleCard( Column { Text( - text = if (oneTime) "Planned for" else "Planned start at", + text = if (oneTime) stringResource(R.string.planned_for) else stringResource(R.string.planned_start_at), style = UI.typo.b2.style( fontWeight = FontWeight.ExtraBold, color = UI.colors.pureInverse @@ -106,7 +107,7 @@ private fun RecurringRuleCard( val intervalTypeLabel = intervalType.forDisplay(intervalN).uppercaseLocal() Text( - text = "REPEATS EVERY $intervalN $intervalTypeLabel", + text = stringResource(R.string.repeats_every, intervalN, intervalTypeLabel), style = UI.typo.c.style( fontWeight = FontWeight.ExtraBold, color = Orange diff --git a/app/src/main/java/com/ivy/wallet/ui/planned/list/PlannedPaymentCard.kt b/app/src/main/java/com/ivy/wallet/ui/planned/list/PlannedPaymentCard.kt index 1323067ea0..495b063289 100644 --- a/app/src/main/java/com/ivy/wallet/ui/planned/list/PlannedPaymentCard.kt +++ b/app/src/main/java/com/ivy/wallet/ui/planned/list/PlannedPaymentCard.kt @@ -13,6 +13,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -22,9 +23,9 @@ import com.ivy.design.l0_system.style import com.ivy.wallet.R import com.ivy.wallet.domain.data.IntervalType import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.data.entity.PlannedPaymentRule +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.data.core.PlannedPaymentRule import com.ivy.wallet.ui.ItemStatistic import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.theme.Gradient @@ -158,7 +159,7 @@ private fun PlannedPaymentHeaderRow( val account = accounts.find { it.id == plannedPayment.accountId } IvyButton( backgroundGradient = Gradient.solid(UI.colors.pure), - text = account?.name ?: "deleted", + text = account?.name ?: stringResource(R.string.deleted), iconTint = UI.colors.pureInverse, iconStart = getCustomIconIdS(account?.icon, R.drawable.ic_custom_account_s), textStyle = UI.typo.c.style( @@ -196,7 +197,7 @@ private fun RuleTextRow( if (oneTime) { Text( - text = "PLANNED FOR ", + text = stringResource(R.string.planned_for_uppercase), style = UI.typo.nC.style( color = Orange, fontWeight = FontWeight.SemiBold @@ -205,7 +206,7 @@ private fun RuleTextRow( Text( modifier = Modifier.padding(bottom = 1.dp), text = startDate?.toLocalDate()?.formatDateOnlyWithYear()?.uppercaseLocal() - ?: "null", + ?: stringResource(R.string.null_text), style = UI.typo.nC.style( color = Orange, fontWeight = FontWeight.ExtraBold @@ -214,7 +215,7 @@ private fun RuleTextRow( } else { val startDateFormatted = startDate?.toLocalDate()?.formatDateOnly()?.uppercaseLocal() Text( - text = "STARTS $startDateFormatted ", + text = stringResource(R.string.starts_date, startDateFormatted ?: ""), style = UI.typo.nC.style( color = Orange, fontWeight = FontWeight.SemiBold @@ -223,7 +224,7 @@ private fun RuleTextRow( val intervalTypeFormatted = intervalType?.forDisplay(intervalN ?: 0)?.uppercaseLocal() Text( modifier = Modifier.padding(bottom = 1.dp), - text = "REPEATS EVERY $intervalN $intervalTypeFormatted", + text = stringResource(R.string.repeats_every, intervalN ?: 0 , intervalTypeFormatted ?: ""), style = UI.typo.nC.style( color = Orange, fontWeight = FontWeight.ExtraBold diff --git a/app/src/main/java/com/ivy/wallet/ui/planned/list/PlannedPaymentsBottomBar.kt b/app/src/main/java/com/ivy/wallet/ui/planned/list/PlannedPaymentsBottomBar.kt index cbe3d977fd..dc50a864f0 100644 --- a/app/src/main/java/com/ivy/wallet/ui/planned/list/PlannedPaymentsBottomBar.kt +++ b/app/src/main/java/com/ivy/wallet/ui/planned/list/PlannedPaymentsBottomBar.kt @@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.width import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp @@ -42,7 +43,7 @@ fun BoxWithConstraintsScope.PlannedPaymentsBottomBar( IvyOutlinedButton( iconStart = R.drawable.ic_planned_payments, - text = "Add payment", + text = stringResource(R.string.add_payment), solidBackground = true ) { onAdd() diff --git a/app/src/main/java/com/ivy/wallet/ui/planned/list/PlannedPaymentsLazyColumn.kt b/app/src/main/java/com/ivy/wallet/ui/planned/list/PlannedPaymentsLazyColumn.kt index 76ba02e91e..ce62f9d763 100644 --- a/app/src/main/java/com/ivy/wallet/ui/planned/list/PlannedPaymentsLazyColumn.kt +++ b/app/src/main/java/com/ivy/wallet/ui/planned/list/PlannedPaymentsLazyColumn.kt @@ -16,9 +16,9 @@ import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style import com.ivy.design.navigation.Navigation import com.ivy.wallet.R -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.data.entity.PlannedPaymentRule +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.data.core.PlannedPaymentRule import com.ivy.wallet.ui.EditPlanned import com.ivy.wallet.ui.theme.Gray diff --git a/app/src/main/java/com/ivy/wallet/ui/planned/list/PlannedPaymentsScreen.kt b/app/src/main/java/com/ivy/wallet/ui/planned/list/PlannedPaymentsScreen.kt index 6639c8a096..d1e19ec616 100644 --- a/app/src/main/java/com/ivy/wallet/ui/planned/list/PlannedPaymentsScreen.kt +++ b/app/src/main/java/com/ivy/wallet/ui/planned/list/PlannedPaymentsScreen.kt @@ -10,6 +10,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -17,11 +18,12 @@ import androidx.lifecycle.viewmodel.compose.viewModel import com.ivy.design.api.navigation import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style +import com.ivy.wallet.R import com.ivy.wallet.domain.data.IntervalType import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.data.entity.PlannedPaymentRule +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.data.core.PlannedPaymentRule import com.ivy.wallet.ui.EditPlanned import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.PlannedPayments @@ -81,7 +83,7 @@ private fun BoxWithConstraintsScope.UI( Text( modifier = Modifier.padding(start = 24.dp), - text = "Planned payments", + text = stringResource(R.string.planned_payments_inline), style = UI.typo.h2.style( fontWeight = FontWeight.ExtraBold, color = UI.colors.pureInverse diff --git a/app/src/main/java/com/ivy/wallet/ui/planned/list/PlannedPaymentsViewModel.kt b/app/src/main/java/com/ivy/wallet/ui/planned/list/PlannedPaymentsViewModel.kt index c3797f97f4..f48a58e551 100644 --- a/app/src/main/java/com/ivy/wallet/ui/planned/list/PlannedPaymentsViewModel.kt +++ b/app/src/main/java/com/ivy/wallet/ui/planned/list/PlannedPaymentsViewModel.kt @@ -3,13 +3,14 @@ package com.ivy.wallet.ui.planned.list import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.data.entity.PlannedPaymentRule -import com.ivy.wallet.domain.logic.PlannedPaymentsLogic +import com.ivy.wallet.domain.action.account.AccountsAct +import com.ivy.wallet.domain.action.category.CategoriesAct +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.data.core.PlannedPaymentRule +import com.ivy.wallet.domain.deprecated.logic.PlannedPaymentsLogic import com.ivy.wallet.io.persistence.dao.AccountDao import com.ivy.wallet.io.persistence.dao.CategoryDao -import com.ivy.wallet.io.persistence.dao.PlannedPaymentRuleDao import com.ivy.wallet.io.persistence.dao.SettingsDao import com.ivy.wallet.ui.PlannedPayments import com.ivy.wallet.utils.TestIdlingResource @@ -22,10 +23,11 @@ import javax.inject.Inject @HiltViewModel class PlannedPaymentsViewModel @Inject constructor( private val settingsDao: SettingsDao, - private val plannedPaymentRuleDao: PlannedPaymentRuleDao, private val categoryDao: CategoryDao, private val accountDao: AccountDao, - private val plannedPaymentsLogic: PlannedPaymentsLogic + private val plannedPaymentsLogic: PlannedPaymentsLogic, + private val categoriesAct: CategoriesAct, + private val accountsAct: AccountsAct ) : ViewModel() { private val _currency = MutableLiveData() @@ -65,8 +67,8 @@ class PlannedPaymentsViewModel @Inject constructor( _currency.value = settings.currency - _categories.value = ioThread { categoryDao.findAll() }!! - _accounts.value = ioThread { accountDao.findAll() }!! + _categories.value = categoriesAct(Unit)!! + _accounts.value = accountsAct(Unit)!! _oneTime.value = ioThread { plannedPaymentsLogic.oneTime() }!! _oneTimeIncome.value = ioThread { plannedPaymentsLogic.oneTimeIncome() }!! diff --git a/app/src/main/java/com/ivy/wallet/ui/reports/FilterOverlay.kt b/app/src/main/java/com/ivy/wallet/ui/reports/FilterOverlay.kt index 5d86efbeab..cb4b771085 100644 --- a/app/src/main/java/com/ivy/wallet/ui/reports/FilterOverlay.kt +++ b/app/src/main/java/com/ivy/wallet/ui/reports/FilterOverlay.kt @@ -15,6 +15,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.layout.layout +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -24,8 +25,8 @@ import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style import com.ivy.wallet.R import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.Category +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Category import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.ivyWalletCtx import com.ivy.wallet.ui.theme.* @@ -121,7 +122,7 @@ fun BoxWithConstraintsScope.FilterOverlay( modifier = Modifier.padding( start = 32.dp ), - text = "Filter", + text = stringResource(R.string.filter), style = UI.typo.h2.style( fontWeight = FontWeight.ExtraBold ) @@ -136,7 +137,7 @@ fun BoxWithConstraintsScope.FilterOverlay( onSetFilter(null) } .padding(all = 4.dp), //expand click area - text = "Clear filter", + text = stringResource(R.string.clean_filter), style = UI.typo.b2.style( fontWeight = FontWeight.Bold, color = Color.Gray @@ -233,7 +234,7 @@ fun BoxWithConstraintsScope.FilterOverlay( Spacer(Modifier.weight(1f)) IvyButton( - text = "Apply filter", + text = stringResource(R.string.apply_filter), iconStart = R.drawable.ic_filter_xs, backgroundGradient = GradientGreen, padding = 10.dp, @@ -336,7 +337,7 @@ private fun TypeFilter( onSetFilter: (ReportFilter) -> Unit ) { FilterTitleText( - text = "By Type", + text = stringResource(R.string.by_type), active = filter != null && filter.trnTypes.isNotEmpty(), inactiveColor = Red ) @@ -387,9 +388,9 @@ private fun TypeFilterCheckbox( IvyCheckboxWithText( modifier = modifier, text = when (trnType) { - TransactionType.INCOME -> "Incomes" - TransactionType.EXPENSE -> "Expenses" - TransactionType.TRANSFER -> "Account transfers" + TransactionType.INCOME -> stringResource(R.string.incomes) + TransactionType.EXPENSE -> stringResource(R.string.expenses) + TransactionType.TRANSFER -> stringResource(R.string.account_transfers) }, checked = filter != null && filter.trnTypes.contains(trnType), ) { checked -> @@ -418,7 +419,7 @@ private fun PeriodFilter( onShowPeriodChooserModal: () -> Unit ) { FilterTitleText( - text = "Time Period", + text = stringResource(R.string.time_period), active = filter?.period != null, inactiveColor = Red ) @@ -432,7 +433,7 @@ private fun PeriodFilter( iconStart = R.drawable.ic_calendar, text = filter?.period?.toDisplayLong(ivyWalletCtx().startDayOfMonth) ?.capitalizeLocal() - ?: "Select time range", + ?: stringResource(R.string.select_time_range), padding = 12.dp, ) { onShowPeriodChooserModal() @@ -447,7 +448,7 @@ private fun AccountsFilter( onSetFilter: (ReportFilter) -> Unit ) { ListFilterTitle( - text = "Accounts (${filter?.accounts?.size ?: 0})", + text = stringResource(R.string.accounts_number, filter?.accounts?.size ?: 0), active = filter != null && filter.accounts.isNotEmpty(), itemsSelected = filter?.accounts?.size ?: 0, onClearAll = { @@ -518,7 +519,7 @@ private fun CategoriesFilter( val selectedItemsCount = filter?.categories?.size ?: 0 ListFilterTitle( - text = "Categories ($selectedItemsCount)", + text = stringResource(R.string.categories_number, selectedItemsCount), active = filter != null && filter.categories.isNotEmpty(), itemsSelected = selectedItemsCount, onClearAll = { @@ -607,7 +608,7 @@ private fun ListFilterTitle( } } .padding(all = 4.dp), //expand click area - text = if (itemsSelected > 0) "Clear all" else "Select all", + text = if (itemsSelected > 0) stringResource(R.string.clear_all) else stringResource(R.string.select_all), style = UI.typo.b2.style( fontWeight = FontWeight.Bold, color = Color.Gray @@ -679,7 +680,7 @@ private fun AmountFilter( onShowMaxAmountModal: () -> Unit, ) { FilterTitleText( - text = "Amount (optional)", + text = stringResource(R.string.amount_optional), active = filter?.minAmount != null || filter?.maxAmount != null ) @@ -697,7 +698,7 @@ private fun AmountFilter( horizontalAlignment = Alignment.Start ) { Text( - text = "From", + text = stringResource(R.string.from), style = UI.typo.b2.style( fontWeight = FontWeight.ExtraBold ) @@ -718,7 +719,7 @@ private fun AmountFilter( horizontalAlignment = Alignment.End ) { Text( - text = "To", + text = stringResource(R.string.to), style = UI.typo.b2.style( fontWeight = FontWeight.ExtraBold ) @@ -743,7 +744,7 @@ private fun KeywordsFilter( onShowExcludeKeywordModal: () -> Unit, ) { FilterTitleText( - text = "Keywords (optional)", + text = stringResource(R.string.keywords_optional), active = filter != null && (filter.includeKeywords.isNotEmpty() || filter.excludeKeywords.isNotEmpty()) ) @@ -752,7 +753,7 @@ private fun KeywordsFilter( Text( modifier = Modifier.padding(start = 32.dp), - text = "INCLUDES", + text = stringResource(R.string.includes_uppercase), style = UI.typo.b2.style( fontWeight = FontWeight.ExtraBold ) @@ -781,7 +782,7 @@ private fun KeywordsFilter( } } is AddKeywordButton -> { - AddKeywordButton(text = "Add a keyword") { + AddKeywordButton(text = stringResource(R.string.add_keyword)) { onShowIncludeKeywordModal() } } @@ -792,7 +793,7 @@ private fun KeywordsFilter( Text( modifier = Modifier.padding(start = 32.dp), - text = "EXCLUDES", + text = stringResource(R.string.excludes_uppercase), style = UI.typo.b2.style( fontWeight = FontWeight.ExtraBold ) @@ -821,7 +822,7 @@ private fun KeywordsFilter( } } is AddKeywordButton -> { - AddKeywordButton(text = "Add a keyword") { + AddKeywordButton(text = stringResource(R.string.add_keyword)) { onShowExcludeKeywordModal() } } diff --git a/app/src/main/java/com/ivy/wallet/ui/reports/ReportFilter.kt b/app/src/main/java/com/ivy/wallet/ui/reports/ReportFilter.kt index e4ca11a807..09cdc4cf1c 100644 --- a/app/src/main/java/com/ivy/wallet/ui/reports/ReportFilter.kt +++ b/app/src/main/java/com/ivy/wallet/ui/reports/ReportFilter.kt @@ -1,8 +1,8 @@ package com.ivy.wallet.ui.reports import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.Category +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Category import com.ivy.wallet.ui.onboarding.model.TimePeriod import java.util.* diff --git a/app/src/main/java/com/ivy/wallet/ui/reports/ReportScreen.kt b/app/src/main/java/com/ivy/wallet/ui/reports/ReportScreen.kt index fcf69dfae1..f85b27a6c4 100644 --- a/app/src/main/java/com/ivy/wallet/ui/reports/ReportScreen.kt +++ b/app/src/main/java/com/ivy/wallet/ui/reports/ReportScreen.kt @@ -25,8 +25,8 @@ import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style import com.ivy.wallet.R import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.Category +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Category import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.PieChartStatistic import com.ivy.wallet.ui.Report diff --git a/app/src/main/java/com/ivy/wallet/ui/reports/ReportScreenEvent.kt b/app/src/main/java/com/ivy/wallet/ui/reports/ReportScreenEvent.kt index c4360d4e9c..7fb7283d3b 100644 --- a/app/src/main/java/com/ivy/wallet/ui/reports/ReportScreenEvent.kt +++ b/app/src/main/java/com/ivy/wallet/ui/reports/ReportScreenEvent.kt @@ -1,7 +1,7 @@ package com.ivy.wallet.ui.reports import android.content.Context -import com.ivy.wallet.domain.data.entity.Transaction +import com.ivy.wallet.domain.data.core.Transaction sealed class ReportScreenEvent { data class OnFilter(val filter: ReportFilter?) : ReportScreenEvent() diff --git a/app/src/main/java/com/ivy/wallet/ui/reports/ReportScreenState.kt b/app/src/main/java/com/ivy/wallet/ui/reports/ReportScreenState.kt index 7f06456657..4c6fbcb4db 100644 --- a/app/src/main/java/com/ivy/wallet/ui/reports/ReportScreenState.kt +++ b/app/src/main/java/com/ivy/wallet/ui/reports/ReportScreenState.kt @@ -1,9 +1,9 @@ package com.ivy.wallet.ui.reports import com.ivy.wallet.domain.data.TransactionHistoryItem -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.data.entity.Transaction +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.data.core.Transaction import java.util.* data class ReportScreenState( diff --git a/app/src/main/java/com/ivy/wallet/ui/reports/ReportViewModel.kt b/app/src/main/java/com/ivy/wallet/ui/reports/ReportViewModel.kt index b818810f0e..b7b9caa6c1 100644 --- a/app/src/main/java/com/ivy/wallet/ui/reports/ReportViewModel.kt +++ b/app/src/main/java/com/ivy/wallet/ui/reports/ReportViewModel.kt @@ -5,17 +5,26 @@ import androidx.compose.ui.graphics.toArgb import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope import com.ivy.design.navigation.Navigation -import com.ivy.design.viewmodel.IvyViewModel +import com.ivy.fp.filterSuspend +import com.ivy.fp.sumOfSuspend +import com.ivy.fp.viewmodel.IvyViewModel +import com.ivy.wallet.domain.action.account.AccountsAct +import com.ivy.wallet.domain.action.category.CategoriesAct +import com.ivy.wallet.domain.action.exchange.ExchangeAct +import com.ivy.wallet.domain.action.settings.BaseCurrencyAct +import com.ivy.wallet.domain.action.transaction.CalcTrnsIncomeExpenseAct +import com.ivy.wallet.domain.action.transaction.TrnsWithDateDivsAct import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.data.entity.Transaction -import com.ivy.wallet.domain.fp.wallet.withDateDividers -import com.ivy.wallet.domain.logic.PlannedPaymentsLogic -import com.ivy.wallet.domain.logic.WalletLogic -import com.ivy.wallet.domain.logic.csv.ExportCSVLogic -import com.ivy.wallet.domain.logic.currency.ExchangeRatesLogic -import com.ivy.wallet.io.persistence.dao.* +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.data.core.Transaction +import com.ivy.wallet.domain.deprecated.logic.PlannedPaymentsLogic +import com.ivy.wallet.domain.deprecated.logic.csv.ExportCSVLogic +import com.ivy.wallet.domain.pure.exchange.ExchangeData +import com.ivy.wallet.domain.pure.transaction.trnCurrency +import com.ivy.wallet.domain.pure.util.orZero +import com.ivy.wallet.io.persistence.dao.SettingsDao +import com.ivy.wallet.io.persistence.dao.TransactionDao import com.ivy.wallet.ui.IvyWalletCtx import com.ivy.wallet.ui.RootActivity import com.ivy.wallet.ui.onboarding.model.TimePeriod @@ -33,15 +42,16 @@ import javax.inject.Inject class ReportViewModel @Inject constructor( private val plannedPaymentsLogic: PlannedPaymentsLogic, private val settingsDao: SettingsDao, - private val walletLogic: WalletLogic, private val transactionDao: TransactionDao, private val ivyContext: IvyWalletCtx, private val nav: Navigation, - private val accountDao: AccountDao, - private val categoryDao: CategoryDao, - private val exchangeRatesLogic: ExchangeRatesLogic, - private val exchangeRateDao: ExchangeRateDao, - private val exportCSVLogic: ExportCSVLogic + private val exportCSVLogic: ExportCSVLogic, + private val exchangeAct: ExchangeAct, + private val accountsAct: AccountsAct, + private val categoriesAct: CategoriesAct, + private val trnsWithDateDivsAct: TrnsWithDateDivsAct, + private val calcTrnsIncomeExpenseAct: CalcTrnsIncomeExpenseAct, + private val baseCurrencyAct: BaseCurrencyAct ) : IvyViewModel() { override val mutableState: MutableStateFlow = MutableStateFlow( ReportScreenState() @@ -65,9 +75,9 @@ class ReportViewModel @Inject constructor( fun start() { viewModelScope.launch(Dispatchers.IO) { - _baseCurrency.value = settingsDao.findFirst().currency - _accounts.value = accountDao.findAll() - _categories.value = listOf(unSpecifiedCategory) + categoryDao.findAll() + _baseCurrency.value = baseCurrencyAct(Unit) + _accounts.value = accountsAct(Unit) + _categories.value = listOf(unSpecifiedCategory) + categoriesAct(Unit) updateState { it.copy( @@ -107,23 +117,29 @@ class ReportViewModel @Inject constructor( .sortedByDescending { it.dateTime } val historyWithDateDividers = scope.async { - history.withDateDividers( - exchangeRateDao = exchangeRateDao, - accountDao = accountDao, - baseCurrencyCode = _baseCurrency.value + trnsWithDateDivsAct( + TrnsWithDateDivsAct.Input( + baseCurrency = stateVal().baseCurrency, + transactions = history + ) ) } - val income = scope.async { walletLogic.calculateIncome(history) } - val expenses = scope.async { walletLogic.calculateExpenses(history) } + val historyIncomeExpense = calcTrnsIncomeExpenseAct( + CalcTrnsIncomeExpenseAct.Input( + transactions = history, + accounts = accounts, + baseCurrency = baseCurrency + ) + ) val balance = scope.async { calculateBalance( baseCurrency = baseCurrency, accounts = accounts, history = history, - income = income.await(), - expenses = expenses.await(), + income = historyIncomeExpense.income.toDouble(), + expenses = historyIncomeExpense.expense.toDouble(), filter = filter ) } @@ -138,27 +154,36 @@ class ReportViewModel @Inject constructor( it.dueDate != null && it.dueDate.isAfter(timeNowUTC) } .sortedBy { it.dueDate } - val upcomingIncome = scope.async { walletLogic.calculateIncome(upcomingTransactions) } - val upcomingExpenses = - scope.async { walletLogic.calculateExpenses(upcomingTransactions) } + val upcomingIncomeExpense = calcTrnsIncomeExpenseAct( + CalcTrnsIncomeExpenseAct.Input( + transactions = upcomingTransactions, + accounts = accounts, + baseCurrency = baseCurrency + ) + ) //Overdue val overdue = transactions.filter { it.dueDate != null && it.dueDate.isBefore(timeNowUTC) }.sortedByDescending { it.dueDate } - val overdueIncome = scope.async { walletLogic.calculateIncome(overdue) } - val overdueExpenses = scope.async { walletLogic.calculateExpenses(overdue) } + val overdueIncomeExpense = calcTrnsIncomeExpenseAct( + CalcTrnsIncomeExpenseAct.Input( + transactions = overdue, + accounts = accounts, + baseCurrency = baseCurrency + ) + ) updateState { it.copy( - income = income.await(), - expenses = expenses.await(), - upcomingIncome = upcomingIncome.await(), - upcomingExpenses = upcomingExpenses.await(), - overdueIncome = overdueIncome.await(), - overdueExpenses = overdueExpenses.await(), + income = historyIncomeExpense.income.toDouble(), + expenses = historyIncomeExpense.expense.toDouble(), + upcomingIncome = upcomingIncomeExpense.income.toDouble(), + upcomingExpenses = upcomingIncomeExpense.expense.toDouble(), + overdueIncome = overdueIncomeExpense.income.toDouble(), + overdueExpenses = overdueIncomeExpense.expense.toDouble(), history = historyWithDateDividers.await(), upcomingTransactions = upcomingTransactions, overdueTransactions = overdue, @@ -175,7 +200,7 @@ class ReportViewModel @Inject constructor( } } - private fun filterTransactions( + private suspend fun filterTransactions( baseCurrency: String, accounts: List, filter: ReportFilter, @@ -187,7 +212,7 @@ class ReportViewModel @Inject constructor( return transactionDao .findAll() - .asSequence() + .map { it.toDomain() } .filter { //Filter by Transaction Type filter.trnTypes.contains(it.type) @@ -209,17 +234,21 @@ class ReportViewModel @Inject constructor( .filter { trn -> //Filter by Categories - filterCategoryIds.contains(trn.smartCategoryId()) || (trn.type == TransactionType.TRANSFER) + filterCategoryIds.contains(trn.categoryId) || (trn.type == TransactionType.TRANSFER) } - .filter { + .filterSuspend { //Filter by Amount //!NOTE: Amount must be converted to baseCurrency amount - val trnAmountBaseCurrency = exchangeRatesLogic.amountBaseCurrency( - transaction = it, - baseCurrency = baseCurrency, - accounts = accounts - ) + val trnAmountBaseCurrency = exchangeAct( + ExchangeAct.Input( + data = ExchangeData( + baseCurrency = baseCurrency, + fromCurrency = trnCurrency(it, accounts, baseCurrency), + ), + amount = it.amount + ) + ).orZero().toDouble() (filter.minAmount == null || trnAmountBaseCurrency >= filter.minAmount) && (filter.maxAmount == null || trnAmountBaseCurrency <= filter.maxAmount) @@ -279,7 +308,7 @@ class ReportViewModel @Inject constructor( return this.toLowerCaseLocal().contains(anotherString.toLowerCaseLocal()) } - private fun calculateBalance( + private suspend fun calculateBalance( baseCurrency: String, accounts: List, history: List, @@ -294,12 +323,16 @@ class ReportViewModel @Inject constructor( it.type == TransactionType.TRANSFER && it.toAccountId != null && includedAccountsIds.contains(it.toAccountId) } - .sumOf { trn -> - exchangeRatesLogic.toAmountBaseCurrency( - transaction = trn, - baseCurrency = baseCurrency, - accounts = accounts - ) + .sumOfSuspend { trn -> + exchangeAct( + ExchangeAct.Input( + data = ExchangeData( + baseCurrency = baseCurrency, + fromCurrency = trnCurrency(trn, accounts, baseCurrency), + ), + amount = trn.amount + ) + ).orZero().toDouble() } //- Transfers Out (#conv to BaseCurrency) @@ -308,19 +341,23 @@ class ReportViewModel @Inject constructor( it.type == TransactionType.TRANSFER && includedAccountsIds.contains(it.accountId) } - .sumOf { trn -> - exchangeRatesLogic.amountBaseCurrency( - transaction = trn, - baseCurrency = baseCurrency, - accounts = accounts - ) + .sumOfSuspend { trn -> + exchangeAct( + ExchangeAct.Input( + data = ExchangeData( + baseCurrency = baseCurrency, + fromCurrency = trnCurrency(trn, accounts, baseCurrency), + ), + amount = trn.amount + ) + ).orZero().toDouble() } //Income - Expenses (#conv to BaseCurrency) return income - expenses + transfersIn - transfersOut } - private fun export(context: Context) { + private suspend fun export(context: Context) { ivyContext.protectWithPaywall( paywallReason = PaywallReason.EXPORT_CSV, navigation = nav diff --git a/app/src/main/java/com/ivy/wallet/ui/search/SearchScreen.kt b/app/src/main/java/com/ivy/wallet/ui/search/SearchScreen.kt index 9516f6fe3d..2385784cf2 100644 --- a/app/src/main/java/com/ivy/wallet/ui/search/SearchScreen.kt +++ b/app/src/main/java/com/ivy/wallet/ui/search/SearchScreen.kt @@ -23,8 +23,8 @@ import com.ivy.design.api.navigation import com.ivy.design.l0_system.UI import com.ivy.wallet.R import com.ivy.wallet.domain.data.TransactionHistoryItem -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.Category +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Category import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.Search import com.ivy.wallet.ui.ivyWalletCtx diff --git a/app/src/main/java/com/ivy/wallet/ui/search/SearchViewModel.kt b/app/src/main/java/com/ivy/wallet/ui/search/SearchViewModel.kt index 4ac8e422c5..fda9410402 100644 --- a/app/src/main/java/com/ivy/wallet/ui/search/SearchViewModel.kt +++ b/app/src/main/java/com/ivy/wallet/ui/search/SearchViewModel.kt @@ -2,15 +2,14 @@ package com.ivy.wallet.ui.search import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.ivy.wallet.domain.action.account.AccountsAct +import com.ivy.wallet.domain.action.category.CategoriesAct +import com.ivy.wallet.domain.action.settings.BaseCurrencyAct +import com.ivy.wallet.domain.action.transaction.AllTrnsAct +import com.ivy.wallet.domain.action.transaction.TrnsWithDateDivsAct import com.ivy.wallet.domain.data.TransactionHistoryItem -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.logic.currency.ExchangeRatesLogic -import com.ivy.wallet.domain.logic.withDateDividers -import com.ivy.wallet.io.persistence.dao.AccountDao -import com.ivy.wallet.io.persistence.dao.CategoryDao -import com.ivy.wallet.io.persistence.dao.SettingsDao -import com.ivy.wallet.io.persistence.dao.TransactionDao +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Category import com.ivy.wallet.utils.TestIdlingResource import com.ivy.wallet.utils.getDefaultFIATCurrency import com.ivy.wallet.utils.ioThread @@ -22,11 +21,11 @@ import javax.inject.Inject @HiltViewModel class SearchViewModel @Inject constructor( - private val transactionDao: TransactionDao, - private val settingsDao: SettingsDao, - private val categoryDao: CategoryDao, - private val accountDao: AccountDao, - private val exchangeRatesLogic: ExchangeRatesLogic + private val trnsWithDateDivsAct: TrnsWithDateDivsAct, + private val accountsAct: AccountsAct, + private val categoriesAct: CategoriesAct, + private val baseCurrencyAct: BaseCurrencyAct, + private val allTrnsAct: AllTrnsAct ) : ViewModel() { private val _baseCurrencyCode = MutableStateFlow(getDefaultFIATCurrency().currencyCode) @@ -47,28 +46,24 @@ class SearchViewModel @Inject constructor( viewModelScope.launch { TestIdlingResource.increment() - _baseCurrencyCode.value = ioThread { - settingsDao.findFirst().currency - } + _baseCurrencyCode.value = baseCurrencyAct(Unit) - _categories.value = ioThread { - categoryDao.findAll() - } + _categories.value = categoriesAct(Unit) - _accounts.value = ioThread { - accountDao.findAll() - } + _accounts.value = accountsAct(Unit) _transactions.value = ioThread { - transactionDao.findAll() + val trns = allTrnsAct(Unit) .filter { it.title.matchesQuery(normalizedQuery) || it.description.matchesQuery(normalizedQuery) - }.withDateDividers( - exchangeRatesLogic = exchangeRatesLogic, - accountDao = accountDao, - settingsDao = settingsDao + } + trnsWithDateDivsAct( + TrnsWithDateDivsAct.Input( + baseCurrency = baseCurrencyCode.value, + transactions = trns ) + ) } TestIdlingResource.decrement() diff --git a/app/src/main/java/com/ivy/wallet/ui/settings/SettingsScreen.kt b/app/src/main/java/com/ivy/wallet/ui/settings/SettingsScreen.kt index e54c8d0dbb..819225a939 100644 --- a/app/src/main/java/com/ivy/wallet/ui/settings/SettingsScreen.kt +++ b/app/src/main/java/com/ivy/wallet/ui/settings/SettingsScreen.kt @@ -16,6 +16,7 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview @@ -34,7 +35,7 @@ import com.ivy.wallet.Constants.URL_IVY_CONTRIBUTORS import com.ivy.wallet.R import com.ivy.wallet.domain.data.AuthProviderType import com.ivy.wallet.domain.data.IvyCurrency -import com.ivy.wallet.domain.data.entity.User +import com.ivy.wallet.domain.data.core.User import com.ivy.wallet.ui.* import com.ivy.wallet.ui.theme.* import com.ivy.wallet.ui.theme.components.IvyButton @@ -56,6 +57,7 @@ fun BoxWithConstraintsScope.SettingsScreen(screen: Settings) { val lockApp by viewModel.lockApp.observeAsState(false) val showNotifications by viewModel.showNotifications.collectAsState() val hideCurrentBalance by viewModel.hideCurrentBalance.collectAsState() + val treatTransfersAsIncomeExpense by viewModel.treatTransfersAsIncomeExpense.collectAsState() val startDateOfMonth by viewModel.startDateOfMonth.observeAsState(1) val progressState by viewModel.progressState.collectAsState() @@ -75,6 +77,7 @@ fun BoxWithConstraintsScope.SettingsScreen(screen: Settings) { showNotifications = showNotifications, hideCurrentBalance = hideCurrentBalance, progressState = progressState, + treatTransfersAsIncomeExpense = treatTransfersAsIncomeExpense, nameLocalAccount = nameLocalAccount, startDateOfMonth = startDateOfMonth, @@ -96,6 +99,7 @@ fun BoxWithConstraintsScope.SettingsScreen(screen: Settings) { onSetShowNotifications = viewModel::setShowNotifications, onSetHideCurrentBalance = viewModel::setHideCurrentBalance, onSetStartDateOfMonth = viewModel::setStartDateOfMonth, + onSetTreatTransfersAsIncExp = viewModel::setTransfersAsIncomeExpense, onRequestFeature = { title, body -> viewModel.requestFeature( rootActivity = ivyActivity, @@ -118,6 +122,7 @@ private fun BoxWithConstraintsScope.UI( showNotifications: Boolean = true, hideCurrentBalance: Boolean = false, progressState: Boolean = false, + treatTransfersAsIncomeExpense :Boolean = false, nameLocalAccount: String?, startDateOfMonth: Int = 1, @@ -133,6 +138,7 @@ private fun BoxWithConstraintsScope.UI( onExportToCSV: () -> Unit = {}, onSetLockApp: (Boolean) -> Unit = {}, onSetShowNotifications: (Boolean) -> Unit = {}, + onSetTreatTransfersAsIncExp: (Boolean) -> Unit = {}, onSetHideCurrentBalance: (Boolean) -> Unit = {}, onSetStartDateOfMonth: (Int) -> Unit = {}, onRequestFeature: (String, String) -> Unit = { _, _ -> }, @@ -179,7 +185,7 @@ private fun BoxWithConstraintsScope.UI( Text( modifier = Modifier.padding(start = 32.dp), - text = "Settings", + text = stringResource(R.string.settings), style = UI.typo.h2.style( fontWeight = FontWeight.Black ) @@ -210,7 +216,7 @@ private fun BoxWithConstraintsScope.UI( } item { - SettingsSectionDivider(text = "Import & Export") + SettingsSectionDivider(text = stringResource(R.string.import_export)) Spacer(Modifier.height(16.dp)) @@ -223,7 +229,7 @@ private fun BoxWithConstraintsScope.UI( SettingsDefaultButton( icon = R.drawable.ic_export_csv, - text = "Backup Data", + text = stringResource(R.string.backup_data), ) { onBackupData() } @@ -232,7 +238,7 @@ private fun BoxWithConstraintsScope.UI( SettingsPrimaryButton( icon = R.drawable.ic_export_csv, - text = "Import Data", + text = stringResource(R.string.import_data), backgroundGradient = GradientGreen ) { nav.navigateTo( @@ -244,14 +250,14 @@ private fun BoxWithConstraintsScope.UI( } item { - SettingsSectionDivider(text = "App Settings") + SettingsSectionDivider(text = stringResource(R.string.app_settings)) Spacer(Modifier.height(16.dp)) AppSwitch( lockApp = lockApp, onSetLockApp = onSetLockApp, - text = "Lock app", + text = stringResource(R.string.lock_app), icon = R.drawable.ic_custom_fingerprint_m ) @@ -260,7 +266,7 @@ private fun BoxWithConstraintsScope.UI( AppSwitch( lockApp = showNotifications, onSetLockApp = onSetShowNotifications, - text = "Show notifications", + text = stringResource(R.string.show_notifications), icon = R.drawable.ic_notification_m ) @@ -269,13 +275,23 @@ private fun BoxWithConstraintsScope.UI( AppSwitch( lockApp = hideCurrentBalance, onSetLockApp = onSetHideCurrentBalance, - text = "Hide balance", - description = "Click on the hidden balance to show the balance for 5s", + text = stringResource(R.string.hide_balance), + description = stringResource(R.string.hide_balance_description), icon = R.drawable.ic_hide_m ) Spacer(Modifier.height(12.dp)) + AppSwitch( + lockApp = treatTransfersAsIncomeExpense, + onSetLockApp = onSetTreatTransfersAsIncExp, + text = stringResource(R.string.transfers_as_income_expense), + description = stringResource(R.string.transfers_as_income_expense_description), + icon = R.drawable.ic_custom_transfer_m + ) + + Spacer(Modifier.height(12.dp)) + StartDateOfMonth( startDateOfMonth = startDateOfMonth ) { @@ -284,14 +300,14 @@ private fun BoxWithConstraintsScope.UI( } item { - SettingsSectionDivider(text = "Other") + SettingsSectionDivider(text = stringResource(R.string.other)) Spacer(Modifier.height(16.dp)) val ivyActivity = LocalContext.current as RootActivity SettingsPrimaryButton( icon = R.drawable.ic_custom_star_m, - text = "Rate us on Google Play", + text = stringResource(R.string.rate_us_on_google_play), backgroundGradient = GradientIvy ) { ivyActivity.reviewIvyWallet(dismissReviewCard = false) @@ -301,7 +317,7 @@ private fun BoxWithConstraintsScope.UI( SettingsPrimaryButton( icon = R.drawable.ic_custom_family_m, - text = "Share Ivy Wallet", + text = stringResource(R.string.share_ivy_wallet), backgroundGradient = Gradient.solid(Red3) ) { ivyActivity.shareIvyWallet() @@ -309,7 +325,7 @@ private fun BoxWithConstraintsScope.UI( } item { - SettingsSectionDivider(text = "Product") + SettingsSectionDivider(text = stringResource(R.string.product)) Spacer(Modifier.height(12.dp)) @@ -344,7 +360,7 @@ private fun BoxWithConstraintsScope.UI( item { SettingsSectionDivider( - text = "Danger zone", + text = stringResource(R.string.danger_zone), color = Red ) @@ -352,7 +368,7 @@ private fun BoxWithConstraintsScope.UI( SettingsPrimaryButton( icon = R.drawable.ic_delete, - text = "Delete all user data", + text = stringResource(R.string.delete_all_user_data), backgroundGradient = Gradient.solid(Red) ) { deleteAllDataModalVisible = true @@ -365,7 +381,7 @@ private fun BoxWithConstraintsScope.UI( } CurrencyModal( - title = "Set currency", + title = stringResource(R.string.set_currency), initialCurrency = IvyCurrency.fromCode(currencyCode), visible = currencyModalVisible, dismiss = { currencyModalVisible = false } @@ -398,8 +414,10 @@ private fun BoxWithConstraintsScope.UI( ) DeleteModal( - title = "Delete all user data?", - description = "WARNING! This action will delete all data for ${user?.email ?: "your account"} PERMANENTLY and you won't be able to recover it.", + title = stringResource(R.string.delete_all_user_data_question), + description = stringResource(R.string.delete_all_user_data_warning, user?.email ?: stringResource( + R.string.your_account) + ), visible = deleteAllDataModalVisible, dismiss = { deleteAllDataModalVisible = false }, onDelete = { @@ -409,8 +427,10 @@ private fun BoxWithConstraintsScope.UI( ) DeleteModal( - title = "Confirm permanent deletion for '${user?.email ?: "all of your data"}'", - description = "FINAL WARNING! After clicking \"Delete\" your data will be gone forever.", + title = stringResource(R.string.confirm_all_userd_data_deletion, user?.email ?: stringResource( + R.string.all_of_your_data) + ), + description = stringResource(R.string.final_deletion_warning), visible = deleteAllDataModalFinalVisible, dismiss = { deleteAllDataModalFinalVisible = false }, onDelete = { @@ -419,8 +439,8 @@ private fun BoxWithConstraintsScope.UI( ) ProgressModal( - title = "Exporting Data", - description = "Please wait, exporting data", + title = stringResource(R.string.exporting_data), + description = stringResource(R.string.exporting_data_description), visible = progressState ) } @@ -447,7 +467,7 @@ private fun StartDateOfMonth( Text( modifier = Modifier.padding(vertical = 20.dp), - text = "Start date of month", + text = stringResource(R.string.start_date_of_month), style = UI.typo.b2.style( color = UI.colors.pureInverse, fontWeight = FontWeight.Bold @@ -474,7 +494,7 @@ private fun IvyTelegram() { val rootActivity = rootActivity() SettingsPrimaryButton( icon = R.drawable.ic_telegram_24dp, - text = "Ivy Telegram", + text = stringResource(R.string.ivy_telegram), backgroundGradient = Gradient.solid(Blue), ) { rootActivity.openUrlInBrowser(Constants.URL_IVY_TELEGRAM_INVITE) @@ -486,7 +506,7 @@ private fun HelpCenter() { val nav = navigation() SettingsDefaultButton( icon = R.drawable.ic_custom_education_m, - text = "Help Center", + text = stringResource(R.string.help_center), ) { nav.navigateTo( IvyWebView(url = Constants.URL_HELP_CENTER) @@ -499,7 +519,7 @@ private fun Roadmap() { val nav = navigation() SettingsDefaultButton( icon = R.drawable.ic_custom_rocket_m, - text = "Roadmap", + text = stringResource(R.string.roadmap), ) { nav.navigateTo( IvyWebView(url = Constants.URL_ROADMAP) @@ -513,7 +533,7 @@ private fun RequestFeature( ) { SettingsDefaultButton( icon = R.drawable.ic_custom_programming_m, - text = "Request a feature", + text = stringResource(R.string.request_a_feature), ) { onClick() } @@ -524,7 +544,7 @@ private fun ContactSupport() { val ivyActivity = LocalContext.current as RootActivity SettingsDefaultButton( icon = R.drawable.ic_support, - text = "Contact support", + text = stringResource(R.string.contact_support), ) { ivyActivity.contactSupport() } @@ -535,7 +555,7 @@ private fun ProjectContributors() { val nav = navigation() SettingsDefaultButton( icon = R.drawable.ic_custom_people_m, - text = "Project Contributors", + text = stringResource(R.string.project_contributors), ) { nav.navigateTo( IvyWebView(url = URL_IVY_CONTRIBUTORS) @@ -631,7 +651,7 @@ private fun AccountCard( Spacer(Modifier.width(24.dp)) Text( - text = "ACCOUNT", + text = stringResource(R.string.account_uppercase), style = UI.typo.c.style( fontWeight = FontWeight.Black, color = UI.colors.gray @@ -643,14 +663,14 @@ private fun AccountCard( if (user != null) { AccountCardButton( icon = R.drawable.ic_logout, - text = "Logout" + text = stringResource(R.string.logout) ) { onLogout() } } else { AccountCardButton( icon = R.drawable.ic_login, - text = "Login" + text = stringResource(R.string.login) ) { onLogin() } @@ -755,7 +775,7 @@ private fun AccountCardUser( Spacer(Modifier.width(12.dp)) Text( - text = "Syncing...", + text = stringResource(R.string.syncing), style = UI.typo.b2.style( fontWeight = FontWeight.ExtraBold, color = Orange @@ -781,7 +801,7 @@ private fun AccountCardUser( Spacer(Modifier.width(12.dp)) Text( - text = "Data synced to cloud", + text = stringResource(R.string.data_synced_to_cloud), style = UI.typo.b2.style( fontWeight = FontWeight.ExtraBold, color = Green @@ -795,7 +815,7 @@ private fun AccountCardUser( IvyButton( modifier = Modifier.padding(horizontal = 24.dp), iconStart = R.drawable.ic_sync, - text = "Tap to sync", + text = stringResource(R.string.tap_to_sync), backgroundGradient = GradientRed ) { onSync() @@ -806,7 +826,7 @@ private fun AccountCardUser( IvyButton( modifier = Modifier.padding(horizontal = 24.dp), iconStart = R.drawable.ic_sync, - text = "Sync failed. Tap to sync", + text = stringResource(R.string.sync_failed), backgroundGradient = GradientRed ) { onSync() @@ -834,7 +854,7 @@ private fun AccountCardLocalAccount( Text( modifier = Modifier.testTag("local_account_name"), - text = if (name != null && name.isNotBlank()) name else "Anonymous", + text = if (name != null && name.isNotBlank()) name else stringResource(R.string.anonymous), style = UI.typo.b2.style( fontWeight = FontWeight.Bold ) @@ -867,7 +887,7 @@ private fun ExportCSV( ) { SettingsDefaultButton( icon = R.drawable.ic_export_csv, - text = "Export to CSV", + text = stringResource(R.string.export_to_csv), ) { onExportToCSV() } @@ -893,7 +913,7 @@ private fun TCAndPrivacyPolicy() { uriHandler.openUri(Constants.URL_TC) } .padding(vertical = 14.dp), - text = "Terms & Conditions", + text = stringResource(R.string.terms_conditions), style = UI.typo.c.style( fontWeight = FontWeight.ExtraBold, color = UI.colors.pureInverse, @@ -912,7 +932,7 @@ private fun TCAndPrivacyPolicy() { uriHandler.openUri(Constants.URL_PRIVACY_POLICY) } .padding(vertical = 14.dp), - text = "Privacy Policy", + text = stringResource(R.string.privacy_policy), style = UI.typo.c.style( fontWeight = FontWeight.ExtraBold, color = UI.colors.pureInverse, @@ -1046,7 +1066,7 @@ private fun CurrencyButton( Text( modifier = Modifier.padding(vertical = 20.dp), - text = "Set currency", + text = stringResource(R.string.set_currency), style = UI.typo.b2.style( color = UI.colors.pureInverse, fontWeight = FontWeight.Bold diff --git a/app/src/main/java/com/ivy/wallet/ui/settings/SettingsViewModel.kt b/app/src/main/java/com/ivy/wallet/ui/settings/SettingsViewModel.kt index d5b969acba..6b5d7819f1 100644 --- a/app/src/main/java/com/ivy/wallet/ui/settings/SettingsViewModel.kt +++ b/app/src/main/java/com/ivy/wallet/ui/settings/SettingsViewModel.kt @@ -5,12 +5,12 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.ivy.wallet.domain.data.analytics.AnalyticsEvent -import com.ivy.wallet.domain.data.entity.User -import com.ivy.wallet.domain.logic.LogoutLogic -import com.ivy.wallet.domain.logic.csv.ExportCSVLogic -import com.ivy.wallet.domain.logic.currency.ExchangeRatesLogic -import com.ivy.wallet.domain.logic.zip.ExportZipLogic -import com.ivy.wallet.domain.sync.IvySync +import com.ivy.wallet.domain.data.core.User +import com.ivy.wallet.domain.deprecated.logic.LogoutLogic +import com.ivy.wallet.domain.deprecated.logic.csv.ExportCSVLogic +import com.ivy.wallet.domain.deprecated.logic.currency.ExchangeRatesLogic +import com.ivy.wallet.domain.deprecated.logic.zip.ExportZipLogic +import com.ivy.wallet.domain.deprecated.sync.IvySync import com.ivy.wallet.io.network.FCMClient import com.ivy.wallet.io.network.IvyAnalytics import com.ivy.wallet.io.network.IvySession @@ -69,6 +69,9 @@ class SettingsViewModel @Inject constructor( private val _showNotifications = MutableStateFlow(true) val showNotifications = _showNotifications.asStateFlow() + private val _treatTransfersAsIncomeExpense = MutableStateFlow(false) + val treatTransfersAsIncomeExpense = _treatTransfersAsIncomeExpense.asStateFlow() + private val _progressState = MutableStateFlow(false) val progressState = _progressState.asStateFlow() @@ -88,15 +91,19 @@ class SettingsViewModel @Inject constructor( _user.value = ioThread { val userId = ivySession.getUserIdSafe() - if (userId != null) userDao.findById(userId) else null + if (userId != null) userDao.findById(userId)?.toDomain() else null } _currencyCode.value = settings.currency _lockApp.value = sharedPrefs.getBoolean(SharedPrefs.APP_LOCK_ENABLED, false) - _hideCurrentBalance.value = sharedPrefs.getBoolean(SharedPrefs.HIDE_CURRENT_BALANCE, false) + _hideCurrentBalance.value = + sharedPrefs.getBoolean(SharedPrefs.HIDE_CURRENT_BALANCE, false) _showNotifications.value = sharedPrefs.getBoolean(SharedPrefs.SHOW_NOTIFICATIONS, true) + _treatTransfersAsIncomeExpense.value = + sharedPrefs.getBoolean(SharedPrefs.TRANSFERS_AS_INCOME_EXPENSE, false) + _opSync.value = OpResult.success(ioThread { ivySync.isSynced() }) TestIdlingResource.decrement() @@ -307,6 +314,20 @@ class SettingsViewModel @Inject constructor( } } + fun setTransfersAsIncomeExpense(treatTransfersAsIncomeExpense: Boolean) { + viewModelScope.launch { + TestIdlingResource.increment() + + sharedPrefs.putBoolean( + SharedPrefs.TRANSFERS_AS_INCOME_EXPENSE, + treatTransfersAsIncomeExpense + ) + _treatTransfersAsIncomeExpense.value = treatTransfersAsIncomeExpense + + TestIdlingResource.decrement() + } + } + fun requestFeature( rootActivity: RootActivity, title: String, diff --git a/app/src/main/java/com/ivy/wallet/ui/statistic/level1/CategoryAmount.kt b/app/src/main/java/com/ivy/wallet/ui/statistic/level1/CategoryAmount.kt index 425b198aec..3f8c39e9ac 100644 --- a/app/src/main/java/com/ivy/wallet/ui/statistic/level1/CategoryAmount.kt +++ b/app/src/main/java/com/ivy/wallet/ui/statistic/level1/CategoryAmount.kt @@ -1,7 +1,7 @@ package com.ivy.wallet.ui.statistic.level1 -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.data.entity.Transaction +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.data.core.Transaction data class CategoryAmount( val category: Category?, diff --git a/app/src/main/java/com/ivy/wallet/ui/statistic/level1/PieChart.kt b/app/src/main/java/com/ivy/wallet/ui/statistic/level1/PieChart.kt index 2084d934b3..541b279ff9 100644 --- a/app/src/main/java/com/ivy/wallet/ui/statistic/level1/PieChart.kt +++ b/app/src/main/java/com/ivy/wallet/ui/statistic/level1/PieChart.kt @@ -22,7 +22,7 @@ import androidx.compose.ui.viewinterop.AndroidView import com.ivy.design.l0_system.UI import com.ivy.wallet.R import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.data.entity.Category +import com.ivy.wallet.domain.data.core.Category import com.ivy.wallet.ui.IvyWalletComponentPreview import com.ivy.wallet.ui.theme.* import com.ivy.wallet.ui.theme.components.IvyIcon diff --git a/app/src/main/java/com/ivy/wallet/ui/statistic/level1/PieChartStatisticBottomBar.kt b/app/src/main/java/com/ivy/wallet/ui/statistic/level1/PieChartStatisticBottomBar.kt index 6cf42b9558..a26bc13305 100644 --- a/app/src/main/java/com/ivy/wallet/ui/statistic/level1/PieChartStatisticBottomBar.kt +++ b/app/src/main/java/com/ivy/wallet/ui/statistic/level1/PieChartStatisticBottomBar.kt @@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.width import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp @@ -51,7 +52,7 @@ fun BoxWithConstraintsScope.PieChartStatisticBottomBar( val isIncome = type == TransactionType.INCOME IvyButton( iconStart = R.drawable.ic_plus, - text = if (isIncome) "Add income" else "Add expense", + text = if (isIncome) stringResource(id = R.string.add_income) else stringResource(id = R.string.add_expense), backgroundGradient = if (isIncome) GradientGreen else Gradient.solid(UI.colors.pureInverse), textStyle = UI.typo.b2.style( color = if (isIncome) White else UI.colors.pure, diff --git a/app/src/main/java/com/ivy/wallet/ui/statistic/level1/PieChartStatisticScreen.kt b/app/src/main/java/com/ivy/wallet/ui/statistic/level1/PieChartStatisticScreen.kt index 6c99171265..c2dc63b1ba 100644 --- a/app/src/main/java/com/ivy/wallet/ui/statistic/level1/PieChartStatisticScreen.kt +++ b/app/src/main/java/com/ivy/wallet/ui/statistic/level1/PieChartStatisticScreen.kt @@ -17,6 +17,8 @@ import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview @@ -30,8 +32,8 @@ import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style import com.ivy.wallet.R import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.data.entity.Transaction +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.data.core.Transaction import com.ivy.wallet.ui.* import com.ivy.wallet.ui.onboarding.model.TimePeriod import com.ivy.wallet.ui.theme.* @@ -158,8 +160,11 @@ private fun BoxWithConstraintsScope.UI( Spacer(Modifier.height(20.dp)) Text( - modifier = Modifier.padding(start = 32.dp), - text = if (transactionType == TransactionType.EXPENSE) "Expenses" else "Income", + modifier = Modifier + .padding(start = 32.dp) + .testTag("piechart_title"), + text = if (transactionType == TransactionType.EXPENSE) + stringResource(R.string.expenses) else stringResource(R.string.income), style = UI.typo.b1.style( fontWeight = FontWeight.ExtraBold ) @@ -168,7 +173,8 @@ private fun BoxWithConstraintsScope.UI( BalanceRow( modifier = Modifier .padding(start = 32.dp, end = 16.dp) - .alpha(percentExpanded), + .alpha(percentExpanded) + .testTag("piechart_total_amount"), currency = currency, balance = totalAmount, currencyUpfront = false, @@ -431,7 +437,7 @@ private fun CategoryAmountCard( modifier = Modifier .weight(1f) .padding(end = 16.dp), - text = category?.name ?: "Unspecified", + text = category?.name ?: stringResource(R.string.unspecified), style = UI.typo.b2.style( color = textColor, fontWeight = FontWeight.Bold, @@ -469,7 +475,9 @@ private fun PercentText( contrastColor: Color ) { Text( - text = if (totalAmount != 0.0) "${((amount / totalAmount) * 100).format(2)}%" else "0%", + text = if (totalAmount != 0.0) + stringResource(R.string.percent, ((amount / totalAmount) * 100).format(2)) + else stringResource(R.string.percent, "0"), style = UI.typo.nB2.style( color = if (selectedState) contrastColor else UI.colors.pureInverse, fontWeight = FontWeight.Normal @@ -524,7 +532,7 @@ private fun Preview_Expense() { ), ), selectedCategory = null, - checkForUnSpecifiedCategory = {false} + checkForUnSpecifiedCategory = { false } ) } } @@ -576,7 +584,7 @@ private fun Preview_Income() { ), ), selectedCategory = null, - checkForUnSpecifiedCategory = {false} + checkForUnSpecifiedCategory = { false } ) } } diff --git a/app/src/main/java/com/ivy/wallet/ui/statistic/level1/PieChartStatisticViewModel.kt b/app/src/main/java/com/ivy/wallet/ui/statistic/level1/PieChartStatisticViewModel.kt index fa501bdd51..f7b4e05610 100644 --- a/app/src/main/java/com/ivy/wallet/ui/statistic/level1/PieChartStatisticViewModel.kt +++ b/app/src/main/java/com/ivy/wallet/ui/statistic/level1/PieChartStatisticViewModel.kt @@ -3,24 +3,23 @@ package com.ivy.wallet.ui.statistic.level1 import androidx.compose.ui.graphics.toArgb import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.ivy.wallet.R +import com.ivy.wallet.domain.action.charts.PieChartAct import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.data.entity.Transaction -import com.ivy.wallet.domain.fp.category.calculateCategoryExpenseWithAccountFilters -import com.ivy.wallet.domain.fp.category.calculateCategoryIncomeWithAccountFilters -import com.ivy.wallet.domain.fp.data.WalletDAOs -import com.ivy.wallet.domain.fp.wallet.calculateWalletExpenseWithAccountFilters -import com.ivy.wallet.domain.fp.wallet.calculateWalletIncomeWithAccountFilters -import com.ivy.wallet.domain.logic.currency.ExchangeRatesLogic -import com.ivy.wallet.domain.logic.currency.sumInBaseCurrency +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.data.core.Transaction +import com.ivy.wallet.domain.deprecated.logic.currency.ExchangeRatesLogic +import com.ivy.wallet.domain.deprecated.logic.currency.sumInBaseCurrency +import com.ivy.wallet.domain.pure.data.WalletDAOs +import com.ivy.wallet.io.persistence.SharedPrefs import com.ivy.wallet.io.persistence.dao.CategoryDao import com.ivy.wallet.io.persistence.dao.SettingsDao import com.ivy.wallet.io.persistence.dao.TransactionDao +import com.ivy.wallet.stringRes import com.ivy.wallet.ui.IvyWalletCtx import com.ivy.wallet.ui.PieChartStatistic import com.ivy.wallet.ui.onboarding.model.FromToTimeRange import com.ivy.wallet.ui.onboarding.model.TimePeriod -import com.ivy.wallet.ui.onboarding.model.toCloseTimeRange import com.ivy.wallet.ui.theme.IvyLight import com.ivy.wallet.utils.dateNowUTC import com.ivy.wallet.utils.ioThread @@ -34,7 +33,6 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch import java.util.* import javax.inject.Inject -import kotlin.math.absoluteValue @HiltViewModel class PieChartStatisticViewModel @Inject constructor( @@ -43,7 +41,9 @@ class PieChartStatisticViewModel @Inject constructor( private val settingsDao: SettingsDao, private val transactionDao: TransactionDao, private val exchangeRatesLogic: ExchangeRatesLogic, - private val ivyContext: IvyWalletCtx + private val ivyContext: IvyWalletCtx, + private val pieChartAct: PieChartAct, + private val sharedPrefs: SharedPrefs ) : ViewModel() { private val _period = MutableStateFlow(ivyContext.selectedPeriod) val period = _period.readOnly() @@ -74,7 +74,11 @@ class PieChartStatisticViewModel @Inject constructor( private var filterExcluded = true private val transfersCategory = - Category("Account Transfers", color = IvyLight.toArgb(), icon = "transfer") + Category( + stringRes(R.string.account_transfers), + color = IvyLight.toArgb(), + icon = "transfer" + ) fun start( screen: PieChartStatistic @@ -111,7 +115,7 @@ class PieChartStatisticViewModel @Inject constructor( load( period = period, type = type, - accountFilterList = accountList + accountIdFilterList = accountList ) } @@ -147,7 +151,7 @@ class PieChartStatisticViewModel @Inject constructor( accountDao = walletDAOs.accountDao ) - CategoryAmount(category, amount, trans) + CategoryAmount(category?.toDomain(), amount, trans) } }.awaitAll().sortedByDescending { it.amount } } @@ -181,7 +185,7 @@ class PieChartStatisticViewModel @Inject constructor( exchangeRatesLogic.toAmountBaseCurrency( transaction = it, baseCurrency = baseCurrencyCode.value, - accounts = walletDAOs.accountDao.findAll() + accounts = walletDAOs.accountDao.findAll().map { it.toDomain() } ) } CategoryAmount(transfersCategory, amt, trans) @@ -192,7 +196,7 @@ class PieChartStatisticViewModel @Inject constructor( private fun load( period: TimePeriod, type: TransactionType, - accountFilterList: List + accountIdFilterList: List ) { _period.value = period @@ -201,73 +205,28 @@ class PieChartStatisticViewModel @Inject constructor( _selectedCategory.value = null - viewModelScope.launch { + viewModelScope.launch(Dispatchers.IO) { val settings = ioThread { settingsDao.findFirst() } - _baseCurrencyCode.value = settings.currency - _totalAmount.value = ioThread { - when (type) { - TransactionType.INCOME -> { - calculateWalletIncomeWithAccountFilters( - walletDAOs = walletDAOs, - baseCurrencyCode = baseCurrencyCode.value, - range = range.toCloseTimeRange(), - accountIdFilterList = accountFilterList, - filterExcluded = filterExcluded - ).value.toDouble() - } - TransactionType.EXPENSE -> { - calculateWalletExpenseWithAccountFilters( - walletDAOs = walletDAOs, - baseCurrencyCode = baseCurrencyCode.value, - range = range.toCloseTimeRange(), - accountIdFilterList = accountFilterList, - filterExcluded = filterExcluded - ).value.toDouble() - } - else -> error("not supported transactionType - $type") - } - }.absoluteValue - - _categoryAmounts.value = scopedIOThread { scope -> - - val categories = - getCategories( - fetchCategoriesFromTransactions = accountFilterList.isNotEmpty(), - timeRange = range - ) - - categories - .plus(null) //for unspecified - .map { category -> - CategoryAmount( - category = category, - amount = when (type) { - TransactionType.INCOME -> { - calculateCategoryIncomeWithAccountFilters( - walletDAOs = walletDAOs, - baseCurrencyCode = baseCurrencyCode.value, - categoryId = category?.id, - accountIdFilterList = accountFilterList, - range = range.toCloseTimeRange() - ).toDouble() - } - TransactionType.EXPENSE -> { - calculateCategoryExpenseWithAccountFilters( - walletDAOs = walletDAOs, - baseCurrencyCode = baseCurrencyCode.value, - categoryId = category?.id, - accountIdList = accountFilterList, - range = range.toCloseTimeRange() - ).toDouble() - } - else -> error("not supported transactionType - $type") - } - ) - } - .sortedByDescending { it.amount } - } + val treatTransferAsIncExp = + sharedPrefs.getBoolean( + SharedPrefs.TRANSFERS_AS_INCOME_EXPENSE, + false + ) && accountIdFilterList.isNotEmpty() + + val pieChartActOutput = pieChartAct( + PieChartAct.Input( + baseCurrency = _baseCurrencyCode.value, + range = range, + type = _type.value, + accountIdFilterList = accountIdFilterList, + treatTransferAsIncExp = treatTransferAsIncExp + ) + ) + + _totalAmount.value = pieChartActOutput.totalAmount + _categoryAmounts.value = pieChartActOutput.categoryAmounts } } @@ -293,7 +252,7 @@ class PieChartStatisticViewModel @Inject constructor( load( period = period, type = type.value, - accountFilterList = accountIdFilterList.value + accountIdFilterList = accountIdFilterList.value ) } @@ -304,7 +263,7 @@ class PieChartStatisticViewModel @Inject constructor( load( period = month.incrementMonthPeriod(ivyContext, 1L, year), type = type.value, - accountFilterList = accountIdFilterList.value + accountIdFilterList = accountIdFilterList.value ) } } @@ -316,7 +275,7 @@ class PieChartStatisticViewModel @Inject constructor( load( period = month.incrementMonthPeriod(ivyContext, -1L, year), type = type.value, - accountFilterList = accountIdFilterList.value + accountIdFilterList = accountIdFilterList.value ) } } @@ -327,19 +286,21 @@ class PieChartStatisticViewModel @Inject constructor( ): List { return scopedIOThread { scope -> if (fetchCategoriesFromTransactions) { - transactionDao.findAllBetween(timeRange.from(), timeRange.to()).filter { - it.categoryId != null - }.map { - scope.async { - categoryDao.findById(it.categoryId!!) - } - }.awaitAll().filterNotNull().distinctBy { it.id } + transactionDao.findAllBetween(timeRange.from(), timeRange.to()) + .map { it.toDomain() } + .filter { + it.categoryId != null + }.map { + scope.async { + categoryDao.findById(it.categoryId!!)?.toDomain() + } + }.awaitAll().filterNotNull().distinctBy { it.id } } else - categoryDao.findAll() + categoryDao.findAll().map { it.toDomain() } } } fun checkForUnspecifiedCategory(category: Category?): Boolean { - return category == null || category == transfersCategory + return category == null || category == transfersCategory || category.name == stringRes(R.string.account_transfers) } } \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/ui/statistic/level1/SelectedCategory.kt b/app/src/main/java/com/ivy/wallet/ui/statistic/level1/SelectedCategory.kt index 6b355b4030..ca05361aba 100644 --- a/app/src/main/java/com/ivy/wallet/ui/statistic/level1/SelectedCategory.kt +++ b/app/src/main/java/com/ivy/wallet/ui/statistic/level1/SelectedCategory.kt @@ -1,6 +1,6 @@ package com.ivy.wallet.ui.statistic.level1 -import com.ivy.wallet.domain.data.entity.Category +import com.ivy.wallet.domain.data.core.Category data class SelectedCategory( val category: Category? //null - Unspecified diff --git a/app/src/main/java/com/ivy/wallet/ui/statistic/level2/ItemStatisticScreen.kt b/app/src/main/java/com/ivy/wallet/ui/statistic/level2/ItemStatisticScreen.kt index a442cf8334..8139f49541 100644 --- a/app/src/main/java/com/ivy/wallet/ui/statistic/level2/ItemStatisticScreen.kt +++ b/app/src/main/java/com/ivy/wallet/ui/statistic/level2/ItemStatisticScreen.kt @@ -15,6 +15,7 @@ import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalView import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -29,9 +30,10 @@ import com.ivy.wallet.R import com.ivy.wallet.domain.data.IvyCurrency import com.ivy.wallet.domain.data.TransactionHistoryItem import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.data.entity.Transaction +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.data.core.Transaction +import com.ivy.wallet.stringRes import com.ivy.wallet.ui.* import com.ivy.wallet.ui.onboarding.model.TimePeriod import com.ivy.wallet.ui.theme.* @@ -355,22 +357,21 @@ private fun BoxWithConstraintsScope.UI( (ivyContext.screenHeight * 0.7f).toDp() }, onPayOrGet = onPayOrGet, - emptyStateTitle = "No transactions", + emptyStateTitle = stringRes(R.string.no_transactions), + + emptyStateText = stringRes(R.string.no_transactions_for_period, period.toDisplayLong(ivyContext.startDayOfMonth)), - emptyStateText = "You don't have any transactions for ${ - period.toDisplayLong(ivyContext.startDayOfMonth) - }.\nYou can add one by scrolling down and tapping \"Add income\" or \"Add expense\" button at the top." ) } } DeleteModal( visible = deleteModalVisible, - title = "Confirm deletion", + title = stringResource(R.string.confirm_deletion), description = if (account != null) { - "Note: Deleting this account will remove it permanently and delete all associated transactions with it." + stringResource(R.string.account_confirm_deletion_description) } else { - "Note: Deleting this category will remove it permanently." + stringResource(R.string.category_confirm_deletion_description) }, dismiss = { deleteModalVisible = false } ) { @@ -562,7 +563,7 @@ fun ItemStatisticToolbar( IvyOutlinedButton( iconStart = R.drawable.ic_edit, - text = "Edit", + text = stringRes(R.string.edit), borderColor = contrastColor, iconTint = contrastColor, textColor = contrastColor, @@ -602,13 +603,13 @@ fun IncomeExpensesCards( Spacer(Modifier.width(16.dp)) HeaderCard( - title = "INCOME", + title = stringRes(R.string.income_uppercase), currencyCode = currency, amount = income, transactionCount = history .filterIsInstance(Transaction::class.java) .count { it.type == TransactionType.INCOME }, - addButtonText = if (hasAddButtons) "Add income" else null, + addButtonText = if (hasAddButtons) stringResource(R.string.add_income) else null, isIncome = true, itemColor = itemColor, @@ -620,13 +621,13 @@ fun IncomeExpensesCards( Spacer(Modifier.width(12.dp)) HeaderCard( - title = "EXPENSES", + title = stringRes(R.string.expenses_uppercase), currencyCode = currency, amount = expenses, transactionCount = history .filterIsInstance(Transaction::class.java) .count { it.type == TransactionType.EXPENSE }, - addButtonText = if (hasAddButtons) "Add expense" else null, + addButtonText = if (hasAddButtons) stringResource(R.string.add_expense) else null, isIncome = false, itemColor = itemColor, @@ -711,7 +712,7 @@ private fun RowScope.HeaderCard( ) Text( modifier = Modifier.padding(horizontal = 24.dp), - text = "transactions", + text = stringRes(R.string.transactions), style = UI.typo.b2.style( color = contrastColor, fontWeight = FontWeight.Normal @@ -794,7 +795,7 @@ private fun Item( modifier = Modifier .align(Alignment.Bottom) .padding(bottom = 12.dp), - text = "(excluded)", + text = stringRes(R.string.excluded), style = UI.typo.c.style( color = account.color.toComposeColor().dynamicContrast() ) diff --git a/app/src/main/java/com/ivy/wallet/ui/statistic/level2/ItemStatisticViewModel.kt b/app/src/main/java/com/ivy/wallet/ui/statistic/level2/ItemStatisticViewModel.kt index d8afc523e7..e535a8bd6b 100644 --- a/app/src/main/java/com/ivy/wallet/ui/statistic/level2/ItemStatisticViewModel.kt +++ b/app/src/main/java/com/ivy/wallet/ui/statistic/level2/ItemStatisticViewModel.kt @@ -4,22 +4,30 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import arrow.core.toOption import com.ivy.design.navigation.Navigation +import com.ivy.fp.action.then +import com.ivy.wallet.R +import com.ivy.wallet.domain.action.account.AccTrnsAct +import com.ivy.wallet.domain.action.account.AccountsAct +import com.ivy.wallet.domain.action.account.CalcAccBalanceAct +import com.ivy.wallet.domain.action.account.CalcAccIncomeExpenseAct +import com.ivy.wallet.domain.action.category.CategoriesAct +import com.ivy.wallet.domain.action.exchange.ExchangeAct +import com.ivy.wallet.domain.action.settings.BaseCurrencyAct +import com.ivy.wallet.domain.action.transaction.TrnsWithDateDivsAct import com.ivy.wallet.domain.data.TransactionHistoryItem import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.data.entity.Transaction -import com.ivy.wallet.domain.fp.account.calculateAccountBalance -import com.ivy.wallet.domain.fp.account.calculateAccountIncomeExpense -import com.ivy.wallet.domain.fp.data.WalletDAOs -import com.ivy.wallet.domain.fp.exchangeToBaseCurrency -import com.ivy.wallet.domain.fp.wallet.baseCurrencyCode -import com.ivy.wallet.domain.fp.wallet.withDateDividers -import com.ivy.wallet.domain.logic.* -import com.ivy.wallet.domain.logic.currency.ExchangeRatesLogic -import com.ivy.wallet.domain.sync.uploader.AccountUploader -import com.ivy.wallet.domain.sync.uploader.CategoryUploader +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.data.core.Transaction +import com.ivy.wallet.domain.deprecated.logic.* +import com.ivy.wallet.domain.deprecated.logic.currency.ExchangeRatesLogic +import com.ivy.wallet.domain.deprecated.sync.uploader.AccountUploader +import com.ivy.wallet.domain.deprecated.sync.uploader.CategoryUploader +import com.ivy.wallet.domain.pure.data.WalletDAOs +import com.ivy.wallet.domain.pure.exchange.ExchangeData +import com.ivy.wallet.io.persistence.SharedPrefs import com.ivy.wallet.io.persistence.dao.* +import com.ivy.wallet.stringRes import com.ivy.wallet.ui.ItemStatistic import com.ivy.wallet.ui.IvyWalletCtx import com.ivy.wallet.ui.onboarding.model.TimePeriod @@ -50,6 +58,15 @@ class ItemStatisticViewModel @Inject constructor( private val accountCreator: AccountCreator, private val plannedPaymentsLogic: PlannedPaymentsLogic, private val exchangeRatesLogic: ExchangeRatesLogic, + private val sharedPrefs: SharedPrefs, + private val categoriesAct: CategoriesAct, + private val accountsAct: AccountsAct, + private val accTrnsAct: AccTrnsAct, + private val trnsWithDateDivsAct: TrnsWithDateDivsAct, + private val baseCurrencyAct: BaseCurrencyAct, + private val calcAccBalanceAct: CalcAccBalanceAct, + private val calcAccIncomeExpenseAct: CalcAccIncomeExpenseAct, + private val exchangeAct: ExchangeAct ) : ViewModel() { private val _period = MutableStateFlow(ivyContext.selectedPeriod) @@ -132,12 +149,12 @@ class ItemStatisticViewModel @Inject constructor( viewModelScope.launch { _period.value = period ?: ivyContext.selectedPeriod - val baseCurrency = ioThread { baseCurrencyCode(settingsDao) } + val baseCurrency = baseCurrencyAct(Unit) _baseCurrency.value = baseCurrency _currency.value = baseCurrency - _categories.value = ioThread { categoryDao.findAll() } - _accounts.value = ioThread { accountDao.findAll() } + _categories.value = categoriesAct(Unit) + _accounts.value = accountsAct(Unit) _initWithTransactions.value = false when { @@ -175,7 +192,7 @@ class ItemStatisticViewModel @Inject constructor( private suspend fun initForAccount(accountId: UUID) { val account = ioThread { - accountDao.findById(accountId) ?: error("account not found") + accountDao.findById(accountId)?.toDomain() ?: error("account not found") } _account.value = account val range = period.value.toRange(ivyContext.startDayOfMonth) @@ -184,37 +201,50 @@ class ItemStatisticViewModel @Inject constructor( _currency.value = account.currency!! } - val balance = ioThread { - calculateAccountBalance( - transactionDao = walletDAOs.transactionDao, - accountId = accountId - ).toDouble() - } + val balance = calcAccBalanceAct( + CalcAccBalanceAct.Input( + account = account + ) + ).balance.toDouble() _balance.value = balance if (baseCurrency.value != currency.value) { - _balanceBaseCurrency.value = ioThread { - exchangeToBaseCurrency( - exchangeRateDao = exchangeRateDao, - baseCurrencyCode = baseCurrency.value, - fromCurrencyCode = currency.value.toOption(), - fromAmount = balance.toBigDecimal() - ).orNull()?.toDouble() - } + _balanceBaseCurrency.value = exchangeAct( + ExchangeAct.Input( + data = ExchangeData( + baseCurrency = baseCurrency.value, + fromCurrency = currency.value.toOption() + ), + amount = balance.toBigDecimal() + ) + ).orNull()?.toDouble() } - val incomeExpensePair = ioThread { - calculateAccountIncomeExpense( - transactionDao = transactionDao, - accountId = accountId, - range = range.toCloseTimeRange() + val includeTransfersInCalc = + sharedPrefs.getBoolean(SharedPrefs.TRANSFERS_AS_INCOME_EXPENSE, false) + + val incomeExpensePair = calcAccIncomeExpenseAct( + CalcAccIncomeExpenseAct.Input( + account = account, + range = range.toCloseTimeRange(), + includeTransfersInCalc = includeTransfersInCalc ) - } + ).incomeExpensePair _income.value = incomeExpensePair.income.toDouble() _expenses.value = incomeExpensePair.expense.toDouble() - _history.value = ioThread { - accountLogic.historyForAccount(account, range) - } + _history.value = (accTrnsAct then { + trnsWithDateDivsAct( + TrnsWithDateDivsAct.Input( + baseCurrency = baseCurrency.value, + transactions = it + ) + ) + })( + AccTrnsAct.Input( + accountId = account.id, + range = range.toCloseTimeRange() + ) + ) //Upcoming _upcomingIncome.value = ioThread { @@ -242,7 +272,7 @@ class ItemStatisticViewModel @Inject constructor( private suspend fun initForCategory(categoryId: UUID, accountFilterList: List) { val accountFilterSet = accountFilterList.toSet() val category = ioThread { - categoryDao.findById(categoryId) ?: error("category not found") + categoryDao.findById(categoryId)?.toDomain() ?: error("category not found") } _category.value = category val range = period.value.toRange(ivyContext.startDayOfMonth) @@ -306,7 +336,7 @@ class ItemStatisticViewModel @Inject constructor( val accountFilterSet = accountFilterList.toSet() val category = ioThread { - categoryDao.findById(categoryId) ?: error("category not found") + categoryDao.findById(categoryId)?.toDomain() ?: error("category not found") } _category.value = category val range = period.value.toRange(ivyContext.startDayOfMonth) @@ -429,7 +459,7 @@ class ItemStatisticViewModel @Inject constructor( val accountTransferCategoryEnabled = categoryId != null if (accountTransferCategoryEnabled) - _category.value = Category("Account Transfers") + _category.value = Category(stringRes(R.string.account_transfers)) val trans = transactions.filter { it.categoryId == null && (accountFilterIdSet.contains(it.accountId) || accountFilterIdSet.contains( @@ -463,22 +493,21 @@ class ItemStatisticViewModel @Inject constructor( _income.value - _expenses.value + if (accountTransferCategoryEnabled) trans.filter { it.type == TransactionType.TRANSFER } - .sumOf { - exchangeRatesLogic.toAmountBaseCurrency( - transaction = it, - baseCurrency = baseCurrency.value, - accounts = walletDAOs.accountDao.findAll() - ) - } else 0.0 - } - - _history.value = ioThread { - trans.withDateDividers( - exchangeRateDao = exchangeRateDao, - accountDao = walletDAOs.accountDao, - baseCurrencyCode = baseCurrency.value + .sumOf { + exchangeRatesLogic.toAmountBaseCurrency( + transaction = it, + baseCurrency = baseCurrency.value, + accounts = accountsAct(Unit) + ) + } else 0.0 + } + + _history.value = trnsWithDateDivsAct( + TrnsWithDateDivsAct.Input( + baseCurrency = baseCurrency.value, + transactions = transactions ) - } + ) } private fun reset() { diff --git a/app/src/main/java/com/ivy/wallet/ui/test/TestScreen.kt b/app/src/main/java/com/ivy/wallet/ui/test/TestScreen.kt index 509396dd95..4a80bb1000 100644 --- a/app/src/main/java/com/ivy/wallet/ui/test/TestScreen.kt +++ b/app/src/main/java/com/ivy/wallet/ui/test/TestScreen.kt @@ -9,7 +9,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.ivy.design.api.navigation -import com.ivy.wallet.domain.data.entity.User +import com.ivy.wallet.domain.data.core.User import com.ivy.wallet.ui.AnalyticsReport import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.Test diff --git a/app/src/main/java/com/ivy/wallet/ui/test/TestViewModel.kt b/app/src/main/java/com/ivy/wallet/ui/test/TestViewModel.kt index d8bd81a844..a988e7664b 100644 --- a/app/src/main/java/com/ivy/wallet/ui/test/TestViewModel.kt +++ b/app/src/main/java/com/ivy/wallet/ui/test/TestViewModel.kt @@ -3,9 +3,9 @@ package com.ivy.wallet.ui.test import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.ivy.wallet.domain.data.entity.User -import com.ivy.wallet.domain.logic.notification.TransactionReminderLogic -import com.ivy.wallet.domain.sync.item.CategorySync +import com.ivy.wallet.domain.data.core.User +import com.ivy.wallet.domain.deprecated.logic.notification.TransactionReminderLogic +import com.ivy.wallet.domain.deprecated.sync.item.CategorySync import com.ivy.wallet.io.network.IvySession import com.ivy.wallet.io.persistence.dao.UserDao import com.ivy.wallet.utils.TestIdlingResource @@ -32,7 +32,7 @@ class TestViewModel @Inject constructor( _user.value = ioThread { val userId = ivySession.getUserIdSafe() - if (userId != null) userDao.findById(userId) else null + if (userId != null) userDao.findById(userId)?.toDomain() else null } TestIdlingResource.decrement() diff --git a/app/src/main/java/com/ivy/wallet/ui/theme/components/BudgetBattery.kt b/app/src/main/java/com/ivy/wallet/ui/theme/components/BudgetBattery.kt index 4ddee7cc19..c19ea00785 100644 --- a/app/src/main/java/com/ivy/wallet/ui/theme/components/BudgetBattery.kt +++ b/app/src/main/java/com/ivy/wallet/ui/theme/components/BudgetBattery.kt @@ -10,6 +10,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -106,9 +107,9 @@ fun BudgetBattery( Text( text = when { percentSpent <= 1 -> { - "Left to spend" + stringResource(R.string.left_to_spend) } - else -> "Budget exceeded by" + else -> stringResource(R.string.budget_exceeded_by) }, style = UI.typo.c.style( color = textColor, diff --git a/app/src/main/java/com/ivy/wallet/ui/theme/components/BufferBattery.kt b/app/src/main/java/com/ivy/wallet/ui/theme/components/BufferBattery.kt index a9b97c7385..b1dec93850 100644 --- a/app/src/main/java/com/ivy/wallet/ui/theme/components/BufferBattery.kt +++ b/app/src/main/java/com/ivy/wallet/ui/theme/components/BufferBattery.kt @@ -10,6 +10,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -96,7 +97,8 @@ fun BufferBattery( Column { Text( - text = if (bufferExceeded) "Buffer exceeded by" else "Left to spend", + text = if (bufferExceeded) stringResource(R.string.buffer_exceeded_by) else stringResource( + R.string.left_to_spend), style = UI.typo.c.style( color = textColor, fontWeight = FontWeight.ExtraBold diff --git a/app/src/main/java/com/ivy/wallet/ui/theme/components/ChangeTransactionTypeModal.kt b/app/src/main/java/com/ivy/wallet/ui/theme/components/ChangeTransactionTypeModal.kt index 8a4cbd459c..8a68ae951e 100644 --- a/app/src/main/java/com/ivy/wallet/ui/theme/components/ChangeTransactionTypeModal.kt +++ b/app/src/main/java/com/ivy/wallet/ui/theme/components/ChangeTransactionTypeModal.kt @@ -11,6 +11,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -30,7 +31,7 @@ import java.util.* @Composable fun BoxWithConstraintsScope.ChangeTransactionTypeModal( - title: String = "Set transaction type", + title: String = stringResource(R.string.set_transaction_type), visible: Boolean, includeTransferType: Boolean, initialType: TransactionType, @@ -164,9 +165,9 @@ private fun TransactionTypeButton( Text( text = when (transactionType) { - TransactionType.INCOME -> "Income" - TransactionType.EXPENSE -> "Expense" - TransactionType.TRANSFER -> "Transfer" + TransactionType.INCOME -> stringResource(R.string.income) + TransactionType.EXPENSE -> stringResource(R.string.expense) + TransactionType.TRANSFER -> stringResource(R.string.transfer) }, style = UI.typo.b1.style( color = textColor @@ -182,7 +183,7 @@ private fun TransactionTypeButton( ) Text( - text = "Selected", + text = stringResource(R.string.selected), style = UI.typo.b2.style( fontWeight = FontWeight.SemiBold, color = textSelectedColor diff --git a/app/src/main/java/com/ivy/wallet/ui/theme/components/CurrencyPicker.kt b/app/src/main/java/com/ivy/wallet/ui/theme/components/CurrencyPicker.kt index dd0fb2b5f4..bd6f9b8ee9 100644 --- a/app/src/main/java/com/ivy/wallet/ui/theme/components/CurrencyPicker.kt +++ b/app/src/main/java/com/ivy/wallet/ui/theme/components/CurrencyPicker.kt @@ -22,6 +22,7 @@ import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.platform.LocalView import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardCapitalization @@ -148,7 +149,7 @@ private fun SearchInput( if (searchTextFieldValue.text.isEmpty()) { //Hint Text( - text = "Search (USD, EUR, GBP, BTC, etc)", + text = stringResource(R.string.search_currency), style = UI.typo.c.style( fontWeight = FontWeight.Bold ) @@ -234,7 +235,7 @@ private fun SelectedCurrencyCard( ) Text( - text = if (preselected) "Pre-selected" else "Selected", + text = if (preselected) stringResource(R.string.pre_selected) else stringResource(R.string.selected), style = UI.typo.b2.style( color = White, fontWeight = FontWeight.SemiBold @@ -265,7 +266,7 @@ private fun CurrencyList( var lastFirstLetter: String? = null for (currency in currencies) { - val firstLetter = if (currency.isCrypto) "Crypto" else currency.code.first().toString() + val firstLetter = if (currency.isCrypto) stringResource(R.string.crypto) else currency.code.first().toString() if (firstLetter != lastFirstLetter) { currenciesWithLetters.add( LetterDivider( diff --git a/app/src/main/java/com/ivy/wallet/ui/theme/components/CustomExchangeRateCard.kt b/app/src/main/java/com/ivy/wallet/ui/theme/components/CustomExchangeRateCard.kt index e0d575bfee..3c6f1e53de 100644 --- a/app/src/main/java/com/ivy/wallet/ui/theme/components/CustomExchangeRateCard.kt +++ b/app/src/main/java/com/ivy/wallet/ui/theme/components/CustomExchangeRateCard.kt @@ -8,6 +8,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -22,7 +23,7 @@ import com.ivy.wallet.utils.format @Composable fun CustomExchangeRateCard( modifier: Modifier = Modifier, - title: String = "Exchange Rate", + title: String = stringResource(R.string.exchange_rate), fromCurrencyCode: String, toCurrencyCode: String, exchangeRate: Double, diff --git a/app/src/main/java/com/ivy/wallet/ui/theme/components/IvyColorPicker.kt b/app/src/main/java/com/ivy/wallet/ui/theme/components/IvyColorPicker.kt index a9c74a1140..a66fb317c8 100644 --- a/app/src/main/java/com/ivy/wallet/ui/theme/components/IvyColorPicker.kt +++ b/app/src/main/java/com/ivy/wallet/ui/theme/components/IvyColorPicker.kt @@ -15,6 +15,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -63,7 +64,7 @@ fun ColumnScope.IvyColorPicker( ) { Text( modifier = Modifier.padding(horizontal = 32.dp), - text = "Choose color", + text = stringResource(R.string.choose_color), style = UI.typo.b2.style( color = UI.colors.pureInverse, fontWeight = FontWeight.ExtraBold diff --git a/app/src/main/java/com/ivy/wallet/ui/theme/components/IvyDescriptionTextField.kt b/app/src/main/java/com/ivy/wallet/ui/theme/components/IvyDescriptionTextField.kt index e0a61cc8b4..17cce29ccd 100644 --- a/app/src/main/java/com/ivy/wallet/ui/theme/components/IvyDescriptionTextField.kt +++ b/app/src/main/java/com/ivy/wallet/ui/theme/components/IvyDescriptionTextField.kt @@ -63,7 +63,7 @@ fun IvyDescriptionTextField( modifier = textModifier, value = value, onValueChange = onValueChanged, - textStyle = UI.typo.b2.style( + textStyle = UI.typo.nB2.style( color = textColor, fontWeight = fontWeight, textAlign = TextAlign.Start diff --git a/app/src/main/java/com/ivy/wallet/ui/theme/components/ReorderModal.kt b/app/src/main/java/com/ivy/wallet/ui/theme/components/ReorderModal.kt index 295f66a29d..3eb052a491 100644 --- a/app/src/main/java/com/ivy/wallet/ui/theme/components/ReorderModal.kt +++ b/app/src/main/java/com/ivy/wallet/ui/theme/components/ReorderModal.kt @@ -14,6 +14,7 @@ import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView @@ -40,7 +41,7 @@ fun BoxScope.ReorderModalSingleType( TitleContent: @Composable ColumnScope.() -> Unit = { Text( modifier = Modifier.padding(start = 32.dp), - text = "Reorder", + text = stringResource(R.string.reorder), style = UI.typo.b1.style( UI.colors.pureInverse, FontWeight.ExtraBold @@ -80,7 +81,7 @@ fun BoxScope.ReorderModal( TitleContent: @Composable ColumnScope.() -> Unit = { Text( modifier = Modifier.padding(start = 32.dp), - text = "Reorder", + text = stringResource(R.string.reorder), style = UI.typo.b1.style( UI.colors.pureInverse, FontWeight.ExtraBold diff --git a/app/src/main/java/com/ivy/wallet/ui/theme/components/WrapContentRow.kt b/app/src/main/java/com/ivy/wallet/ui/theme/components/WrapContentRow.kt index 95ffd68a5c..22baf4a433 100644 --- a/app/src/main/java/com/ivy/wallet/ui/theme/components/WrapContentRow.kt +++ b/app/src/main/java/com/ivy/wallet/ui/theme/components/WrapContentRow.kt @@ -18,7 +18,7 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.ivy.design.l0_system.UI -import com.ivy.wallet.domain.data.entity.Category +import com.ivy.wallet.domain.data.core.Category import com.ivy.wallet.ui.IvyWalletPreview diff --git a/app/src/main/java/com/ivy/wallet/ui/theme/modal/AddKeywordModal.kt b/app/src/main/java/com/ivy/wallet/ui/theme/modal/AddKeywordModal.kt index 5400ff26a8..143d68a367 100644 --- a/app/src/main/java/com/ivy/wallet/ui/theme/modal/AddKeywordModal.kt +++ b/app/src/main/java/com/ivy/wallet/ui/theme/modal/AddKeywordModal.kt @@ -9,11 +9,13 @@ import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style +import com.ivy.wallet.R import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.theme.components.IvyTitleTextField import com.ivy.wallet.utils.onScreenStart @@ -45,7 +47,7 @@ fun BoxWithConstraintsScope.AddKeywordModal( Text( modifier = Modifier.padding(start = 32.dp), - text = "Add keyword", + text = stringResource(R.string.add_keyword), style = UI.typo.b1.style( fontWeight = FontWeight.ExtraBold, color = UI.colors.pureInverse @@ -66,7 +68,7 @@ fun BoxWithConstraintsScope.AddKeywordModal( .focusRequester(inputFocus), dividerModifier = Modifier.padding(horizontal = 24.dp), value = modalKeyword, - hint = "Keyword" + hint = stringResource(R.string.keyword) ) { modalKeyword = it } diff --git a/app/src/main/java/com/ivy/wallet/ui/theme/modal/BudgetModal.kt b/app/src/main/java/com/ivy/wallet/ui/theme/modal/BudgetModal.kt index 4beff2d26e..53dfa94db7 100644 --- a/app/src/main/java/com/ivy/wallet/ui/theme/modal/BudgetModal.kt +++ b/app/src/main/java/com/ivy/wallet/ui/theme/modal/BudgetModal.kt @@ -23,10 +23,10 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.Budget -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.logic.model.CreateBudgetData +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Budget +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.deprecated.logic.model.CreateBudgetData import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.reports.ListItem import com.ivy.wallet.ui.theme.Green diff --git a/app/src/main/java/com/ivy/wallet/ui/theme/modal/BufferModal.kt b/app/src/main/java/com/ivy/wallet/ui/theme/modal/BufferModal.kt index 12957f3391..b25710676c 100644 --- a/app/src/main/java/com/ivy/wallet/ui/theme/modal/BufferModal.kt +++ b/app/src/main/java/com/ivy/wallet/ui/theme/modal/BufferModal.kt @@ -6,8 +6,10 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.runtime.* import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.ivy.design.l0_system.UI +import com.ivy.wallet.R import com.ivy.wallet.ui.theme.components.BufferBattery import com.ivy.wallet.ui.theme.modal.edit.AmountModal import java.util.* @@ -56,7 +58,7 @@ fun BoxWithConstraintsScope.BufferModal( Spacer(Modifier.height(24.dp)) ModalAmountSection( - label = "Edit Savings goal", + label = stringResource(R.string.edit_savings_goal), currency = modal?.currency ?: "", amount = newBufferAmount ) { diff --git a/app/src/main/java/com/ivy/wallet/ui/theme/modal/ChooseIconModal.kt b/app/src/main/java/com/ivy/wallet/ui/theme/modal/ChooseIconModal.kt index 90d168417a..df9544e299 100644 --- a/app/src/main/java/com/ivy/wallet/ui/theme/modal/ChooseIconModal.kt +++ b/app/src/main/java/com/ivy/wallet/ui/theme/modal/ChooseIconModal.kt @@ -14,9 +14,11 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalView import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.ivy.design.l0_system.UI +import com.ivy.wallet.R import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.theme.Ivy import com.ivy.wallet.ui.theme.components.ItemIconS @@ -68,7 +70,7 @@ fun BoxWithConstraintsScope.ChooseIconModal( item { Spacer(Modifier.height(32.dp)) - ModalTitle(text = "Choose icon") + ModalTitle(text = stringResource(R.string.choose_icon)) Spacer(Modifier.height(32.dp)) } diff --git a/app/src/main/java/com/ivy/wallet/ui/theme/modal/ChoosePeriodModal.kt b/app/src/main/java/com/ivy/wallet/ui/theme/modal/ChoosePeriodModal.kt index 9ac357ba99..be9ba2e340 100644 --- a/app/src/main/java/com/ivy/wallet/ui/theme/modal/ChoosePeriodModal.kt +++ b/app/src/main/java/com/ivy/wallet/ui/theme/modal/ChoosePeriodModal.kt @@ -11,6 +11,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalView +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -136,7 +137,7 @@ private fun ChooseMonth( Text( modifier = Modifier .padding(start = 32.dp), - text = "Choose month", + text = stringResource(R.string.choose_month), style = UI.typo.b1.style( color = if (selectedMonthYear != null) UI.colors.pureInverse else Gray, fontWeight = FontWeight.ExtraBold @@ -260,7 +261,7 @@ private fun FromToRange( Text( modifier = Modifier .padding(start = 32.dp), - text = "or custom range", + text = stringResource(R.string.or_custom_range), style = UI.typo.b1.style( color = if (timeRange != null) UI.colors.pureInverse else Gray, fontWeight = FontWeight.ExtraBold @@ -349,7 +350,7 @@ private fun IntervalFromToDate( .padding( vertical = 16.dp, ), - text = if (border == IntervalBorder.FROM) "From" else "To", + text = if (border == IntervalBorder.FROM) stringResource(R.string.from) else stringResource(R.string.to), style = UI.typo.b2.style( fontWeight = FontWeight.ExtraBold, color = if (dateTime != null) Green else UI.colors.pureInverse @@ -363,7 +364,7 @@ private fun IntervalFromToDate( } Text( - text = dateTime?.toLocalDate()?.formatDateOnlyWithYear() ?: "Add date", + text = dateTime?.toLocalDate()?.formatDateOnlyWithYear() ?: stringResource(R.string.add_date), style = UI.typo.nB2.style( fontWeight = FontWeight.Bold, color = if (dateTime != null) UI.colors.pureInverse else Gray @@ -414,7 +415,7 @@ private fun LastNPeriod( Text( modifier = Modifier .padding(start = 32.dp), - text = "or in the last", + text = stringResource(R.string.or_in_the_last), style = UI.typo.b1.style( color = if (lastNTimeRange != null) UI.colors.pureInverse else Gray, fontWeight = FontWeight.ExtraBold @@ -460,7 +461,7 @@ private fun AllTime( Text( modifier = Modifier .padding(start = 32.dp), - text = "or all time", + text = stringResource(R.string.or_all_time), style = UI.typo.b1.style( color = if (active) UI.colors.pureInverse else Gray, fontWeight = FontWeight.ExtraBold @@ -472,7 +473,7 @@ private fun AllTime( MonthButton( modifier = Modifier.padding(start = 32.dp), selected = active, - text = if (active) "Unselect All Time" else "Select All Time" + text = if (active) stringResource(R.string.unselect_all_time) else stringResource(R.string.select_all_time) ) { onSelected( if (active) { diff --git a/app/src/main/java/com/ivy/wallet/ui/theme/modal/ChooseStartDateOfMonthModal.kt b/app/src/main/java/com/ivy/wallet/ui/theme/modal/ChooseStartDateOfMonthModal.kt index 7cd6caa11e..0f520f7d0f 100644 --- a/app/src/main/java/com/ivy/wallet/ui/theme/modal/ChooseStartDateOfMonthModal.kt +++ b/app/src/main/java/com/ivy/wallet/ui/theme/modal/ChooseStartDateOfMonthModal.kt @@ -10,12 +10,14 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style +import com.ivy.wallet.R import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.theme.Ivy import com.ivy.wallet.ui.theme.White @@ -39,7 +41,7 @@ fun BoxWithConstraintsScope.ChooseStartDateOfMonthModal( ) { Spacer(Modifier.height(32.dp)) - ModalTitle(text = "Choose start date of month") + ModalTitle(text = stringResource(R.string.choose_start_date_of_month)) Spacer(Modifier.height(32.dp)) diff --git a/app/src/main/java/com/ivy/wallet/ui/theme/modal/CurrencyModal.kt b/app/src/main/java/com/ivy/wallet/ui/theme/modal/CurrencyModal.kt index b74e356d4b..3bd894de98 100644 --- a/app/src/main/java/com/ivy/wallet/ui/theme/modal/CurrencyModal.kt +++ b/app/src/main/java/com/ivy/wallet/ui/theme/modal/CurrencyModal.kt @@ -6,11 +6,13 @@ import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style +import com.ivy.wallet.R import com.ivy.wallet.domain.data.IvyCurrency import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.theme.Gray @@ -61,7 +63,7 @@ fun BoxWithConstraintsScope.CurrencyModal( Spacer(Modifier.weight(1f)) Text( - text = "supports crypto", + text = stringResource(R.string.supports_crypto), style = UI.typo.c.style( fontWeight = FontWeight.ExtraBold, color = Gray diff --git a/app/src/main/java/com/ivy/wallet/ui/theme/modal/DeleteModal.kt b/app/src/main/java/com/ivy/wallet/ui/theme/modal/DeleteModal.kt index 44ad40ca25..2334dc47e3 100644 --- a/app/src/main/java/com/ivy/wallet/ui/theme/modal/DeleteModal.kt +++ b/app/src/main/java/com/ivy/wallet/ui/theme/modal/DeleteModal.kt @@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import com.ivy.design.l0_system.UI @@ -22,7 +23,7 @@ fun BoxWithConstraintsScope.DeleteModal( title: String, description: String, visible: Boolean, - buttonText: String = "Delete", + buttonText: String = stringResource(R.string.delete), iconStart: Int = R.drawable.ic_delete, dismiss: () -> Unit, onDelete: () -> Unit, diff --git a/app/src/main/java/com/ivy/wallet/ui/theme/modal/IvyModalComponents.kt b/app/src/main/java/com/ivy/wallet/ui/theme/modal/IvyModalComponents.kt index 3df09fb20b..bca8ef7609 100644 --- a/app/src/main/java/com/ivy/wallet/ui/theme/modal/IvyModalComponents.kt +++ b/app/src/main/java/com/ivy/wallet/ui/theme/modal/IvyModalComponents.kt @@ -7,6 +7,7 @@ import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import com.ivy.design.l0_system.UI @@ -55,7 +56,7 @@ fun ModalDynamicPrimaryAction( @Composable fun ModalSet( modifier: Modifier = Modifier, - label: String = "Set", + label: String = stringResource(R.string.set), enabled: Boolean = true, onClick: () -> Unit ) { @@ -110,7 +111,7 @@ fun ModalSave( ) { ModalPositiveButton( modifier = modifier, - text = "Save", + text = stringResource(R.string.save), iconStart = R.drawable.ic_save, enabled = enabled, onClick = onClick @@ -123,7 +124,7 @@ fun ModalAdd( onClick: () -> Unit ) { ModalPositiveButton( - text = "Add", + text = stringResource(R.string.add), iconStart = R.drawable.ic_plus, enabled = enabled, onClick = onClick @@ -136,7 +137,7 @@ fun ModalCreate( onClick: () -> Unit ) { ModalPositiveButton( - text = "Create", + text = stringResource(R.string.create), iconStart = R.drawable.ic_plus, enabled = enabled, onClick = onClick @@ -226,7 +227,7 @@ fun ModalTitle( @Composable fun ModalSkip( - text: String = "Skip", + text: String = stringResource(R.string.skip), onClick: () -> Unit ) { IvyOutlinedButton( diff --git a/app/src/main/java/com/ivy/wallet/ui/theme/modal/LoanModal.kt b/app/src/main/java/com/ivy/wallet/ui/theme/modal/LoanModal.kt index 4dcafc784e..42e88a716b 100644 --- a/app/src/main/java/com/ivy/wallet/ui/theme/modal/LoanModal.kt +++ b/app/src/main/java/com/ivy/wallet/ui/theme/modal/LoanModal.kt @@ -15,6 +15,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.style.TextAlign @@ -25,10 +26,10 @@ import com.ivy.design.l0_system.style import com.ivy.wallet.R import com.ivy.wallet.domain.data.IvyCurrency import com.ivy.wallet.domain.data.LoanType -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.Loan -import com.ivy.wallet.domain.logic.model.CreateAccountData -import com.ivy.wallet.domain.logic.model.CreateLoanData +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Loan +import com.ivy.wallet.domain.deprecated.logic.model.CreateAccountData +import com.ivy.wallet.domain.deprecated.logic.model.CreateLoanData import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.theme.* import com.ivy.wallet.ui.theme.components.ItemIconSDefaultIcon @@ -145,13 +146,13 @@ fun BoxWithConstraintsScope.LoanModal( Spacer(Modifier.height(32.dp)) ModalTitle( - text = if (modal?.loan != null) "Edit loan" else "New loan", + text = if (modal?.loan != null) stringResource(R.string.edit_loan) else stringResource(R.string.new_loan), ) Spacer(Modifier.height(24.dp)) IconNameRow( - hint = "Loan name", + hint = stringResource(R.string.loan_name), defaultIcon = R.drawable.ic_custom_loan_m, color = color, icon = icon, @@ -183,7 +184,7 @@ fun BoxWithConstraintsScope.LoanModal( Text( modifier = Modifier.padding(horizontal = 32.dp), - text = "Associated Account", + text = stringResource(R.string.associated_account), style = UI.typo.b2.style( color = UI.colors.pureInverse, fontWeight = FontWeight.ExtraBold @@ -215,7 +216,7 @@ fun BoxWithConstraintsScope.LoanModal( modifier = Modifier .padding(start = 16.dp) .align(Alignment.Start), - text = "Create a Main Transaction", + text = stringResource(R.string.create_main_transaction), checked = createLoanTrans ) { createLoanTrans = it @@ -224,7 +225,7 @@ fun BoxWithConstraintsScope.LoanModal( Spacer(modifier = Modifier.height(24.dp)) ModalAmountSection( - label = "ENTER LOAN AMOUNT", + label = stringResource(R.string.enter_loan_amount_uppercase), currency = currencyCode, amount = amount, amountPaddingTop = 40.dp, @@ -248,7 +249,7 @@ fun BoxWithConstraintsScope.LoanModal( } CurrencyModal( - title = "Choose currency", + title = stringResource(R.string.choose_currency), initialCurrency = IvyCurrency.fromCode(currencyCode), visible = currencyModalVisible, dismiss = { currencyModalVisible = false } @@ -276,10 +277,9 @@ fun BoxWithConstraintsScope.LoanModal( DeleteModal( visible = accountChangeModal, - title = "Confirm Account Change", - description = "Note: You are trying to change the account associated with the loan with an account of different currency, " + - "\nAll the loan records will be re-calculated based on today's exchanges rates ", - buttonText = "Confirm", + title = stringResource(R.string.confirm_account_change), + description = stringResource(R.string.confirm_account_change_warning), + buttonText = stringResource(R.string.confirm), iconStart = R.drawable.ic_agreed, dismiss = { selectedAcc = modal?.selectedAccount ?: selectedAcc @@ -433,7 +433,7 @@ private fun AddAccount( Text( modifier = Modifier.padding(vertical = 10.dp), - text = "Add account", + text = stringResource(R.string.add_account), style = UI.typo.b2.style( color = UI.colors.pureInverse, fontWeight = FontWeight.ExtraBold @@ -453,7 +453,7 @@ private fun ColumnScope.LoanTypePicker( ) { Text( modifier = Modifier.padding(horizontal = 32.dp), - text = "Loan type", + text = stringResource(R.string.loan_type), style = UI.typo.b2.style( color = UI.colors.pureInverse, fontWeight = FontWeight.ExtraBold @@ -473,7 +473,7 @@ private fun ColumnScope.LoanTypePicker( SelectorButton( selected = type == LoanType.BORROW, - label = "Borrow money" + label = stringResource(R.string.borrow_money) ) { onTypeSelected(LoanType.BORROW) } @@ -482,7 +482,7 @@ private fun ColumnScope.LoanTypePicker( SelectorButton( selected = type == LoanType.LEND, - label = "Lend money" + label = stringResource(R.string.lend_money) ) { onTypeSelected(LoanType.LEND) } diff --git a/app/src/main/java/com/ivy/wallet/ui/theme/modal/LoanRecordModal.kt b/app/src/main/java/com/ivy/wallet/ui/theme/modal/LoanRecordModal.kt index 5ba9bf7c3b..5c1e38fe50 100644 --- a/app/src/main/java/com/ivy/wallet/ui/theme/modal/LoanRecordModal.kt +++ b/app/src/main/java/com/ivy/wallet/ui/theme/modal/LoanRecordModal.kt @@ -13,6 +13,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.tooling.preview.Preview @@ -20,11 +21,11 @@ import androidx.compose.ui.unit.dp import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style import com.ivy.wallet.R -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.LoanRecord -import com.ivy.wallet.domain.logic.model.CreateAccountData -import com.ivy.wallet.domain.logic.model.CreateLoanRecordData -import com.ivy.wallet.domain.logic.model.EditLoanRecordData +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.LoanRecord +import com.ivy.wallet.domain.deprecated.logic.model.CreateAccountData +import com.ivy.wallet.domain.deprecated.logic.model.CreateLoanRecordData +import com.ivy.wallet.domain.deprecated.logic.model.EditLoanRecordData import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.ivyWalletCtx import com.ivy.wallet.ui.theme.components.ItemIconSDefaultIcon @@ -141,7 +142,8 @@ fun BoxWithConstraintsScope.LoanRecordModal( verticalAlignment = Alignment.CenterVertically ) { ModalTitle( - text = if (initialRecord != null) "Edit record" else "New record" + text = if (initialRecord != null) stringResource(R.string.edit_record) else stringResource( + R.string.new_record) ) if (initialRecord != null) { @@ -158,7 +160,7 @@ fun BoxWithConstraintsScope.LoanRecordModal( Spacer(Modifier.height(24.dp)) ModalNameInput( - hint = "Note", + hint = stringResource(R.string.note), autoFocusKeyboard = false, textFieldValue = noteTextFieldValue, setTextFieldValue = { @@ -179,7 +181,7 @@ fun BoxWithConstraintsScope.LoanRecordModal( Text( modifier = Modifier.padding(horizontal = 32.dp), - text = "Associated Account", + text = stringResource(R.string.associated_account), style = UI.typo.b2.style( color = UI.colors.pureInverse, fontWeight = FontWeight.ExtraBold @@ -217,7 +219,7 @@ fun BoxWithConstraintsScope.LoanRecordModal( modifier = Modifier .padding(start = 16.dp) .align(Alignment.Start), - text = "Create a Main Transaction", + text = stringResource(R.string.create_main_transaction), checked = createLoanRecordTrans ) { createLoanRecordTrans = it @@ -227,7 +229,7 @@ fun BoxWithConstraintsScope.LoanRecordModal( modifier = Modifier .padding(start = 16.dp) .align(Alignment.Start), - text = "Mark as Interest", + text = stringResource(R.string.mark_as_interest), checked = loanInterest ) { loanInterest = it @@ -238,7 +240,7 @@ fun BoxWithConstraintsScope.LoanRecordModal( modifier = Modifier .padding(start = 16.dp, end = 8.dp) .align(Alignment.Start), - text = "Recalculate Amount with today's Currency exchange Rates", + text = stringResource(R.string.recalculate_amount_with_today_exchange_rates), checked = reCalculate ) { reCalculate = it @@ -248,7 +250,7 @@ fun BoxWithConstraintsScope.LoanRecordModal( Spacer(modifier = Modifier.height(16.dp)) ModalAmountSection( - label = "ENTER RECORD AMOUNT", + label = stringResource(R.string.enter_record_amount_uppercase), currency = currencyCode, amount = amount, amountPaddingTop = 40.dp, @@ -273,8 +275,8 @@ fun BoxWithConstraintsScope.LoanRecordModal( DeleteModal( visible = deleteModalVisible, - title = "Confirm deletion", - description = "Are you sure that you want to delete \"${noteTextFieldValue.text}\" record?", + title = stringResource(R.string.confirm_deletion), + description = stringResource(R.string.record_deletion_warning, noteTextFieldValue.text), dismiss = { deleteModalVisible = false } ) { if (initialRecord != null) { @@ -296,10 +298,9 @@ fun BoxWithConstraintsScope.LoanRecordModal( DeleteModal( visible = accountChangeConformationModal, - title = "Confirm Account Change", - description = "Note: You are trying to change the account associated with the loan record with an account of different currency" + - "\nThe amount will be re-calculated based on today's exchanges rates ", - buttonText = "Confirm", + title = stringResource(R.string.confirm_account_change), + description = stringResource(R.string.account_change_warning), + buttonText = stringResource(R.string.confirm), iconStart = R.drawable.ic_agreed, dismiss = { selectedAcc = modal?.selectedAccount ?: selectedAcc @@ -540,7 +541,7 @@ private fun AddAccount( Text( modifier = Modifier.padding(vertical = 10.dp), - text = "Add account", + text = stringResource(R.string.add_account), style = UI.typo.b2.style( color = UI.colors.pureInverse, fontWeight = FontWeight.ExtraBold diff --git a/app/src/main/java/com/ivy/wallet/ui/theme/modal/MonthPickerModal.kt b/app/src/main/java/com/ivy/wallet/ui/theme/modal/MonthPickerModal.kt index e65eb9f496..edaec94eba 100644 --- a/app/src/main/java/com/ivy/wallet/ui/theme/modal/MonthPickerModal.kt +++ b/app/src/main/java/com/ivy/wallet/ui/theme/modal/MonthPickerModal.kt @@ -12,11 +12,13 @@ import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalView +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style +import com.ivy.wallet.R import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.theme.Gradient import com.ivy.wallet.ui.theme.Ivy @@ -59,7 +61,7 @@ fun BoxWithConstraintsScope.MonthPickerModal( Spacer(Modifier.height(32.dp)) ModalTitle( - text = "Choose month" + text = stringResource(R.string.choose_month) ) Spacer(Modifier.height(24.dp)) diff --git a/app/src/main/java/com/ivy/wallet/ui/theme/modal/NameModal.kt b/app/src/main/java/com/ivy/wallet/ui/theme/modal/NameModal.kt index abb4a71bf8..2f6cec0666 100644 --- a/app/src/main/java/com/ivy/wallet/ui/theme/modal/NameModal.kt +++ b/app/src/main/java/com/ivy/wallet/ui/theme/modal/NameModal.kt @@ -7,11 +7,13 @@ import androidx.compose.foundation.layout.padding import androidx.compose.material.Text import androidx.compose.runtime.* import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style +import com.ivy.wallet.R import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.theme.components.IvyTitleTextField import com.ivy.wallet.utils.selectEndTextFieldValue @@ -42,7 +44,7 @@ fun BoxWithConstraintsScope.NameModal( Text( modifier = Modifier.padding(start = 32.dp), - text = "Edit name", + text = stringResource(R.string.edit_name), style = UI.typo.b1.style( fontWeight = FontWeight.ExtraBold, color = UI.colors.pureInverse @@ -55,7 +57,7 @@ fun BoxWithConstraintsScope.NameModal( modifier = Modifier.padding(horizontal = 32.dp), dividerModifier = Modifier.padding(horizontal = 24.dp), value = modalName, - hint = "What's your name?" + hint = stringResource(R.string.what_is_your_name) ) { modalName = it } diff --git a/app/src/main/java/com/ivy/wallet/ui/theme/modal/RecurringRuleModal.kt b/app/src/main/java/com/ivy/wallet/ui/theme/modal/RecurringRuleModal.kt index 945320aaa7..70fca87e0e 100644 --- a/app/src/main/java/com/ivy/wallet/ui/theme/modal/RecurringRuleModal.kt +++ b/app/src/main/java/com/ivy/wallet/ui/theme/modal/RecurringRuleModal.kt @@ -12,6 +12,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalView import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview @@ -94,7 +95,7 @@ fun BoxWithConstraintsScope.RecurringRuleModal( hideKeyboard(rootView) } - ModalTitle(text = "Plan for") + ModalTitle(text = stringResource(R.string.plan_for)) Spacer(Modifier.height(16.dp)) @@ -157,7 +158,7 @@ private fun TimesSelector( TimesSelectorButton( selected = oneTime, - label = "One time" + label = stringResource(R.string.one_time) ) { onSetOneTime(true) } @@ -166,7 +167,7 @@ private fun TimesSelector( TimesSelectorButton( selected = !oneTime, - label = "Multiple times" + label = stringResource(R.string.multiple_times) ) { onSetOneTime(false) } @@ -233,7 +234,7 @@ private fun MultipleTimes( Text( modifier = Modifier .padding(start = 32.dp), - text = "Starts on", + text = stringResource(R.string.starts_on), style = UI.typo.b2.style( color = UI.colors.pureInverse, fontWeight = FontWeight.ExtraBold @@ -257,7 +258,7 @@ private fun MultipleTimes( Text( modifier = Modifier .padding(start = 32.dp), - text = "Repeats every", + text = stringResource(R.string.repeats_every_text), style = UI.typo.b2.style( fontWeight = FontWeight.ExtraBold, color = UI.colors.pureInverse diff --git a/app/src/main/java/com/ivy/wallet/ui/theme/modal/RequestFeatureModal.kt b/app/src/main/java/com/ivy/wallet/ui/theme/modal/RequestFeatureModal.kt index cc63639fb0..89dbb3e125 100644 --- a/app/src/main/java/com/ivy/wallet/ui/theme/modal/RequestFeatureModal.kt +++ b/app/src/main/java/com/ivy/wallet/ui/theme/modal/RequestFeatureModal.kt @@ -5,12 +5,14 @@ import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.runtime.* import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import com.ivy.wallet.R import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.theme.Gray import com.ivy.wallet.ui.theme.components.IvyDescriptionTextField @@ -39,7 +41,7 @@ fun BoxWithConstraintsScope.RequestFeatureModal( dismiss = dismiss, PrimaryAction = { ModalSet( - label = "Submit", + label = stringResource(R.string.submit), enabled = title.text.isNotBlank() ) { onSubmit( @@ -52,12 +54,12 @@ fun BoxWithConstraintsScope.RequestFeatureModal( ) { Spacer(Modifier.height(32.dp)) - ModalTitle(text = "Request a feature") + ModalTitle(text = stringResource(R.string.request_a_feature)) Spacer(Modifier.height(24.dp)) ModalNameInput( - hint = "What do you need?", + hint = stringResource(R.string.what_do_you_need), autoFocusKeyboard = true, textFieldValue = title, setTextFieldValue = { @@ -87,7 +89,7 @@ fun BoxWithConstraintsScope.RequestFeatureModal( ) } ), - hint = "Explain it in one sentence. (supports markdown)", + hint = stringResource(R.string.explain_it_in_one_sentence), hintColor = Gray, value = body, ) { diff --git a/app/src/main/java/com/ivy/wallet/ui/theme/modal/edit/AccountModal.kt b/app/src/main/java/com/ivy/wallet/ui/theme/modal/edit/AccountModal.kt index 0f4f9a9c85..a62f4d9656 100644 --- a/app/src/main/java/com/ivy/wallet/ui/theme/modal/edit/AccountModal.kt +++ b/app/src/main/java/com/ivy/wallet/ui/theme/modal/edit/AccountModal.kt @@ -12,6 +12,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.tooling.preview.Preview @@ -20,8 +21,8 @@ import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style import com.ivy.wallet.R import com.ivy.wallet.domain.data.IvyCurrency -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.logic.model.CreateAccountData +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.deprecated.logic.model.CreateAccountData import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.theme.Gray import com.ivy.wallet.ui.theme.Ivy @@ -112,13 +113,13 @@ fun BoxWithConstraintsScope.AccountModal( Spacer(Modifier.height(32.dp)) ModalTitle( - text = if (modal?.account != null) "Edit account" else "New account", + text = if (modal?.account != null) stringResource(R.string.edit_account) else stringResource(R.string.new_account), ) Spacer(Modifier.height(24.dp)) IconNameRow( - hint = "Account name", + hint = stringResource(R.string.account_name), defaultIcon = R.drawable.ic_custom_account_m, color = color, icon = icon, @@ -157,13 +158,13 @@ fun BoxWithConstraintsScope.AccountModal( modifier = Modifier .padding(start = 16.dp) .align(Alignment.Start), - text = "Include account", + text = stringResource(R.string.include_account), checked = includeInBalance ) { includeInBalance = it } }, - label = "ENTER ACCOUNT BALANCE", + label = stringResource(R.string.enter_account_balance).uppercase(), currency = currencyCode, amount = amount, amountPaddingTop = 40.dp, @@ -204,7 +205,7 @@ fun BoxWithConstraintsScope.AccountModal( val context = LocalContext.current CurrencyModal( - title = "Choose currency", + title = stringResource(R.string.choose_currency), initialCurrency = IvyCurrency.fromCode(currencyCode), visible = currencyModalVisible, dismiss = { currencyModalVisible = false } diff --git a/app/src/main/java/com/ivy/wallet/ui/theme/modal/edit/AmountModal.kt b/app/src/main/java/com/ivy/wallet/ui/theme/modal/edit/AmountModal.kt index f459e84fe3..984d6b31f9 100644 --- a/app/src/main/java/com/ivy/wallet/ui/theme/modal/edit/AmountModal.kt +++ b/app/src/main/java/com/ivy/wallet/ui/theme/modal/edit/AmountModal.kt @@ -14,6 +14,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalView import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview @@ -71,6 +72,7 @@ fun BoxWithConstraintsScope.AmountModal( onClick = { calculatorModalVisible = true }) + .testTag("btn_calculator") .padding(all = 4.dp), icon = R.drawable.ic_custom_calculator_m, tint = UI.colors.pureInverse @@ -79,7 +81,7 @@ fun BoxWithConstraintsScope.AmountModal( Spacer(Modifier.width(16.dp)) ModalPositiveButton( - text = "Enter", + text = stringResource(R.string.enter), iconStart = R.drawable.ic_check ) { try { @@ -147,15 +149,15 @@ fun AmountCurrency( Spacer(Modifier.weight(1f)) Text( - text = if (amount.isBlank()) "0" else amount, + text = amount.ifBlank { "0" }, style = UI.typo.nH1.style( fontWeight = FontWeight.Bold, color = UI.colors.pureInverse ) ) - + Spacer(Modifier.width(4.dp)) Text( - text = " $currency", + text = currency, style = UI.typo.nH2.style( fontWeight = FontWeight.Normal, color = UI.colors.pureInverse @@ -177,6 +179,7 @@ fun AmountInput( var firstInput by remember { mutableStateOf(true) } AmountKeyboard( + forCalculator = false, onNumberPressed = { if (firstInput) { setAmount(it) @@ -242,6 +245,7 @@ private fun formatNumber(number: String): String? { @Composable fun AmountKeyboard( + forCalculator: Boolean, ZeroRow: (@Composable RowScope.() -> Unit)? = null, FirstRowExtra: (@Composable RowScope.() -> Unit)? = null, SecondRowExtra: (@Composable RowScope.() -> Unit)? = null, @@ -270,21 +274,24 @@ fun AmountKeyboard( horizontalArrangement = Arrangement.Center ) { CircleNumberButton( - value = "1", + forCalculator = forCalculator, + value = "7", onNumberPressed = onNumberPressed ) Spacer(Modifier.width(16.dp)) CircleNumberButton( - value = "2", + forCalculator = forCalculator, + value = "8", onNumberPressed = onNumberPressed ) Spacer(Modifier.width(16.dp)) CircleNumberButton( - value = "3", + forCalculator = forCalculator, + value = "9", onNumberPressed = onNumberPressed ) @@ -303,6 +310,7 @@ fun AmountKeyboard( horizontalArrangement = Arrangement.Center ) { CircleNumberButton( + forCalculator = forCalculator, value = "4", onNumberPressed = onNumberPressed ) @@ -310,6 +318,7 @@ fun AmountKeyboard( Spacer(Modifier.width(16.dp)) CircleNumberButton( + forCalculator = forCalculator, value = "5", onNumberPressed = onNumberPressed ) @@ -317,6 +326,7 @@ fun AmountKeyboard( Spacer(Modifier.width(16.dp)) CircleNumberButton( + forCalculator = forCalculator, value = "6", onNumberPressed = onNumberPressed ) @@ -336,21 +346,24 @@ fun AmountKeyboard( horizontalArrangement = Arrangement.Center ) { CircleNumberButton( - value = "7", + forCalculator = forCalculator, + value = "1", onNumberPressed = onNumberPressed ) Spacer(Modifier.width(16.dp)) CircleNumberButton( - value = "8", + forCalculator = forCalculator, + value = "2", onNumberPressed = onNumberPressed ) Spacer(Modifier.width(16.dp)) CircleNumberButton( - value = "9", + forCalculator = forCalculator, + value = "3", onNumberPressed = onNumberPressed ) @@ -370,7 +383,8 @@ fun AmountKeyboard( ) { KeypadCircleButton( text = localDecimalSeparator(), - testTag = "key_decimal_separator" + testTag = if (forCalculator) + "calc_key_decimal_separator" else "key_decimal_separator" ) { onDecimalPoint() } @@ -378,6 +392,7 @@ fun AmountKeyboard( Spacer(Modifier.width(16.dp)) CircleNumberButton( + forCalculator = forCalculator, value = "0", onNumberPressed = onNumberPressed ) @@ -402,12 +417,14 @@ fun AmountKeyboard( @Composable fun CircleNumberButton( + forCalculator: Boolean, value: String, onNumberPressed: (String) -> Unit, ) { KeypadCircleButton( text = value, - testTag = "key_${value}", + testTag = if (forCalculator) + "calc_key_${value}" else "key_${value}", onClick = { onNumberPressed(value) } diff --git a/app/src/main/java/com/ivy/wallet/ui/theme/modal/edit/CalculatorModal.kt b/app/src/main/java/com/ivy/wallet/ui/theme/modal/edit/CalculatorModal.kt index 391a29ef48..32751c898f 100644 --- a/app/src/main/java/com/ivy/wallet/ui/theme/modal/edit/CalculatorModal.kt +++ b/app/src/main/java/com/ivy/wallet/ui/theme/modal/edit/CalculatorModal.kt @@ -4,12 +4,15 @@ import androidx.compose.foundation.layout.* import androidx.compose.material.Text import androidx.compose.runtime.* import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style +import com.ivy.wallet.R import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.theme.Gray import com.ivy.wallet.ui.theme.Red @@ -39,7 +42,9 @@ fun BoxWithConstraintsScope.CalculatorModal( visible = visible, dismiss = dismiss, PrimaryAction = { - ModalSet { + ModalSet( + modifier = Modifier.testTag("calc_set") + ) { val result = calculate(expression) if (result != null) { onCalculation(result) @@ -50,7 +55,7 @@ fun BoxWithConstraintsScope.CalculatorModal( ) { Spacer(Modifier.height(32.dp)) - ModalTitle(text = "Calculator") + ModalTitle(text = stringResource(R.string.calculator)) Spacer(Modifier.height(32.dp)) @@ -59,7 +64,7 @@ fun BoxWithConstraintsScope.CalculatorModal( modifier = Modifier .fillMaxWidth() .padding(horizontal = 24.dp), - text = if (isEmpty) "Calculation (+-/*=)" else expression, + text = if (isEmpty) stringResource(R.string.calculator_empty_expression) else expression, style = UI.typo.nH2.style( fontWeight = FontWeight.Bold, textAlign = TextAlign.Center, @@ -70,6 +75,7 @@ fun BoxWithConstraintsScope.CalculatorModal( Spacer(Modifier.height(32.dp)) AmountKeyboard( + forCalculator = true, ZeroRow = { KeypadCircleButton( text = "C", diff --git a/app/src/main/java/com/ivy/wallet/ui/theme/modal/edit/CategoryModal.kt b/app/src/main/java/com/ivy/wallet/ui/theme/modal/edit/CategoryModal.kt index 2a5c842f4c..991aa7506b 100644 --- a/app/src/main/java/com/ivy/wallet/ui/theme/modal/edit/CategoryModal.kt +++ b/app/src/main/java/com/ivy/wallet/ui/theme/modal/edit/CategoryModal.kt @@ -17,6 +17,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalView import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.text.input.KeyboardType @@ -24,8 +25,8 @@ import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.ivy.wallet.R -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.logic.model.CreateCategoryData +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.deprecated.logic.model.CreateCategoryData import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.theme.Ivy import com.ivy.wallet.ui.theme.components.ItemIconMDefaultIcon @@ -105,13 +106,16 @@ fun BoxWithConstraintsScope.CategoryModal( Spacer(Modifier.height(32.dp)) ModalTitle( - text = if (modal?.category != null) "Edit category" else "Create category" + text = if (modal?.category != null) stringResource(R.string.edit_category) + else stringResource( + R.string.create_category + ) ) Spacer(Modifier.height(24.dp)) IconNameRow( - hint = "Category name", + hint = stringResource(R.string.category_name), defaultIcon = R.drawable.ic_custom_category_m, color = color, icon = icon, diff --git a/app/src/main/java/com/ivy/wallet/ui/theme/modal/edit/ChooseCategoryModal.kt b/app/src/main/java/com/ivy/wallet/ui/theme/modal/edit/ChooseCategoryModal.kt index ea7e38d046..07d3d2ada2 100644 --- a/app/src/main/java/com/ivy/wallet/ui/theme/modal/edit/ChooseCategoryModal.kt +++ b/app/src/main/java/com/ivy/wallet/ui/theme/modal/edit/ChooseCategoryModal.kt @@ -14,13 +14,14 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalView import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style import com.ivy.wallet.R -import com.ivy.wallet.domain.data.entity.Category +import com.ivy.wallet.domain.data.core.Category import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.theme.* import com.ivy.wallet.ui.theme.components.ItemIconSDefaultIcon @@ -74,7 +75,7 @@ fun BoxWithConstraintsScope.ChooseCategoryModal( Spacer(Modifier.height(32.dp)) ModalTitle( - text = "Choose category" + text = stringResource(R.string.choose_category) ) Spacer(Modifier.height(24.dp)) @@ -240,7 +241,7 @@ fun AddNewButton( ) { IvyBorderButton( modifier = modifier, - text = "Add new", + text = stringResource(R.string.add_new), backgroundGradient = Gradient.solid(UI.colors.mediumInverse), iconStart = R.drawable.ic_plus, textStyle = UI.typo.b2.style( diff --git a/app/src/main/java/com/ivy/wallet/ui/theme/modal/edit/DescriptionModal.kt b/app/src/main/java/com/ivy/wallet/ui/theme/modal/edit/DescriptionModal.kt index 234a60a390..fd7f6cedbd 100644 --- a/app/src/main/java/com/ivy/wallet/ui/theme/modal/edit/DescriptionModal.kt +++ b/app/src/main/java/com/ivy/wallet/ui/theme/modal/edit/DescriptionModal.kt @@ -8,6 +8,7 @@ import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.ImeAction @@ -17,7 +18,10 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style +import com.ivy.design.utils.hideKeyboard +import com.ivy.wallet.R import com.ivy.wallet.ui.IvyWalletPreview +import com.ivy.wallet.ui.rootView import com.ivy.wallet.ui.theme.components.IvyDescriptionTextField import com.ivy.wallet.ui.theme.modal.IvyModal import com.ivy.wallet.ui.theme.modal.ModalDynamicPrimaryAction @@ -38,6 +42,7 @@ fun BoxWithConstraintsScope.DescriptionModal( var descTextFieldValue by remember(description) { mutableStateOf(selectEndTextFieldValue(description)) } + val view = rootView() IvyModal( id = id, @@ -49,9 +54,11 @@ fun BoxWithConstraintsScope.DescriptionModal( initialChanged = description != descTextFieldValue.text, onSave = { onDescriptionChanged(descTextFieldValue.text) + view.hideKeyboard() }, onDelete = { onDescriptionChanged(null) + view.hideKeyboard() }, dismiss = dismiss ) @@ -62,7 +69,7 @@ fun BoxWithConstraintsScope.DescriptionModal( Text( modifier = Modifier .padding(start = 32.dp), - text = "Description", + text = stringResource(R.string.description), style = UI.typo.b1.style( color = UI.colors.pureInverse, fontWeight = FontWeight.ExtraBold @@ -98,7 +105,7 @@ fun BoxWithConstraintsScope.DescriptionModal( } ), value = descTextFieldValue, - hint = "Enter any details here (supports Markdown)", + hint = stringResource(R.string.description_text_field_hint), ) { descTextFieldValue = it } diff --git a/app/src/main/java/com/ivy/wallet/ui/theme/modal/model/Month.kt b/app/src/main/java/com/ivy/wallet/ui/theme/modal/model/Month.kt index 2317160261..ec5400d688 100644 --- a/app/src/main/java/com/ivy/wallet/ui/theme/modal/model/Month.kt +++ b/app/src/main/java/com/ivy/wallet/ui/theme/modal/model/Month.kt @@ -1,5 +1,7 @@ package com.ivy.wallet.ui.theme.modal.model +import com.ivy.wallet.R +import com.ivy.wallet.stringRes import com.ivy.wallet.ui.IvyWalletCtx import com.ivy.wallet.ui.onboarding.model.TimePeriod import com.ivy.wallet.utils.dateNowUTC @@ -11,18 +13,18 @@ data class Month( ) { companion object { fun monthsList(): MutableList = mutableListOf( - Month(1, "January"), - Month(2, "February"), - Month(3, "March"), - Month(4, "April"), - Month(5, "May"), - Month(6, "June"), - Month(7, "July"), - Month(8, "August"), - Month(9, "September"), - Month(10, "October"), - Month(11, "November"), - Month(12, "December"), + Month(1, stringRes(R.string.january)), + Month(2, stringRes(R.string.february)), + Month(3, stringRes(R.string.march)), + Month(4, stringRes(R.string.april)), + Month(5, stringRes(R.string.may)), + Month(6, stringRes(R.string.june)), + Month(7, stringRes(R.string.july)), + Month(8, stringRes(R.string.august)), + Month(9, stringRes(R.string.september)), + Month(10, stringRes(R.string.october)), + Month(11, stringRes(R.string.november)), + Month(12, stringRes(R.string.december)), ) fun fromMonthValue(code: Int): Month = diff --git a/app/src/main/java/com/ivy/wallet/ui/theme/transaction/HistoryDateDivider.kt b/app/src/main/java/com/ivy/wallet/ui/theme/transaction/HistoryDateDivider.kt index 7db9b688dd..c0ad36b46d 100644 --- a/app/src/main/java/com/ivy/wallet/ui/theme/transaction/HistoryDateDivider.kt +++ b/app/src/main/java/com/ivy/wallet/ui/theme/transaction/HistoryDateDivider.kt @@ -5,6 +5,7 @@ import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp @@ -18,6 +19,7 @@ import com.ivy.wallet.utils.dateNowUTC import com.ivy.wallet.utils.format import com.ivy.wallet.utils.formatLocal import java.time.LocalDate +import com.ivy.wallet.R @Composable fun HistoryDateDivider( @@ -53,13 +55,13 @@ fun HistoryDateDivider( Text( text = when (date) { today -> { - "Today" + stringResource(R.string.today) } today.minusDays(1) -> { - "Yesterday" + stringResource(R.string.yesterday) } today.plusDays(1) -> { - "Tomorrow" + stringResource(R.string.tomorrow) } else -> { date.formatLocal("EEEE") diff --git a/app/src/main/java/com/ivy/wallet/ui/theme/transaction/TransactionCard.kt b/app/src/main/java/com/ivy/wallet/ui/theme/transaction/TransactionCard.kt index 326d067bad..63909beb63 100644 --- a/app/src/main/java/com/ivy/wallet/ui/theme/transaction/TransactionCard.kt +++ b/app/src/main/java/com/ivy/wallet/ui/theme/transaction/TransactionCard.kt @@ -15,6 +15,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview @@ -24,9 +25,9 @@ import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style import com.ivy.wallet.R import com.ivy.wallet.domain.data.TransactionType -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.data.entity.Transaction +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.data.core.Transaction import com.ivy.wallet.ui.ItemStatistic import com.ivy.wallet.ui.IvyWalletPreview import com.ivy.wallet.ui.theme.* @@ -84,7 +85,7 @@ fun LazyItemScope.TransactionCard( Text( modifier = Modifier.padding(horizontal = 24.dp), - text = "DUE ON ${transaction.dueDate.formatNicely()}".uppercase(), + text = stringResource(R.string.due_on, transaction.dueDate.formatNicely()).uppercase(), style = UI.typo.nC.style( color = if (transaction.dueDate.isAfter(timeNowUTC())) Orange else UI.colors.gray, @@ -139,13 +140,13 @@ fun LazyItemScope.TransactionCard( transactionType = transaction.type, dueDate = transaction.dueDate, currency = transactionCurrency, - amount = transaction.amount + amount = transaction.amount.toDouble() ) - if (transaction.type == TransactionType.TRANSFER && transaction.toAmount != null && toAccountCurrency != transactionCurrency) { + if (transaction.type == TransactionType.TRANSFER && toAccountCurrency != transactionCurrency) { Text( modifier = Modifier.padding(start = 68.dp), - text = transaction.toAmount.format(2) + " $toAccountCurrency", + text = "${transaction.toAmount.toDouble().format(2)} $toAccountCurrency", style = UI.typo.nB2.style( color = Gray, fontWeight = FontWeight.Normal @@ -162,7 +163,7 @@ fun LazyItemScope.TransactionCard( modifier = Modifier .fillMaxWidth() .padding(horizontal = 24.dp), - text = if (isExpense) "Pay" else "Get", + text = if (isExpense) stringResource(R.string.pay) else stringResource(R.string.get), wrapContentMode = false, backgroundGradient = if (isExpense) gradientExpenses() else GradientGreen, textStyle = UI.typo.b2.style( @@ -197,7 +198,7 @@ private fun TransactionHeaderRow( verticalAlignment = Alignment.CenterVertically ) { val category = - transaction.smartCategoryId() + transaction.categoryId ?.let { targetId -> categories.find { it.id == targetId } } if (category != null) { IvyButton( @@ -233,7 +234,7 @@ private fun TransactionHeaderRow( backgroundGradient = Gradient.solid(UI.colors.pure), hasGlow = false, iconTint = UI.colors.pureInverse, - text = account?.name ?: "deleted", + text = account?.name ?: stringResource(R.string.deleted), iconStart = getCustomIconIdS( iconName = account?.icon, defaultIcon = R.drawable.ic_custom_account_s @@ -282,7 +283,8 @@ private fun TransferHeader( Text( modifier = Modifier .padding(vertical = 8.dp), - text = account?.name ?: "null", + // used toString() in case of null + text = account?.name.toString(), style = UI.typo.c.style( fontWeight = FontWeight.ExtraBold, color = UI.colors.pureInverse @@ -306,7 +308,8 @@ private fun TransferHeader( Text( modifier = Modifier .padding(vertical = 8.dp), - text = toAccount?.name ?: "null", + // used toString() in case of null + text = toAccount?.name.toString(), style = UI.typo.c.style( fontWeight = FontWeight.ExtraBold, color = UI.colors.pureInverse @@ -326,6 +329,7 @@ fun TypeAmountCurrency( ) { Row( + modifier = Modifier.testTag("type_amount_currency"), verticalAlignment = Alignment.CenterVertically ) { Spacer(Modifier.width(24.dp)) @@ -424,7 +428,7 @@ private fun PreviewUpcomingExpense() { accountId = cash.id, title = "Lidl pazar", categoryId = food.id, - amount = 250.75, + amount = 250.75.toBigDecimal(), dueDate = timeNowUTC().plusDays(5), dateTime = null, type = TransactionType.EXPENSE, @@ -455,7 +459,7 @@ private fun PreviewOverdueExpense() { accountId = cash.id, title = "Rent", categoryId = food.id, - amount = 500.0, + amount = 500.0.toBigDecimal(), dueDate = timeNowUTC().minusDays(5), dateTime = null, type = TransactionType.EXPENSE @@ -490,7 +494,7 @@ private fun PreviewNormalExpense() { accountId = cash.id, title = "Близкия магазин", categoryId = food.id, - amount = 32.51, + amount = 32.51.toBigDecimal(), dateTime = timeNowUTC(), type = TransactionType.EXPENSE ), @@ -519,7 +523,7 @@ private fun PreviewIncome() { accountId = cash.id, title = "Qredo Salary May", categoryId = category.id, - amount = 8049.70, + amount = 8049.70.toBigDecimal(), dateTime = timeNowUTC(), type = TransactionType.INCOME ), @@ -549,7 +553,7 @@ private fun PreviewTransfer() { accountId = acc1.id, toAccountId = acc2.id, title = "Top-up revolut", - amount = 1000.0, + amount = 1000.0.toBigDecimal(), dateTime = timeNowUTC(), type = TransactionType.TRANSFER ), diff --git a/app/src/main/java/com/ivy/wallet/ui/theme/transaction/TransactionSectionDivider.kt b/app/src/main/java/com/ivy/wallet/ui/theme/transaction/TransactionSectionDivider.kt index 1786cbf52b..3d0c1fee57 100644 --- a/app/src/main/java/com/ivy/wallet/ui/theme/transaction/TransactionSectionDivider.kt +++ b/app/src/main/java/com/ivy/wallet/ui/theme/transaction/TransactionSectionDivider.kt @@ -10,6 +10,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.rotate import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -107,8 +108,9 @@ private fun SectionDividerIncomeExpenseRow( color = UI.colors.pureInverse ) ) + Spacer(modifier = Modifier.width(4.dp)) Text( - text = " expenses", + text = stringResource(R.string.expenses_lowercase), style = UI.typo.c.style( fontWeight = FontWeight.Normal, color = UI.colors.pureInverse @@ -133,8 +135,9 @@ private fun SectionDividerIncomeExpenseRow( color = UI.colors.green ) ) + Spacer(modifier = Modifier.width(4.dp)) Text( - text = " income", + text = stringResource(R.string.income_lowercase), style = UI.typo.c.style( fontWeight = FontWeight.Normal, color = UI.colors.pureInverse diff --git a/app/src/main/java/com/ivy/wallet/ui/theme/transaction/Transactions.kt b/app/src/main/java/com/ivy/wallet/ui/theme/transaction/Transactions.kt index 0db0f7f840..75976ee907 100644 --- a/app/src/main/java/com/ivy/wallet/ui/theme/transaction/Transactions.kt +++ b/app/src/main/java/com/ivy/wallet/ui/theme/transaction/Transactions.kt @@ -19,9 +19,9 @@ import com.ivy.design.navigation.Navigation import com.ivy.wallet.R import com.ivy.wallet.domain.data.TransactionHistoryDateDivider import com.ivy.wallet.domain.data.TransactionHistoryItem -import com.ivy.wallet.domain.data.entity.Account -import com.ivy.wallet.domain.data.entity.Category -import com.ivy.wallet.domain.data.entity.Transaction +import com.ivy.wallet.domain.data.core.Account +import com.ivy.wallet.domain.data.core.Category +import com.ivy.wallet.domain.data.core.Transaction import com.ivy.wallet.ui.EditTransaction import com.ivy.wallet.ui.IvyWalletCtx import com.ivy.wallet.ui.theme.Gray diff --git a/app/src/main/java/com/ivy/wallet/ui/theme/wallet/AmountCurrency.kt b/app/src/main/java/com/ivy/wallet/ui/theme/wallet/AmountCurrency.kt index 64454abbe6..9a34133580 100644 --- a/app/src/main/java/com/ivy/wallet/ui/theme/wallet/AmountCurrency.kt +++ b/app/src/main/java/com/ivy/wallet/ui/theme/wallet/AmountCurrency.kt @@ -1,11 +1,16 @@ package com.ivy.wallet.ui.theme.wallet import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.width import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.testTag import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style import com.ivy.wallet.utils.format @@ -30,9 +35,9 @@ fun AmountCurrencyB2Row( color = textColor ) ) - + Spacer(modifier = Modifier.width(4.dp)) Text( - text = " $currency", + text = currency, style = UI.typo.nB2.style( fontWeight = FontWeight.Normal, color = textColor @@ -72,15 +77,16 @@ fun AmountCurrencyB1( val shortAmount = shortenBigNumbers && shouldShortAmount(amount) Text( + modifier = Modifier.testTag("amount_currency_b1"), text = if (shortAmount) shortenAmount(amount) else amount.format(currency), style = UI.typo.nB1.style( fontWeight = amountFontWeight, color = textColor ) ) - + Spacer(modifier = Modifier.width(4.dp)) Text( - text = " $currency", + text = currency, style = UI.typo.nB1.style( fontWeight = FontWeight.Normal, color = textColor @@ -101,9 +107,9 @@ fun AmountCurrencyH1( color = textColor ) ) - + Spacer(modifier = Modifier.width(4.dp)) Text( - text = " $currency", + text = currency, style = UI.typo.nH2.style( fontWeight = FontWeight.Normal, color = textColor @@ -127,9 +133,9 @@ fun AmountCurrencyH2Row( color = textColor ) ) - + Spacer(modifier = Modifier.width(4.dp)) Text( - text = " $currency", + text = currency, style = UI.typo.b1.style( fontWeight = FontWeight.Normal, color = textColor @@ -152,9 +158,9 @@ fun AmountCurrencyCaption( color = textColor ) ) - + Spacer(modifier = Modifier.width(4.dp)) Text( - text = " $currency", + text = currency, style = UI.typo.nC.style( fontWeight = FontWeight.Normal, color = textColor diff --git a/app/src/main/java/com/ivy/wallet/utils/DateExt.kt b/app/src/main/java/com/ivy/wallet/utils/DateExt.kt index 22519154f6..b58998ebc5 100644 --- a/app/src/main/java/com/ivy/wallet/utils/DateExt.kt +++ b/app/src/main/java/com/ivy/wallet/utils/DateExt.kt @@ -3,6 +3,8 @@ package com.ivy.wallet.utils import androidx.compose.runtime.Composable import androidx.compose.ui.platform.LocalContext +import com.ivy.wallet.R +import com.ivy.wallet.stringRes import java.time.* import java.time.format.DateTimeFormatter import java.util.* @@ -50,13 +52,13 @@ fun LocalDateTime.formatNicely( return when (this.toLocalDate()) { today -> { - "Today, ${this.formatLocal(patternNoWeekDay, zone)}" + stringRes(R.string.today_date, this.formatLocal(patternNoWeekDay, zone)) } today.minusDays(1) -> { - "Yesterday, ${this.formatLocal(patternNoWeekDay, zone)}" + stringRes(R.string.yesterday_date, this.formatLocal(patternNoWeekDay, zone)) } today.plusDays(1) -> { - "Tomorrow, ${this.formatLocal(patternNoWeekDay, zone)}" + stringRes(R.string.tomorrow_date, this.formatLocal(patternNoWeekDay, zone)) } else -> { if (isThisYear) { @@ -75,31 +77,31 @@ fun LocalDateTime.formatNicelyWithTime( val today = dateNowUTC() val isThisYear = today.year == this.year - val patternNoWeekDay = "dd MMM 'at' HH:mm" + val patternNoWeekDay = "dd MMM HH:mm" if (noWeekDay) { return if (isThisYear) { this.formatLocal(patternNoWeekDay) } else { - this.formatLocal("dd MMM, yyyy 'at' HH:mm") + this.formatLocal("dd MMM, yyyy HH:mm") } } return when (this.toLocalDate()) { today -> { - "Today, ${this.formatLocal(patternNoWeekDay, zone)}" + stringRes(R.string.today_date, this.formatLocal(patternNoWeekDay, zone)) } today.minusDays(1) -> { - "Yesterday, ${this.formatLocal(patternNoWeekDay, zone)}" + stringRes(R.string.yesterday_date, this.formatLocal(patternNoWeekDay, zone)) } today.plusDays(1) -> { - "Tomorrow, ${this.formatLocal(patternNoWeekDay, zone)}" + stringRes(R.string.tomorrow, this.formatLocal(patternNoWeekDay, zone)) } else -> { if (isThisYear) { - this.formatLocal("EEE, dd MMM 'at' HH:mm", zone) + this.formatLocal("EEE, dd MMM HH:mm", zone) } else { - this.formatLocal("dd MMM, yyyy 'at' HH:mm", zone) + this.formatLocal("dd MMM, yyyy HH:mm", zone) } } } @@ -140,13 +142,13 @@ fun LocalDate.closeDay(): String? { val today = dateNowUTC() return when (this) { today -> { - "Today" + stringRes(R.string.today) } today.minusDays(1) -> { - "Yesterday" + stringRes(R.string.yesterday) } today.plusDays(1) -> { - "Tomorrow" + stringRes(R.string.tomorrow) } else -> { null @@ -235,7 +237,7 @@ fun LocalDateTime.timeLeft( secondsLabel: String = "s" ): String { val timeLeftMs = this.millis() - from.millis() - if (timeLeftMs <= 0) return "Expired" + if (timeLeftMs <= 0) return stringRes(R.string.expired) val days = TimeUnit.MILLISECONDS.toDays(timeLeftMs) var timeLeftAfterCalculations = timeLeftMs - TimeUnit.DAYS.toMillis(days) diff --git a/app/src/main/java/com/ivy/wallet/utils/FileUtil.kt b/app/src/main/java/com/ivy/wallet/utils/FileUtil.kt index 1cfe4ce093..0243acd8d5 100644 --- a/app/src/main/java/com/ivy/wallet/utils/FileUtil.kt +++ b/app/src/main/java/com/ivy/wallet/utils/FileUtil.kt @@ -1,8 +1,10 @@ package com.ivy.wallet.utils +import android.content.ContentResolver import android.content.Context import android.net.Uri import android.os.Environment +import android.provider.OpenableColumns import java.io.* import java.nio.charset.Charset @@ -81,4 +83,16 @@ private fun readFileContent( } return sb.toString() } -} \ No newline at end of file +} + +fun Context.getFileName(uri: Uri): String? = when (uri.scheme) { + ContentResolver.SCHEME_CONTENT -> getContentFileName(uri) + else -> uri.path?.let(::File)?.name +} + +private fun Context.getContentFileName(uri: Uri): String? = runCatching { + contentResolver.query(uri, null, null, null, null)?.use { cursor -> + cursor.moveToFirst() + return@use cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME).let(cursor::getString) + } +}.getOrNull() \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/utils/UiText.kt b/app/src/main/java/com/ivy/wallet/utils/UiText.kt new file mode 100644 index 0000000000..b5b419ff39 --- /dev/null +++ b/app/src/main/java/com/ivy/wallet/utils/UiText.kt @@ -0,0 +1,29 @@ +package com.ivy.wallet.utils + +import android.content.Context +import androidx.annotation.StringRes +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource + +sealed class UiText { + data class DynamicString(val value: String) : UiText() + class StringResource( + @StringRes val resId: Int, + vararg val args: Any + ) : UiText() + + @Composable + fun asString(): String { + return when (this) { + is DynamicString -> value + is StringResource -> stringResource(resId, *args) + } + } + + fun asString(context:Context): String { + return when (this) { + is DynamicString -> value + is StringResource -> context.getString(resId, *args) + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_vue_brands_android.xml b/app/src/main/res/drawable/ic_vue_brands_android.xml new file mode 100644 index 0000000000..7d68063994 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_brands_android.xml @@ -0,0 +1,55 @@ + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_brands_apple.xml b/app/src/main/res/drawable/ic_vue_brands_apple.xml new file mode 100644 index 0000000000..b0a76a7738 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_brands_apple.xml @@ -0,0 +1,14 @@ + + + diff --git a/app/src/main/res/drawable/ic_vue_brands_be.xml b/app/src/main/res/drawable/ic_vue_brands_be.xml new file mode 100644 index 0000000000..1d01a9d89c --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_brands_be.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_brands_blogger.xml b/app/src/main/res/drawable/ic_vue_brands_blogger.xml new file mode 100644 index 0000000000..d7993c2cc7 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_brands_blogger.xml @@ -0,0 +1,32 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_brands_bootsrap.xml b/app/src/main/res/drawable/ic_vue_brands_bootsrap.xml new file mode 100644 index 0000000000..5182198e96 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_brands_bootsrap.xml @@ -0,0 +1,19 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_brands_dribbble.xml b/app/src/main/res/drawable/ic_vue_brands_dribbble.xml new file mode 100644 index 0000000000..ab779fbd06 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_brands_dribbble.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_brands_drive.xml b/app/src/main/res/drawable/ic_vue_brands_drive.xml new file mode 100644 index 0000000000..aa63521393 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_brands_drive.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_brands_dropbox.xml b/app/src/main/res/drawable/ic_vue_brands_dropbox.xml new file mode 100644 index 0000000000..0af41b81ce --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_brands_dropbox.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_brands_facebook.xml b/app/src/main/res/drawable/ic_vue_brands_facebook.xml new file mode 100644 index 0000000000..4073918f8d --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_brands_facebook.xml @@ -0,0 +1,19 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_brands_figma.xml b/app/src/main/res/drawable/ic_vue_brands_figma.xml new file mode 100644 index 0000000000..2bb3ff50d6 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_brands_figma.xml @@ -0,0 +1,31 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_brands_framer.xml b/app/src/main/res/drawable/ic_vue_brands_framer.xml new file mode 100644 index 0000000000..c0f1e5c61a --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_brands_framer.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_brands_google.xml b/app/src/main/res/drawable/ic_vue_brands_google.xml new file mode 100644 index 0000000000..717105f05a --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_brands_google.xml @@ -0,0 +1,13 @@ + + + diff --git a/app/src/main/res/drawable/ic_vue_brands_google_play.xml b/app/src/main/res/drawable/ic_vue_brands_google_play.xml new file mode 100644 index 0000000000..f9f48513d6 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_brands_google_play.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_brands_html3.xml b/app/src/main/res/drawable/ic_vue_brands_html3.xml new file mode 100644 index 0000000000..5919044bff --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_brands_html3.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_brands_html5.xml b/app/src/main/res/drawable/ic_vue_brands_html5.xml new file mode 100644 index 0000000000..56203fe01f --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_brands_html5.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_brands_illustrator.xml b/app/src/main/res/drawable/ic_vue_brands_illustrator.xml new file mode 100644 index 0000000000..54609ce057 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_brands_illustrator.xml @@ -0,0 +1,39 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_brands_js.xml b/app/src/main/res/drawable/ic_vue_brands_js.xml new file mode 100644 index 0000000000..e38f09fd5d --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_brands_js.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_brands_messenger.xml b/app/src/main/res/drawable/ic_vue_brands_messenger.xml new file mode 100644 index 0000000000..8124ced870 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_brands_messenger.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_brands_ok.xml b/app/src/main/res/drawable/ic_vue_brands_ok.xml new file mode 100644 index 0000000000..d5f96d9d23 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_brands_ok.xml @@ -0,0 +1,30 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_brands_paypal.xml b/app/src/main/res/drawable/ic_vue_brands_paypal.xml new file mode 100644 index 0000000000..a229e32fe7 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_brands_paypal.xml @@ -0,0 +1,16 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_brands_photoshop.xml b/app/src/main/res/drawable/ic_vue_brands_photoshop.xml new file mode 100644 index 0000000000..7b8600d8e3 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_brands_photoshop.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_brands_python.xml b/app/src/main/res/drawable/ic_vue_brands_python.xml new file mode 100644 index 0000000000..6de0664852 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_brands_python.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_brands_slack.xml b/app/src/main/res/drawable/ic_vue_brands_slack.xml new file mode 100644 index 0000000000..9b294050ea --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_brands_slack.xml @@ -0,0 +1,62 @@ + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_brands_snapchat.xml b/app/src/main/res/drawable/ic_vue_brands_snapchat.xml new file mode 100644 index 0000000000..d9571bc8b1 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_brands_snapchat.xml @@ -0,0 +1,19 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_brands_spotify.xml b/app/src/main/res/drawable/ic_vue_brands_spotify.xml new file mode 100644 index 0000000000..63a7f882ad --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_brands_spotify.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_brands_trello.xml b/app/src/main/res/drawable/ic_vue_brands_trello.xml new file mode 100644 index 0000000000..70978b3d89 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_brands_trello.xml @@ -0,0 +1,23 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_brands_triangle.xml b/app/src/main/res/drawable/ic_vue_brands_triangle.xml new file mode 100644 index 0000000000..f4d888c36c --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_brands_triangle.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_brands_twitch.xml b/app/src/main/res/drawable/ic_vue_brands_twitch.xml new file mode 100644 index 0000000000..b4dc761559 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_brands_twitch.xml @@ -0,0 +1,28 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_brands_ui8.xml b/app/src/main/res/drawable/ic_vue_brands_ui8.xml new file mode 100644 index 0000000000..d568f34934 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_brands_ui8.xml @@ -0,0 +1,32 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_brands_vuesax.xml b/app/src/main/res/drawable/ic_vue_brands_vuesax.xml new file mode 100644 index 0000000000..9140c66d45 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_brands_vuesax.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_brands_whatsapp.xml b/app/src/main/res/drawable/ic_vue_brands_whatsapp.xml new file mode 100644 index 0000000000..6b8b8eab76 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_brands_whatsapp.xml @@ -0,0 +1,18 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_brands_windows.xml b/app/src/main/res/drawable/ic_vue_brands_windows.xml new file mode 100644 index 0000000000..ae214fbcbd --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_brands_windows.xml @@ -0,0 +1,38 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_brands_xd.xml b/app/src/main/res/drawable/ic_vue_brands_xd.xml new file mode 100644 index 0000000000..73b40e2c12 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_brands_xd.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_brands_xiaomi.xml b/app/src/main/res/drawable/ic_vue_brands_xiaomi.xml new file mode 100644 index 0000000000..f4c9b9c4bb --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_brands_xiaomi.xml @@ -0,0 +1,32 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_brands_youtube.xml b/app/src/main/res/drawable/ic_vue_brands_youtube.xml new file mode 100644 index 0000000000..cdad2499c8 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_brands_youtube.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_brands_zoom.xml b/app/src/main/res/drawable/ic_vue_brands_zoom.xml new file mode 100644 index 0000000000..7e8ba0cb11 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_brands_zoom.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_building_bank.xml b/app/src/main/res/drawable/ic_vue_building_bank.xml new file mode 100644 index 0000000000..7ba69ac339 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_building_bank.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_building_building.xml b/app/src/main/res/drawable/ic_vue_building_building.xml new file mode 100644 index 0000000000..01b7d668aa --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_building_building.xml @@ -0,0 +1,48 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_building_building1.xml b/app/src/main/res/drawable/ic_vue_building_building1.xml new file mode 100644 index 0000000000..351a917597 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_building_building1.xml @@ -0,0 +1,55 @@ + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_building_buildings.xml b/app/src/main/res/drawable/ic_vue_building_buildings.xml new file mode 100644 index 0000000000..94c663b054 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_building_buildings.xml @@ -0,0 +1,55 @@ + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_building_courthouse.xml b/app/src/main/res/drawable/ic_vue_building_courthouse.xml new file mode 100644 index 0000000000..4692723a15 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_building_courthouse.xml @@ -0,0 +1,59 @@ + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_building_hospital.xml b/app/src/main/res/drawable/ic_vue_building_hospital.xml new file mode 100644 index 0000000000..4d9391c422 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_building_hospital.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_building_house.xml b/app/src/main/res/drawable/ic_vue_building_house.xml new file mode 100644 index 0000000000..ea7df02c73 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_building_house.xml @@ -0,0 +1,44 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_chart_chart.xml b/app/src/main/res/drawable/ic_vue_chart_chart.xml new file mode 100644 index 0000000000..1fce16655f --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_chart_chart.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_chart_diagram.xml b/app/src/main/res/drawable/ic_vue_chart_diagram.xml new file mode 100644 index 0000000000..9bf204bc7f --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_chart_diagram.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_chart_graph.xml b/app/src/main/res/drawable/ic_vue_chart_graph.xml new file mode 100644 index 0000000000..45d767b956 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_chart_graph.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_chart_status_up.xml b/app/src/main/res/drawable/ic_vue_chart_status_up.xml new file mode 100644 index 0000000000..b846e08ed9 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_chart_status_up.xml @@ -0,0 +1,44 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_chart_trend_up.xml b/app/src/main/res/drawable/ic_vue_chart_trend_up.xml new file mode 100644 index 0000000000..964af3d3e6 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_chart_trend_up.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_aave.xml b/app/src/main/res/drawable/ic_vue_crypto_aave.xml new file mode 100644 index 0000000000..fde3583f42 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_aave.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_ankr.xml b/app/src/main/res/drawable/ic_vue_crypto_ankr.xml new file mode 100644 index 0000000000..c025dbbc44 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_ankr.xml @@ -0,0 +1,32 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_augur.xml b/app/src/main/res/drawable/ic_vue_crypto_augur.xml new file mode 100644 index 0000000000..ca0aadff13 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_augur.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_autonio.xml b/app/src/main/res/drawable/ic_vue_crypto_autonio.xml new file mode 100644 index 0000000000..3ba0e85b44 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_autonio.xml @@ -0,0 +1,32 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_avalanche.xml b/app/src/main/res/drawable/ic_vue_crypto_avalanche.xml new file mode 100644 index 0000000000..d35c5c5ccd --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_avalanche.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_binance_coin.xml b/app/src/main/res/drawable/ic_vue_crypto_binance_coin.xml new file mode 100644 index 0000000000..a01003e583 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_binance_coin.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_binance_usd.xml b/app/src/main/res/drawable/ic_vue_crypto_binance_usd.xml new file mode 100644 index 0000000000..3e970d96fa --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_binance_usd.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_bitcoin.xml b/app/src/main/res/drawable/ic_vue_crypto_bitcoin.xml new file mode 100644 index 0000000000..32675b5b57 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_bitcoin.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_cardano.xml b/app/src/main/res/drawable/ic_vue_crypto_cardano.xml new file mode 100644 index 0000000000..01c8f3aa0f --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_cardano.xml @@ -0,0 +1,180 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_celo.xml b/app/src/main/res/drawable/ic_vue_crypto_celo.xml new file mode 100644 index 0000000000..54275ee334 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_celo.xml @@ -0,0 +1,16 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_celsius_.xml b/app/src/main/res/drawable/ic_vue_crypto_celsius_.xml new file mode 100644 index 0000000000..a9af21fdd0 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_celsius_.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_chainlink.xml b/app/src/main/res/drawable/ic_vue_crypto_chainlink.xml new file mode 100644 index 0000000000..b6079cd26f --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_chainlink.xml @@ -0,0 +1,13 @@ + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_civic.xml b/app/src/main/res/drawable/ic_vue_crypto_civic.xml new file mode 100644 index 0000000000..079a24f473 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_civic.xml @@ -0,0 +1,18 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_dai.xml b/app/src/main/res/drawable/ic_vue_crypto_dai.xml new file mode 100644 index 0000000000..01d550345b --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_dai.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_dash.xml b/app/src/main/res/drawable/ic_vue_crypto_dash.xml new file mode 100644 index 0000000000..f6ee8da706 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_dash.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_decred.xml b/app/src/main/res/drawable/ic_vue_crypto_decred.xml new file mode 100644 index 0000000000..6370483aa6 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_decred.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_dent.xml b/app/src/main/res/drawable/ic_vue_crypto_dent.xml new file mode 100644 index 0000000000..e785144c84 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_dent.xml @@ -0,0 +1,26 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_educare.xml b/app/src/main/res/drawable/ic_vue_crypto_educare.xml new file mode 100644 index 0000000000..a6c2329c8a --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_educare.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_emercoin.xml b/app/src/main/res/drawable/ic_vue_crypto_emercoin.xml new file mode 100644 index 0000000000..611aa06281 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_emercoin.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_enjin_coin.xml b/app/src/main/res/drawable/ic_vue_crypto_enjin_coin.xml new file mode 100644 index 0000000000..481d124df7 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_enjin_coin.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_eos.xml b/app/src/main/res/drawable/ic_vue_crypto_eos.xml new file mode 100644 index 0000000000..f71befb792 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_eos.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_ethereum.xml b/app/src/main/res/drawable/ic_vue_crypto_ethereum.xml new file mode 100644 index 0000000000..3e476d07ab --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_ethereum.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_ethereum_classic.xml b/app/src/main/res/drawable/ic_vue_crypto_ethereum_classic.xml new file mode 100644 index 0000000000..642b9c17c4 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_ethereum_classic.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_ftx_token.xml b/app/src/main/res/drawable/ic_vue_crypto_ftx_token.xml new file mode 100644 index 0000000000..4b0c0b7f5d --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_ftx_token.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_graph.xml b/app/src/main/res/drawable/ic_vue_crypto_graph.xml new file mode 100644 index 0000000000..61fcbd0ce1 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_graph.xml @@ -0,0 +1,32 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_harmony.xml b/app/src/main/res/drawable/ic_vue_crypto_harmony.xml new file mode 100644 index 0000000000..dd6070c6a1 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_harmony.xml @@ -0,0 +1,31 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_hedera_hashgraph.xml b/app/src/main/res/drawable/ic_vue_crypto_hedera_hashgraph.xml new file mode 100644 index 0000000000..9b043279de --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_hedera_hashgraph.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_hex.xml b/app/src/main/res/drawable/ic_vue_crypto_hex.xml new file mode 100644 index 0000000000..5c4b5b6182 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_hex.xml @@ -0,0 +1,21 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_huobi_token.xml b/app/src/main/res/drawable/ic_vue_crypto_huobi_token.xml new file mode 100644 index 0000000000..552c4cb6fe --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_huobi_token.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_icon.xml b/app/src/main/res/drawable/ic_vue_crypto_icon.xml new file mode 100644 index 0000000000..c9d12ece0b --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_icon.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_iost.xml b/app/src/main/res/drawable/ic_vue_crypto_iost.xml new file mode 100644 index 0000000000..a3706fd10f --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_iost.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_kyber_network.xml b/app/src/main/res/drawable/ic_vue_crypto_kyber_network.xml new file mode 100644 index 0000000000..b2d9417258 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_kyber_network.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_litecoin.xml b/app/src/main/res/drawable/ic_vue_crypto_litecoin.xml new file mode 100644 index 0000000000..cb660d2fd6 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_litecoin.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_maker.xml b/app/src/main/res/drawable/ic_vue_crypto_maker.xml new file mode 100644 index 0000000000..c0d12873a3 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_maker.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_monero.xml b/app/src/main/res/drawable/ic_vue_crypto_monero.xml new file mode 100644 index 0000000000..37567ff77a --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_monero.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_nebulas.xml b/app/src/main/res/drawable/ic_vue_crypto_nebulas.xml new file mode 100644 index 0000000000..9b459cfbb4 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_nebulas.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_nem.xml b/app/src/main/res/drawable/ic_vue_crypto_nem.xml new file mode 100644 index 0000000000..81e160055c --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_nem.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_nexo.xml b/app/src/main/res/drawable/ic_vue_crypto_nexo.xml new file mode 100644 index 0000000000..496baec2b7 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_nexo.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_ocean_protocol.xml b/app/src/main/res/drawable/ic_vue_crypto_ocean_protocol.xml new file mode 100644 index 0000000000..7765996ac4 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_ocean_protocol.xml @@ -0,0 +1,174 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_okb.xml b/app/src/main/res/drawable/ic_vue_crypto_okb.xml new file mode 100644 index 0000000000..f609ca2eb4 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_okb.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_ontology.xml b/app/src/main/res/drawable/ic_vue_crypto_ontology.xml new file mode 100644 index 0000000000..d0c984b5a2 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_ontology.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_polkadot.xml b/app/src/main/res/drawable/ic_vue_crypto_polkadot.xml new file mode 100644 index 0000000000..90ab0e9c0d --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_polkadot.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_polygon.xml b/app/src/main/res/drawable/ic_vue_crypto_polygon.xml new file mode 100644 index 0000000000..cdc0d200f0 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_polygon.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_polyswarm.xml b/app/src/main/res/drawable/ic_vue_crypto_polyswarm.xml new file mode 100644 index 0000000000..14c722a26b --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_polyswarm.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_quant.xml b/app/src/main/res/drawable/ic_vue_crypto_quant.xml new file mode 100644 index 0000000000..ce085a06da --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_quant.xml @@ -0,0 +1,60 @@ + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_siacoin.xml b/app/src/main/res/drawable/ic_vue_crypto_siacoin.xml new file mode 100644 index 0000000000..9dbf953951 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_siacoin.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_solana.xml b/app/src/main/res/drawable/ic_vue_crypto_solana.xml new file mode 100644 index 0000000000..56a835d5b4 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_solana.xml @@ -0,0 +1,21 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_stacks.xml b/app/src/main/res/drawable/ic_vue_crypto_stacks.xml new file mode 100644 index 0000000000..dd9d826086 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_stacks.xml @@ -0,0 +1,53 @@ + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_stellar.xml b/app/src/main/res/drawable/ic_vue_crypto_stellar.xml new file mode 100644 index 0000000000..27a67f4879 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_stellar.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_tenx.xml b/app/src/main/res/drawable/ic_vue_crypto_tenx.xml new file mode 100644 index 0000000000..6e6723900b --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_tenx.xml @@ -0,0 +1,18 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_tether.xml b/app/src/main/res/drawable/ic_vue_crypto_tether.xml new file mode 100644 index 0000000000..17a53e0bfe --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_tether.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_theta.xml b/app/src/main/res/drawable/ic_vue_crypto_theta.xml new file mode 100644 index 0000000000..f0ba0235f7 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_theta.xml @@ -0,0 +1,48 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_thorchain.xml b/app/src/main/res/drawable/ic_vue_crypto_thorchain.xml new file mode 100644 index 0000000000..bfcb3f6a85 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_thorchain.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_trontron.xml b/app/src/main/res/drawable/ic_vue_crypto_trontron.xml new file mode 100644 index 0000000000..2d11909b32 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_trontron.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_usd_coin.xml b/app/src/main/res/drawable/ic_vue_crypto_usd_coin.xml new file mode 100644 index 0000000000..dd6cf100aa --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_usd_coin.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_velas.xml b/app/src/main/res/drawable/ic_vue_crypto_velas.xml new file mode 100644 index 0000000000..5d984cd817 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_velas.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_vibe.xml b/app/src/main/res/drawable/ic_vue_crypto_vibe.xml new file mode 100644 index 0000000000..c708dc3817 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_vibe.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_wanchain.xml b/app/src/main/res/drawable/ic_vue_crypto_wanchain.xml new file mode 100644 index 0000000000..f60a8bd239 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_wanchain.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_wing.xml b/app/src/main/res/drawable/ic_vue_crypto_wing.xml new file mode 100644 index 0000000000..8baf65bbd1 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_wing.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_xrp.xml b/app/src/main/res/drawable/ic_vue_crypto_xrp.xml new file mode 100644 index 0000000000..a4ba47f608 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_xrp.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_crypto_zel.xml b/app/src/main/res/drawable/ic_vue_crypto_zel.xml new file mode 100644 index 0000000000..4a06b841ff --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_crypto_zel.xml @@ -0,0 +1,30 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_delivery_box.xml b/app/src/main/res/drawable/ic_vue_delivery_box.xml new file mode 100644 index 0000000000..8fa8e1b037 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_delivery_box.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_delivery_box1.xml b/app/src/main/res/drawable/ic_vue_delivery_box1.xml new file mode 100644 index 0000000000..3d2188c786 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_delivery_box1.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_delivery_package.xml b/app/src/main/res/drawable/ic_vue_delivery_package.xml new file mode 100644 index 0000000000..f35242109d --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_delivery_package.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_delivery_receive.xml b/app/src/main/res/drawable/ic_vue_delivery_receive.xml new file mode 100644 index 0000000000..b595df84bc --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_delivery_receive.xml @@ -0,0 +1,62 @@ + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_delivery_truck.xml b/app/src/main/res/drawable/ic_vue_delivery_truck.xml new file mode 100644 index 0000000000..0e01266098 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_delivery_truck.xml @@ -0,0 +1,62 @@ + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_design_bezier.xml b/app/src/main/res/drawable/ic_vue_design_bezier.xml new file mode 100644 index 0000000000..9373f932ac --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_design_bezier.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_design_brush.xml b/app/src/main/res/drawable/ic_vue_design_brush.xml new file mode 100644 index 0000000000..af50101c12 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_design_brush.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_design_color_swatch.xml b/app/src/main/res/drawable/ic_vue_design_color_swatch.xml new file mode 100644 index 0000000000..0afbbbe006 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_design_color_swatch.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_design_magicpen.xml b/app/src/main/res/drawable/ic_vue_design_magicpen.xml new file mode 100644 index 0000000000..d8d9a668b4 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_design_magicpen.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_design_roller.xml b/app/src/main/res/drawable/ic_vue_design_roller.xml new file mode 100644 index 0000000000..129772ffe7 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_design_roller.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_design_scissors.xml b/app/src/main/res/drawable/ic_vue_design_scissors.xml new file mode 100644 index 0000000000..8b4ab618e1 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_design_scissors.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_design_tool_pen.xml b/app/src/main/res/drawable/ic_vue_design_tool_pen.xml new file mode 100644 index 0000000000..4cd199ddaa --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_design_tool_pen.xml @@ -0,0 +1,62 @@ + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_dev_arrow.xml b/app/src/main/res/drawable/ic_vue_dev_arrow.xml new file mode 100644 index 0000000000..31e8ba0848 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_dev_arrow.xml @@ -0,0 +1,48 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_dev_code.xml b/app/src/main/res/drawable/ic_vue_dev_code.xml new file mode 100644 index 0000000000..d79e928f6d --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_dev_code.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_dev_data.xml b/app/src/main/res/drawable/ic_vue_dev_data.xml new file mode 100644 index 0000000000..9bdf96104c --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_dev_data.xml @@ -0,0 +1,48 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_dev_hashtag.xml b/app/src/main/res/drawable/ic_vue_dev_hashtag.xml new file mode 100644 index 0000000000..48fed54866 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_dev_hashtag.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_dev_hierarchy.xml b/app/src/main/res/drawable/ic_vue_dev_hierarchy.xml new file mode 100644 index 0000000000..2029857cbe --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_dev_hierarchy.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_dev_relation.xml b/app/src/main/res/drawable/ic_vue_dev_relation.xml new file mode 100644 index 0000000000..4bd9f19d07 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_dev_relation.xml @@ -0,0 +1,58 @@ + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_edu_award.xml b/app/src/main/res/drawable/ic_vue_edu_award.xml new file mode 100644 index 0000000000..2545dc4c12 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_edu_award.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_edu_book.xml b/app/src/main/res/drawable/ic_vue_edu_book.xml new file mode 100644 index 0000000000..ee01810c15 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_edu_book.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_edu_bookmark.xml b/app/src/main/res/drawable/ic_vue_edu_bookmark.xml new file mode 100644 index 0000000000..8dc917bf7d --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_edu_bookmark.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_edu_briefcase.xml b/app/src/main/res/drawable/ic_vue_edu_briefcase.xml new file mode 100644 index 0000000000..30ec3e34c2 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_edu_briefcase.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_edu_calculator.xml b/app/src/main/res/drawable/ic_vue_edu_calculator.xml new file mode 100644 index 0000000000..63aef6b949 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_edu_calculator.xml @@ -0,0 +1,62 @@ + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_edu_glass.xml b/app/src/main/res/drawable/ic_vue_edu_glass.xml new file mode 100644 index 0000000000..3fb60d7c38 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_edu_glass.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_edu_graduate_cap.xml b/app/src/main/res/drawable/ic_vue_edu_graduate_cap.xml new file mode 100644 index 0000000000..53f47ad38d --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_edu_graduate_cap.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_edu_magazine.xml b/app/src/main/res/drawable/ic_vue_edu_magazine.xml new file mode 100644 index 0000000000..0da63523dc --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_edu_magazine.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_edu_note.xml b/app/src/main/res/drawable/ic_vue_edu_note.xml new file mode 100644 index 0000000000..bcae87e919 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_edu_note.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_edu_omega.xml b/app/src/main/res/drawable/ic_vue_edu_omega.xml new file mode 100644 index 0000000000..9876f9390a --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_edu_omega.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_edu_pen.xml b/app/src/main/res/drawable/ic_vue_edu_pen.xml new file mode 100644 index 0000000000..361869ef81 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_edu_pen.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_edu_planer.xml b/app/src/main/res/drawable/ic_vue_edu_planer.xml new file mode 100644 index 0000000000..3cf061277c --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_edu_planer.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_edu_ruler_pen.xml b/app/src/main/res/drawable/ic_vue_edu_ruler_pen.xml new file mode 100644 index 0000000000..6f2228ed2a --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_edu_ruler_pen.xml @@ -0,0 +1,48 @@ + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_edu_telescope.xml b/app/src/main/res/drawable/ic_vue_edu_telescope.xml new file mode 100644 index 0000000000..7773c0f824 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_edu_telescope.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_edu_todo.xml b/app/src/main/res/drawable/ic_vue_edu_todo.xml new file mode 100644 index 0000000000..a52122c4af --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_edu_todo.xml @@ -0,0 +1,48 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_files_folder.xml b/app/src/main/res/drawable/ic_vue_files_folder.xml new file mode 100644 index 0000000000..35b28d52cc --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_files_folder.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable/ic_vue_files_folder_cloud.xml b/app/src/main/res/drawable/ic_vue_files_folder_cloud.xml new file mode 100644 index 0000000000..9a08ce5f96 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_files_folder_cloud.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_files_folder_favorite.xml b/app/src/main/res/drawable/ic_vue_files_folder_favorite.xml new file mode 100644 index 0000000000..f9ca76131d --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_files_folder_favorite.xml @@ -0,0 +1,18 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_location_discover.xml b/app/src/main/res/drawable/ic_vue_location_discover.xml new file mode 100644 index 0000000000..103a828849 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_location_discover.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_location_global.xml b/app/src/main/res/drawable/ic_vue_location_global.xml new file mode 100644 index 0000000000..af57f1a2bc --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_location_global.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_location_global_edit.xml b/app/src/main/res/drawable/ic_vue_location_global_edit.xml new file mode 100644 index 0000000000..e88cb41a7b --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_location_global_edit.xml @@ -0,0 +1,55 @@ + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_location_global_search.xml b/app/src/main/res/drawable/ic_vue_location_global_search.xml new file mode 100644 index 0000000000..46453b709f --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_location_global_search.xml @@ -0,0 +1,55 @@ + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_location_location.xml b/app/src/main/res/drawable/ic_vue_location_location.xml new file mode 100644 index 0000000000..fcb757c6bd --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_location_location.xml @@ -0,0 +1,16 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_location_map.xml b/app/src/main/res/drawable/ic_vue_location_map.xml new file mode 100644 index 0000000000..1277f9c4c5 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_location_map.xml @@ -0,0 +1,39 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_location_map1.xml b/app/src/main/res/drawable/ic_vue_location_map1.xml new file mode 100644 index 0000000000..63e3c04405 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_location_map1.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_location_radar.xml b/app/src/main/res/drawable/ic_vue_location_radar.xml new file mode 100644 index 0000000000..aa4556b60c --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_location_radar.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_location_routing.xml b/app/src/main/res/drawable/ic_vue_location_routing.xml new file mode 100644 index 0000000000..66fbd4af92 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_location_routing.xml @@ -0,0 +1,37 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_main_archive.xml b/app/src/main/res/drawable/ic_vue_main_archive.xml new file mode 100644 index 0000000000..872f6b67d1 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_main_archive.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_main_battery_charging.xml b/app/src/main/res/drawable/ic_vue_main_battery_charging.xml new file mode 100644 index 0000000000..51cad3239b --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_main_battery_charging.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_main_battery_half.xml b/app/src/main/res/drawable/ic_vue_main_battery_half.xml new file mode 100644 index 0000000000..5275d9828e --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_main_battery_half.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_main_broom.xml b/app/src/main/res/drawable/ic_vue_main_broom.xml new file mode 100644 index 0000000000..db2f2cb152 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_main_broom.xml @@ -0,0 +1,48 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_main_cake.xml b/app/src/main/res/drawable/ic_vue_main_cake.xml new file mode 100644 index 0000000000..7c674fd0cd --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_main_cake.xml @@ -0,0 +1,55 @@ + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_main_calendar.xml b/app/src/main/res/drawable/ic_vue_main_calendar.xml new file mode 100644 index 0000000000..01e6a3ec73 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_main_calendar.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_main_clock.xml b/app/src/main/res/drawable/ic_vue_main_clock.xml new file mode 100644 index 0000000000..a01282984a --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_main_clock.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_main_coffee.xml b/app/src/main/res/drawable/ic_vue_main_coffee.xml new file mode 100644 index 0000000000..8e714a1483 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_main_coffee.xml @@ -0,0 +1,48 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_main_crown.xml b/app/src/main/res/drawable/ic_vue_main_crown.xml new file mode 100644 index 0000000000..6869756b84 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_main_crown.xml @@ -0,0 +1,13 @@ + + + diff --git a/app/src/main/res/drawable/ic_vue_main_cup.xml b/app/src/main/res/drawable/ic_vue_main_cup.xml new file mode 100644 index 0000000000..8b58747973 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_main_cup.xml @@ -0,0 +1,46 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_main_emoji_happy.xml b/app/src/main/res/drawable/ic_vue_main_emoji_happy.xml new file mode 100644 index 0000000000..1c4c5f346e --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_main_emoji_happy.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_main_emoji_normal.xml b/app/src/main/res/drawable/ic_vue_main_emoji_normal.xml new file mode 100644 index 0000000000..a6cefdfc47 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_main_emoji_normal.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_main_emoji_sad.xml b/app/src/main/res/drawable/ic_vue_main_emoji_sad.xml new file mode 100644 index 0000000000..216b50f792 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_main_emoji_sad.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_main_flash.xml b/app/src/main/res/drawable/ic_vue_main_flash.xml new file mode 100644 index 0000000000..d82b6ca802 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_main_flash.xml @@ -0,0 +1,13 @@ + + + diff --git a/app/src/main/res/drawable/ic_vue_main_gift.xml b/app/src/main/res/drawable/ic_vue_main_gift.xml new file mode 100644 index 0000000000..5fa0c1a750 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_main_gift.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_main_glass.xml b/app/src/main/res/drawable/ic_vue_main_glass.xml new file mode 100644 index 0000000000..0cab6e35f0 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_main_glass.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_main_home.xml b/app/src/main/res/drawable/ic_vue_main_home.xml new file mode 100644 index 0000000000..0bd31fa670 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_main_home.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_main_home_safe.xml b/app/src/main/res/drawable/ic_vue_main_home_safe.xml new file mode 100644 index 0000000000..4a5caa3cea --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_main_home_safe.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_main_home_wifi.xml b/app/src/main/res/drawable/ic_vue_main_home_wifi.xml new file mode 100644 index 0000000000..74f587b6fe --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_main_home_wifi.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_main_judge.xml b/app/src/main/res/drawable/ic_vue_main_judge.xml new file mode 100644 index 0000000000..2734bce4eb --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_main_judge.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_main_lamp.xml b/app/src/main/res/drawable/ic_vue_main_lamp.xml new file mode 100644 index 0000000000..3dab639970 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_main_lamp.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_main_lamp_charge.xml b/app/src/main/res/drawable/ic_vue_main_lamp_charge.xml new file mode 100644 index 0000000000..1f2930b0e3 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_main_lamp_charge.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_main_lifebuoy.xml b/app/src/main/res/drawable/ic_vue_main_lifebuoy.xml new file mode 100644 index 0000000000..a827c7102c --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_main_lifebuoy.xml @@ -0,0 +1,48 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_main_milk.xml b/app/src/main/res/drawable/ic_vue_main_milk.xml new file mode 100644 index 0000000000..56f6cabb3e --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_main_milk.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_main_notification.xml b/app/src/main/res/drawable/ic_vue_main_notification.xml new file mode 100644 index 0000000000..00fdfbaa74 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_main_notification.xml @@ -0,0 +1,24 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_main_pet.xml b/app/src/main/res/drawable/ic_vue_main_pet.xml new file mode 100644 index 0000000000..ac9dd5daf5 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_main_pet.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_main_reserve.xml b/app/src/main/res/drawable/ic_vue_main_reserve.xml new file mode 100644 index 0000000000..a79a5e5d68 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_main_reserve.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_main_send.xml b/app/src/main/res/drawable/ic_vue_main_send.xml new file mode 100644 index 0000000000..23adb66884 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_main_send.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_main_share.xml b/app/src/main/res/drawable/ic_vue_main_share.xml new file mode 100644 index 0000000000..e79b0a6b82 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_main_share.xml @@ -0,0 +1,48 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_main_signpost.xml b/app/src/main/res/drawable/ic_vue_main_signpost.xml new file mode 100644 index 0000000000..d8803a7aa3 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_main_signpost.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_main_sport.xml b/app/src/main/res/drawable/ic_vue_main_sport.xml new file mode 100644 index 0000000000..d0dbde9153 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_main_sport.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_main_timer.xml b/app/src/main/res/drawable/ic_vue_main_timer.xml new file mode 100644 index 0000000000..e369d6f537 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_main_timer.xml @@ -0,0 +1,13 @@ + + + diff --git a/app/src/main/res/drawable/ic_vue_main_trash.xml b/app/src/main/res/drawable/ic_vue_main_trash.xml new file mode 100644 index 0000000000..39f2d6aca8 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_main_trash.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_main_tree.xml b/app/src/main/res/drawable/ic_vue_main_tree.xml new file mode 100644 index 0000000000..4ce053718b --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_main_tree.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_media_camera.xml b/app/src/main/res/drawable/ic_vue_media_camera.xml new file mode 100644 index 0000000000..db8265eb56 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_media_camera.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_media_film.xml b/app/src/main/res/drawable/ic_vue_media_film.xml new file mode 100644 index 0000000000..8c14880e81 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_media_film.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_media_film_play.xml b/app/src/main/res/drawable/ic_vue_media_film_play.xml new file mode 100644 index 0000000000..d853c1c262 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_media_film_play.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_media_image.xml b/app/src/main/res/drawable/ic_vue_media_image.xml new file mode 100644 index 0000000000..49937ef676 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_media_image.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_media_microphone.xml b/app/src/main/res/drawable/ic_vue_media_microphone.xml new file mode 100644 index 0000000000..7e108950fe --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_media_microphone.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_media_mountains.xml b/app/src/main/res/drawable/ic_vue_media_mountains.xml new file mode 100644 index 0000000000..8938a8fd7c --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_media_mountains.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_media_music.xml b/app/src/main/res/drawable/ic_vue_media_music.xml new file mode 100644 index 0000000000..2ba5d89864 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_media_music.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_media_photocamera.xml b/app/src/main/res/drawable/ic_vue_media_photocamera.xml new file mode 100644 index 0000000000..fc51192124 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_media_photocamera.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_media_play.xml b/app/src/main/res/drawable/ic_vue_media_play.xml new file mode 100644 index 0000000000..a0fe455a35 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_media_play.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_media_scissors.xml b/app/src/main/res/drawable/ic_vue_media_scissors.xml new file mode 100644 index 0000000000..e844187da9 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_media_scissors.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_media_screenmirroring.xml b/app/src/main/res/drawable/ic_vue_media_screenmirroring.xml new file mode 100644 index 0000000000..4eeecda467 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_media_screenmirroring.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_media_setting.xml b/app/src/main/res/drawable/ic_vue_media_setting.xml new file mode 100644 index 0000000000..3b3cc0ddc0 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_media_setting.xml @@ -0,0 +1,55 @@ + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_media_speaker.xml b/app/src/main/res/drawable/ic_vue_media_speaker.xml new file mode 100644 index 0000000000..971385848b --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_media_speaker.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_media_subtitle.xml b/app/src/main/res/drawable/ic_vue_media_subtitle.xml new file mode 100644 index 0000000000..eaca75bf11 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_media_subtitle.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_media_voice.xml b/app/src/main/res/drawable/ic_vue_media_voice.xml new file mode 100644 index 0000000000..8ccad935d5 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_media_voice.xml @@ -0,0 +1,48 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_messages_device_msg.xml b/app/src/main/res/drawable/ic_vue_messages_device_msg.xml new file mode 100644 index 0000000000..305d79983d --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_messages_device_msg.xml @@ -0,0 +1,55 @@ + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_messages_direct.xml b/app/src/main/res/drawable/ic_vue_messages_direct.xml new file mode 100644 index 0000000000..22621435e9 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_messages_direct.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_messages_edit.xml b/app/src/main/res/drawable/ic_vue_messages_edit.xml new file mode 100644 index 0000000000..7267dce85d --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_messages_edit.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_messages_letter.xml b/app/src/main/res/drawable/ic_vue_messages_letter.xml new file mode 100644 index 0000000000..9d1a3cf83c --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_messages_letter.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_messages_msg.xml b/app/src/main/res/drawable/ic_vue_messages_msg.xml new file mode 100644 index 0000000000..a6d462f7f0 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_messages_msg.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_messages_msg_favorite.xml b/app/src/main/res/drawable/ic_vue_messages_msg_favorite.xml new file mode 100644 index 0000000000..fb4b86b155 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_messages_msg_favorite.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_messages_msg_notification.xml b/app/src/main/res/drawable/ic_vue_messages_msg_notification.xml new file mode 100644 index 0000000000..2ecc9eafaf --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_messages_msg_notification.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_messages_msg_search.xml b/app/src/main/res/drawable/ic_vue_messages_msg_search.xml new file mode 100644 index 0000000000..edcf911362 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_messages_msg_search.xml @@ -0,0 +1,48 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_messages_msg_text.xml b/app/src/main/res/drawable/ic_vue_messages_msg_text.xml new file mode 100644 index 0000000000..67bee42fcf --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_messages_msg_text.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_messages_msgs.xml b/app/src/main/res/drawable/ic_vue_messages_msgs.xml new file mode 100644 index 0000000000..ad80ef4026 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_messages_msgs.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_money_archive.xml b/app/src/main/res/drawable/ic_vue_money_archive.xml new file mode 100644 index 0000000000..a140d4e5e0 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_money_archive.xml @@ -0,0 +1,48 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_money_bitcoin_refresh.xml b/app/src/main/res/drawable/ic_vue_money_bitcoin_refresh.xml new file mode 100644 index 0000000000..6ea5afd05d --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_money_bitcoin_refresh.xml @@ -0,0 +1,62 @@ + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_money_buy_bitcoin.xml b/app/src/main/res/drawable/ic_vue_money_buy_bitcoin.xml new file mode 100644 index 0000000000..4cf36d31c7 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_money_buy_bitcoin.xml @@ -0,0 +1,62 @@ + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_money_buy_crypto.xml b/app/src/main/res/drawable/ic_vue_money_buy_crypto.xml new file mode 100644 index 0000000000..ec9580734a --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_money_buy_crypto.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_money_card.xml b/app/src/main/res/drawable/ic_vue_money_card.xml new file mode 100644 index 0000000000..50a367f9a1 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_money_card.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_money_card_bitcoin.xml b/app/src/main/res/drawable/ic_vue_money_card_bitcoin.xml new file mode 100644 index 0000000000..98d720602c --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_money_card_bitcoin.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_money_card_coin.xml b/app/src/main/res/drawable/ic_vue_money_card_coin.xml new file mode 100644 index 0000000000..156e521ea9 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_money_card_coin.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_money_card_receive.xml b/app/src/main/res/drawable/ic_vue_money_card_receive.xml new file mode 100644 index 0000000000..75fdbe938c --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_money_card_receive.xml @@ -0,0 +1,48 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_money_card_send.xml b/app/src/main/res/drawable/ic_vue_money_card_send.xml new file mode 100644 index 0000000000..4c3d467822 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_money_card_send.xml @@ -0,0 +1,48 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_money_coins.xml b/app/src/main/res/drawable/ic_vue_money_coins.xml new file mode 100644 index 0000000000..28e6e7379a --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_money_coins.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_money_discount.xml b/app/src/main/res/drawable/ic_vue_money_discount.xml new file mode 100644 index 0000000000..7845cd6d24 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_money_discount.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_money_dollar.xml b/app/src/main/res/drawable/ic_vue_money_dollar.xml new file mode 100644 index 0000000000..7fae0d67e4 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_money_dollar.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_money_math.xml b/app/src/main/res/drawable/ic_vue_money_math.xml new file mode 100644 index 0000000000..f6c3fa6a94 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_money_math.xml @@ -0,0 +1,55 @@ + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_money_percentage.xml b/app/src/main/res/drawable/ic_vue_money_percentage.xml new file mode 100644 index 0000000000..b0999ccc93 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_money_percentage.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_money_receipt_discount.xml b/app/src/main/res/drawable/ic_vue_money_receipt_discount.xml new file mode 100644 index 0000000000..be393ef068 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_money_receipt_discount.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_money_receipt_empty.xml b/app/src/main/res/drawable/ic_vue_money_receipt_empty.xml new file mode 100644 index 0000000000..f5d55457d9 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_money_receipt_empty.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_money_receipt_items.xml b/app/src/main/res/drawable/ic_vue_money_receipt_items.xml new file mode 100644 index 0000000000..4ca0143cce --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_money_receipt_items.xml @@ -0,0 +1,48 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_money_recive.xml b/app/src/main/res/drawable/ic_vue_money_recive.xml new file mode 100644 index 0000000000..36804ecb7f --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_money_recive.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_money_security_card.xml b/app/src/main/res/drawable/ic_vue_money_security_card.xml new file mode 100644 index 0000000000..8f9fddd32d --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_money_security_card.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_money_send.xml b/app/src/main/res/drawable/ic_vue_money_send.xml new file mode 100644 index 0000000000..07bcd2849b --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_money_send.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_money_tag.xml b/app/src/main/res/drawable/ic_vue_money_tag.xml new file mode 100644 index 0000000000..4feddde863 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_money_tag.xml @@ -0,0 +1,19 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_money_ticket.xml b/app/src/main/res/drawable/ic_vue_money_ticket.xml new file mode 100644 index 0000000000..e0be1198b7 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_money_ticket.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_money_ticket_discount.xml b/app/src/main/res/drawable/ic_vue_money_ticket_discount.xml new file mode 100644 index 0000000000..31cd986599 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_money_ticket_discount.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_money_ticket_star.xml b/app/src/main/res/drawable/ic_vue_money_ticket_star.xml new file mode 100644 index 0000000000..9cc5b57613 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_money_ticket_star.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_money_transfer.xml b/app/src/main/res/drawable/ic_vue_money_transfer.xml new file mode 100644 index 0000000000..be9b41cbd1 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_money_transfer.xml @@ -0,0 +1,48 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_money_wallet.xml b/app/src/main/res/drawable/ic_vue_money_wallet.xml new file mode 100644 index 0000000000..c975c1acc6 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_money_wallet.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_money_wallet_cards.xml b/app/src/main/res/drawable/ic_vue_money_wallet_cards.xml new file mode 100644 index 0000000000..9bdd4cf71b --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_money_wallet_cards.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_money_wallet_empty.xml b/app/src/main/res/drawable/ic_vue_money_wallet_empty.xml new file mode 100644 index 0000000000..e1901e27d4 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_money_wallet_empty.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_money_wallet_money.xml b/app/src/main/res/drawable/ic_vue_money_wallet_money.xml new file mode 100644 index 0000000000..9e6dcf18b5 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_money_wallet_money.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_pc_bluetooth.xml b/app/src/main/res/drawable/ic_vue_pc_bluetooth.xml new file mode 100644 index 0000000000..e996ccb6db --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_pc_bluetooth.xml @@ -0,0 +1,13 @@ + + + diff --git a/app/src/main/res/drawable/ic_vue_pc_charging.xml b/app/src/main/res/drawable/ic_vue_pc_charging.xml new file mode 100644 index 0000000000..91f0bfa6b2 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_pc_charging.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_pc_cpu.xml b/app/src/main/res/drawable/ic_vue_pc_cpu.xml new file mode 100644 index 0000000000..ee2c61c556 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_pc_cpu.xml @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_pc_game.xml b/app/src/main/res/drawable/ic_vue_pc_game.xml new file mode 100644 index 0000000000..023e9eb3fa --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_pc_game.xml @@ -0,0 +1,62 @@ + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_pc_gameboy.xml b/app/src/main/res/drawable/ic_vue_pc_gameboy.xml new file mode 100644 index 0000000000..5079b13073 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_pc_gameboy.xml @@ -0,0 +1,48 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_pc_headphone.xml b/app/src/main/res/drawable/ic_vue_pc_headphone.xml new file mode 100644 index 0000000000..14e4c315d4 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_pc_headphone.xml @@ -0,0 +1,13 @@ + + + diff --git a/app/src/main/res/drawable/ic_vue_pc_monitor.xml b/app/src/main/res/drawable/ic_vue_pc_monitor.xml new file mode 100644 index 0000000000..9d107f75ad --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_pc_monitor.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_pc_phone.xml b/app/src/main/res/drawable/ic_vue_pc_phone.xml new file mode 100644 index 0000000000..adf41c0353 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_pc_phone.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable/ic_vue_pc_phone_call.xml b/app/src/main/res/drawable/ic_vue_pc_phone_call.xml new file mode 100644 index 0000000000..4d21c4e54d --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_pc_phone_call.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_pc_printer.xml b/app/src/main/res/drawable/ic_vue_pc_printer.xml new file mode 100644 index 0000000000..a9c3676556 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_pc_printer.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_pc_setting.xml b/app/src/main/res/drawable/ic_vue_pc_setting.xml new file mode 100644 index 0000000000..dc33c3f93d --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_pc_setting.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_pc_speaker.xml b/app/src/main/res/drawable/ic_vue_pc_speaker.xml new file mode 100644 index 0000000000..86d637c460 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_pc_speaker.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_pc_watch.xml b/app/src/main/res/drawable/ic_vue_pc_watch.xml new file mode 100644 index 0000000000..3ea71a183d --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_pc_watch.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_pc_wifi.xml b/app/src/main/res/drawable/ic_vue_pc_wifi.xml new file mode 100644 index 0000000000..5bf7c35419 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_pc_wifi.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_people_2persons.xml b/app/src/main/res/drawable/ic_vue_people_2persons.xml new file mode 100644 index 0000000000..1440c8df01 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_people_2persons.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_people_people.xml b/app/src/main/res/drawable/ic_vue_people_people.xml new file mode 100644 index 0000000000..47e49e90e7 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_people_people.xml @@ -0,0 +1,48 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_people_person.xml b/app/src/main/res/drawable/ic_vue_people_person.xml new file mode 100644 index 0000000000..bcd85734cd --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_people_person.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_people_person_search.xml b/app/src/main/res/drawable/ic_vue_people_person_search.xml new file mode 100644 index 0000000000..94b28fbe11 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_people_person_search.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_people_person_tag.xml b/app/src/main/res/drawable/ic_vue_people_person_tag.xml new file mode 100644 index 0000000000..2b50dc6ebf --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_people_person_tag.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_security_alarm.xml b/app/src/main/res/drawable/ic_vue_security_alarm.xml new file mode 100644 index 0000000000..e1a0f69d3a --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_security_alarm.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_security_eye.xml b/app/src/main/res/drawable/ic_vue_security_eye.xml new file mode 100644 index 0000000000..35c6e5716e --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_security_eye.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_security_key.xml b/app/src/main/res/drawable/ic_vue_security_key.xml new file mode 100644 index 0000000000..2ef0eb68d6 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_security_key.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_security_lock.xml b/app/src/main/res/drawable/ic_vue_security_lock.xml new file mode 100644 index 0000000000..d99a38d171 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_security_lock.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_security_password.xml b/app/src/main/res/drawable/ic_vue_security_password.xml new file mode 100644 index 0000000000..1d93b7ee5d --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_security_password.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_security_radar.xml b/app/src/main/res/drawable/ic_vue_security_radar.xml new file mode 100644 index 0000000000..4140378806 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_security_radar.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_security_shield.xml b/app/src/main/res/drawable/ic_vue_security_shield.xml new file mode 100644 index 0000000000..b60a31511a --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_security_shield.xml @@ -0,0 +1,13 @@ + + + diff --git a/app/src/main/res/drawable/ic_vue_security_shield_person.xml b/app/src/main/res/drawable/ic_vue_security_shield_person.xml new file mode 100644 index 0000000000..1143640e56 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_security_shield_person.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_security_shield_security.xml b/app/src/main/res/drawable/ic_vue_security_shield_security.xml new file mode 100644 index 0000000000..36bb64e0a5 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_security_shield_security.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_shop_bag.xml b/app/src/main/res/drawable/ic_vue_shop_bag.xml new file mode 100644 index 0000000000..8f96cf3eed --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_shop_bag.xml @@ -0,0 +1,43 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_shop_bag1.xml b/app/src/main/res/drawable/ic_vue_shop_bag1.xml new file mode 100644 index 0000000000..38bba38f19 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_shop_bag1.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_shop_barcode.xml b/app/src/main/res/drawable/ic_vue_shop_barcode.xml new file mode 100644 index 0000000000..3fe1a48472 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_shop_barcode.xml @@ -0,0 +1,62 @@ + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_shop_cart.xml b/app/src/main/res/drawable/ic_vue_shop_cart.xml new file mode 100644 index 0000000000..3b8486d6e6 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_shop_cart.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_shop_shop.xml b/app/src/main/res/drawable/ic_vue_shop_shop.xml new file mode 100644 index 0000000000..0ef6b05cae --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_shop_shop.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_support_dislike.xml b/app/src/main/res/drawable/ic_vue_support_dislike.xml new file mode 100644 index 0000000000..02dffec5a1 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_support_dislike.xml @@ -0,0 +1,18 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_support_heart.xml b/app/src/main/res/drawable/ic_vue_support_heart.xml new file mode 100644 index 0000000000..b058962134 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_support_heart.xml @@ -0,0 +1,13 @@ + + + diff --git a/app/src/main/res/drawable/ic_vue_support_like.xml b/app/src/main/res/drawable/ic_vue_support_like.xml new file mode 100644 index 0000000000..a7e1ecb5bf --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_support_like.xml @@ -0,0 +1,18 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_support_like_dislike.xml b/app/src/main/res/drawable/ic_vue_support_like_dislike.xml new file mode 100644 index 0000000000..77cf04c7f4 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_support_like_dislike.xml @@ -0,0 +1,30 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_support_medal.xml b/app/src/main/res/drawable/ic_vue_support_medal.xml new file mode 100644 index 0000000000..41e7d8810a --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_support_medal.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_support_smileys.xml b/app/src/main/res/drawable/ic_vue_support_smileys.xml new file mode 100644 index 0000000000..62b7861e4a --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_support_smileys.xml @@ -0,0 +1,62 @@ + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_support_star.xml b/app/src/main/res/drawable/ic_vue_support_star.xml new file mode 100644 index 0000000000..9f55e7de16 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_support_star.xml @@ -0,0 +1,13 @@ + + + diff --git a/app/src/main/res/drawable/ic_vue_transport_airplane.xml b/app/src/main/res/drawable/ic_vue_transport_airplane.xml new file mode 100644 index 0000000000..881506f63a --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_transport_airplane.xml @@ -0,0 +1,13 @@ + + + diff --git a/app/src/main/res/drawable/ic_vue_transport_bus.xml b/app/src/main/res/drawable/ic_vue_transport_bus.xml new file mode 100644 index 0000000000..19173fe62a --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_transport_bus.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_transport_car.xml b/app/src/main/res/drawable/ic_vue_transport_car.xml new file mode 100644 index 0000000000..c6a6a7bcd6 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_transport_car.xml @@ -0,0 +1,62 @@ + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_transport_car_wash.xml b/app/src/main/res/drawable/ic_vue_transport_car_wash.xml new file mode 100644 index 0000000000..22b3435888 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_transport_car_wash.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_transport_gas.xml b/app/src/main/res/drawable/ic_vue_transport_gas.xml new file mode 100644 index 0000000000..d39f08ad03 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_transport_gas.xml @@ -0,0 +1,41 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_transport_ship.xml b/app/src/main/res/drawable/ic_vue_transport_ship.xml new file mode 100644 index 0000000000..8c9b3a80be --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_transport_ship.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_transport_train.xml b/app/src/main/res/drawable/ic_vue_transport_train.xml new file mode 100644 index 0000000000..3a381611f2 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_transport_train.xml @@ -0,0 +1,62 @@ + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_type_link.xml b/app/src/main/res/drawable/ic_vue_type_link.xml new file mode 100644 index 0000000000..9ff9212b89 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_type_link.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_type_link2.xml b/app/src/main/res/drawable/ic_vue_type_link2.xml new file mode 100644 index 0000000000..4c6790d1ce --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_type_link2.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_type_paperclip.xml b/app/src/main/res/drawable/ic_vue_type_paperclip.xml new file mode 100644 index 0000000000..24b353e812 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_type_paperclip.xml @@ -0,0 +1,13 @@ + + + diff --git a/app/src/main/res/drawable/ic_vue_type_text.xml b/app/src/main/res/drawable/ic_vue_type_text.xml new file mode 100644 index 0000000000..4757c3aaac --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_type_text.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_vue_type_textalign_center.xml b/app/src/main/res/drawable/ic_vue_type_textalign_center.xml new file mode 100644 index 0000000000..1c9657cd19 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_type_textalign_center.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_type_textalign_justifycenter.xml b/app/src/main/res/drawable/ic_vue_type_textalign_justifycenter.xml new file mode 100644 index 0000000000..57b7ae63c1 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_type_textalign_justifycenter.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_type_textalign_left.xml b/app/src/main/res/drawable/ic_vue_type_textalign_left.xml new file mode 100644 index 0000000000..b33e70dd8e --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_type_textalign_left.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_type_textalign_right.xml b/app/src/main/res/drawable/ic_vue_type_textalign_right.xml new file mode 100644 index 0000000000..1e1aa0078f --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_type_textalign_right.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_type_translate.xml b/app/src/main/res/drawable/ic_vue_type_translate.xml new file mode 100644 index 0000000000..4f78638301 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_type_translate.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_weather_cloud.xml b/app/src/main/res/drawable/ic_vue_weather_cloud.xml new file mode 100644 index 0000000000..d3dae4ed5e --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_weather_cloud.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_weather_cold.xml b/app/src/main/res/drawable/ic_vue_weather_cold.xml new file mode 100644 index 0000000000..389ff1dc78 --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_weather_cold.xml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_weather_drop.xml b/app/src/main/res/drawable/ic_vue_weather_drop.xml new file mode 100644 index 0000000000..9ffac7946b --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_weather_drop.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable/ic_vue_weather_flash.xml b/app/src/main/res/drawable/ic_vue_weather_flash.xml new file mode 100644 index 0000000000..cb9e7c6abf --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_weather_flash.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_vue_weather_moon.xml b/app/src/main/res/drawable/ic_vue_weather_moon.xml new file mode 100644 index 0000000000..f68ef893ec --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_weather_moon.xml @@ -0,0 +1,13 @@ + + + diff --git a/app/src/main/res/drawable/ic_vue_weather_sun.xml b/app/src/main/res/drawable/ic_vue_weather_sun.xml new file mode 100644 index 0000000000..419faf4e5a --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_weather_sun.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vue_weather_wind.xml b/app/src/main/res/drawable/ic_vue_weather_wind.xml new file mode 100644 index 0000000000..eaeedfa13d --- /dev/null +++ b/app/src/main/res/drawable/ic_vue_weather_wind.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/layout/widget_add_transaction.xml b/app/src/main/res/layout/widget_add_transaction.xml index 50be6d422b..ca997821c7 100644 --- a/app/src/main/res/layout/widget_add_transaction.xml +++ b/app/src/main/res/layout/widget_add_transaction.xml @@ -39,7 +39,7 @@ android:layout_marginTop="10dp" android:fontFamily="@font/raleway_bold" android:gravity="center" - android:text="Income" + android:text="@string/income" android:textColor="@color/white" android:textSize="12sp" /> @@ -68,7 +68,7 @@ android:layout_marginTop="10dp" android:fontFamily="@font/raleway_bold" android:gravity="center" - android:text="Expense" + android:text="@string/expense" android:textColor="@color/white" android:textSize="12sp" /> @@ -95,7 +95,7 @@ android:layout_marginTop="10dp" android:fontFamily="@font/raleway_bold" android:gravity="center" - android:text="Transfer" + android:text="@string/transfer" android:textColor="@color/white" android:textSize="12sp" /> diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml new file mode 100644 index 0000000000..67a4c0cd5e --- /dev/null +++ b/app/src/main/res/values-it/strings.xml @@ -0,0 +1,429 @@ + + + Conti + Totale: %1$s %2$s + ENTRATE DEL MESE + USCITE DEL MESE + (escluso) + ENTRATE + USCITE + APP BLOCCATA + Autenticati per entrare nell\'app + Sblocca + SALDO ATTUALE + SALDO DOPO I PAGAMENTI PIANIFICATI + Connetti + Sincronizza transazioni + Sincronizzazione transazioni… + Sincronizzazione della banca attiva: + Rimuovi client + Aggiungi budget + Nessun budget + Non hai nessun budget impostato.\nTocca \"+ Aggiungi budget\" per aggiungerne uno. + Budgets + %1$s %2$s per categorie + %1$s %2$s budget dell\'app + Informazioni sul budget: %1$s / %2$s + Info sul budget %1$s%2$s + Aggiungi categoria + Uscite + Numero uscite + Entrate + Numero entrate + Grafico del saldo + SALDO %1$s + Grafici + Periodo: + Categorie + Esporta file CSV + Esporta il file CSV con le opzioni standard + Si prega di utilizzare le opzioni standard e assicurarsi di includere le intestazioni. + Come importare + apri + Passaggi + Come fare + Video + Articolo + Carica il file CSV + Esporta i dati + Carica il file CSV/ZIP + Esporta su file + Character set: UTF-8\nDecimal separator: Decimal point \'.\'\nDelimiter character: Comma \',\' + Esporta il file Excel + Converti XLS in CSV + !NOTA: Se il file esportato non ha l\'estensione \".xls\", aggiungila rinominando manualmente il file. + Convertitore online di CSV GRATIS + Controlla le cartelle \"Promozioni\" e \"Spam\" della tua email + Scarica il file \"transactions_export…\" allegato all\'email. + Se hai più di una valuta dovrai scaricare ogni file \"transactions_export…\" e importarlo in Ivy. + Importa da + Attendere prego + Importazione del file CSV + Completato + Errore + Importato + %1$d transazioni + %1$d conti + %1$d categorie + Non riuscito + %1$d righe dal file CSV non riconosciute + Fine + Aggiungi descrizione + Descrizione + Pianificato per + Aggiungi denaro a + Paga con + Da + Conto + A + Aggiungi conto + Titolo entrata + Titolo spesa + Titolo del trasferimento + Uscite + Aggiungi la data prevista del pagamento + Paga + Ricevi + Conferma l\'eliminazione + L\'eliminazione di questa transazione la rimuoverà dalla cronologia delle transazioni e aggiornerà di conseguenza il saldo. + Conferma cambio contro + Nota: Stai cercando di modificare il conto associato al prestito con un conto di valuta diversa, \nTutti i record del prestito saranno ricalcolati in base ai tassi di cambio di oggi + Conferma + Attendi, tutti i prestiti stanno venendo ricalcolati + Creato il + Ciao + Ciao %1$s + Flusso di cassa: %1$s%2$s %3$s + Cerca transazioni + Ivy Wallet è open-source + Obiettivo di risparmio + Accesso rapido + Impostazioni + Modalità chiara + Modalità scura + Modalità\nautomatica + Pagamenti\nPianificati + Condividi Ivy + Resoconti + Prestiti + Imposta la valuta + Nessuna transazione + Non hai nessuna transazione per %1$s.\nPuoi aggiungerne una toccando il tasto \"+\". + Aggiungi prestito + Nessun prestito + Non hai alcun prestito.\nTocca il tasto \"+ Aggiungi prestito\" per aggiungerne uno. + Nota: L\'eliminazione di questo prestito lo rimuoverà definitivamente ed eliminerà tutte le registrazioni associate. + Attendi, tutte le registrazioni dei prestiti stanno venendo ricalcolate + Pagato + %1$s %2$s rimasti + Interessi del prestito + %1$s %2$s pagati + Aggiungi registrazione + Interessi + Nessuna registrazione + Non hai ancora nessuna registrazione per questo prestito. Tocca \"Aggiungi registrazione\" per crearne uno. + Aggiungi entrata + Aggiungi uscita + Non specificato + %1$s\%% + Trasferimenti Conti + Non hai nessuna transazione per %1$s.\nÈ possibile aggiungerne una scorrendo verso il basso e toccando il pulsante \"Aggiungi entrata\" o \"Aggiungi uscita\" in alto. + Nota: L\'eliminazione di questo conto lo rimuoverà definitivamente ed eliminerà tutte le registrazioni associate. + Nota: L\'eliminazione di questa categoria la rimuoverà permanentemente. + Modifica + transazioni + Home + Aggiungi pagamento pianificato + AGGIUNGI ENTRATA + AGGIUNGI SPESA + TRASFERIM. CONTO + Salta + Aggiungi nuovo + Da %1$s + A %1$s + Range + Privacy e\nraccolta di dati + Scorri per accettare i nostri Termini e condizioni + In accordo con i nostri Termini e condizioni + Scorri per accettare la nostra informativa sulla privacy + In accordo con la nostra Informativa sulla privacy + Termini e condizioni + Informativa sulla privacy + Traccia le tue entrate, le uscite e i budget con Ivy.\n\nIU intuitiva, pagamenti ricorrenti e pianificati, gestione di più conti, organizzazione delle transazioni in categorie, statistiche eloquenti, esportazione in CSV e molto altro. + Inserisci il tuo nome\nper personalizzare il tuo\nportafoglio + Come ti chiami? + Invia + Aggiungi i conti + Suggerimenti + Prossimo + Aggiungi le categorie + Suggerimenti + Imposta + Il tuo gestore di denaro personale + #opensource + Errore. Prova di nuovo: %1$s + Accesso in corso… + Completato! + Accedi con Google + Account offline + SINCRONIZZA I TUOI DATI SU IVY CLOUD + L\'integrità e la protezione dei dati non sono garantite! + O PROCEDI CON UN ACCOUNT OFFLINE + I tuoi dati verranno salvati localmente (solo sul tuo telefono) e non verranno sincronizzati nel cloud. Rischi di perdete i dati se disinstalli l\'applicazione o se cambi il tuo dispositivo. È sempre possibile attivare la sincronizzazione in seguito se decidi di farlo. + + Accedendo accetti i nostri %1$s e la nostra %2$s. + Importa un file CSV + da Ivy o da un\'altra app + Importare un file di backup da un\'altra app può richiedere fino a 5 minuti. È sempre possibile importare i dati in un secondo momento, se lo desideri. + Importa un file di backup + Inizia da zero + L\'eliminazione di questo pagamento pianificato eliminerà anche tutte le transazioni associate non pagate in arrivo o in ritardo. + Imposta il tipo di pagamento + Inizio pianificato il + SI RIPETE OGNI %1$d %2$s + eliminato + "PIANIFICATO PER " + n.d. + "INIZIA %1$s " + Aggiungi pagamento + Pagamenti una tantum + Pagamenti ricorrenti + Nessun pagamento pianificato + Non hai nessun pagamento pianificato.\nPremi il tasto \'⚡\' in basso per aggiungerne uno. + Pagamenti pianificati + Oggi + Ieri + Domani + Scade %1$s + In arrivo + Scaduti + uscite + entrate + Modifica conto + Nuovo conto + Nome del conto + Includi conto + Inserisci il saldo del conto + Scegli una valuta + Calcolatrice + Calcolo (+-/*=) + Modifica categoria + Crea una categoria + Nome della categoria + Scegli la categoria + Inserisci qui ogni dettaglio (supporta il Markdown) + Cancella filtro + Filtro + Applica il filtro + Per Tipo + Entrate + Periodo di Tempo + Seleziona l\'intervallo di tempo + Conti (%1$d) + Categorie (%1$d) + Cancella tutto + Seleziona tutto + Importo (facoltativo) + Parole chiave (opzionale) + INCLUDI + Aggiungi una parola chiave + ESCLUDI + Non hai transazioni per il tuo filtro. + Nessun Filtro + Per generare un resoconto devi prima impostare un filtro valido. + Imposta Filtro + Esporta + Non hai nessuna transazione corrispondente alla ricerca di \"%1$s\". + + Backup dei dati + Importa i dati + Impostazioni dell\'App + Blocca l\'app + Mostra le notifiche + Nascondi saldo + Fai clic sul saldo nascosto per visualizzarlo per 5s + Altro + Valutaci su Google Play + Condividi Ivy Wallet + Prodotto + Zona pericolosa + Elimina tutti i dati utente + Eliminare tutti i dati utente? + ATTENZIONE! Questa azione eliminerà tutti i dati per %1$s PERMANENTEMENTE e non sarai in grado di recuperarli. + il tuo account + Conferma l\'eliminazione permanente per \'%1$s\' + tutti i tuoi dati + AVVISO FINALE! Dopo aver cliccato su \"Elimina\" i tuoi dati saranno persi per sempre. + Esportando i dati + Attendi, esportazione dei dati in corso + Data di inizio del mese + Ivy Telegram + Centro assistenza + Roadmap + Richiedi una funzionalità + Contatta il supporto + Contributori del progetto + ACCOUNT + Esci + Accedi + Sincronizzazione… + Dati sincronizzati nel cloud + Tocca per sincronizzare + Sincronizzazione non riuscita. Tocca per sincronizzare + Anonimo + Esporta in CSV + Rimangono da spendere + Budget superato di + Margine superato di + Imposta il tipo di transazione + Trasferimento + Selezionato + Cerca (USD, EUR, GBP, BTC, ecc) + Pre-selezionato + Crypto + Tasso Di Cambio + Scegli il colore + Riordina + Parola chiave + Modifica il budget + Crea un budget + Nome del budget + IMPORTO DEL BUDGET + Sei sicuro di voler eliminare il budget \"%1$s\"? + Modifica l\'obiettivo di risparmio + Scegli l\'icona + Scegli il mese + o un intervallo personalizzato + Aggiungi una data + o nell’ultimo periodo di + o tutto il tempo + Deseleziona tutto il tempo + Seleziona tutto il tempo + Scegli la data di inizio del mese + supporta criptovalute + Elimina + Salva + Aggiungi + Crea + Modifica prestito + Nuovo prestito + Nome del prestito + Account Associato + Crea una transazione principale + INSERISCI L\'IMPORTO DEL PRESTITO + "Nota: Stai cercando di modificare il conto associato al prestito con un conto di valuta diversa, \nTutti i record del prestito saranno ricalcolati in base ai tassi di cambio di oggi " + Tipo di prestito + Ricevi in prestito + Dai in prestito + Modifica registrazione + Nuovo record + Note + Segna come interessi + Ricalcola l\'ammontare con i tassi di cambio di oggi + INSERISCI L\'IMPORTO DELLA REGISTRAZIONE + Sei sicuro di voler eliminare la registrazione \"%1$s\"? + "Nota: Stai cercando di modificare il conto associato alla registrazione del prestito con un conto di valuta diversa, \nL'importo sarà ricalcolato in base ai tassi di cambio di oggi " + Modifica il nome + Pianifica per + Una volta + Più volte + Inizia + Ripeti ogni + Invia + Di cosa hai bisogno? + Spiegalo in una frase in inglese. (è supportato il markdown) + Ultimi 12 mesi + Ultimi 6 mesi + Ultime 4 settimane + Ultimi 7 giorni + Oggi, %1$s + Ieri, %1$s + Domani, %1$s + Scaduto + Autenticazione riuscita! + Autenticazione non riuscita. + Hai effettuato delle transazioni oggi? 🏁 + Hai tenuto traccia delle tue spese oggi? 💸 + Hai registrato le tue transazioni oggi? 🏁 + Contanti + Banca + Revolut + + + Trasporti + Alimentari + Intrattenimento + Shopping + Regali + Salute + Investimenti + Auto + Lavoro + Ristorante + Famiglia + Vita Sociale + Cibo a domicilio + Viaggi + Fitness + Self-development + Vestiti + Bellezza + Istruzione + Animali + Sport + Sistema il tuo saldo iniziale + Visualizza i conti + Seleziona un conto -> Seleziona il suo saldo -> Digita il saldo attuale. Ecco fatto!]]> + Crea il tuo primo pagamento pianificato + Automatizza il monitoraggio delle transazioni ricorrenti come i tuoi abbonamenti, l\'affitto, lo stipendio, ecc. Stai al passo con le tue finanze sapendo già quanto devi pagare/ricevere in anticipo. + Lo sapevi? + Ivy Wallet ha un fantastico widget che consente di aggiungere ENTRATE/USCITE/TRASFERIMENTI con un clic dalla tua home\n\nNota: se il pulsante \"Aggiungi widget\" non funziona, aggiungilo manualmente dal menu dei widget del tuo launcher. + Aggiungi widget + Imposta un budget + Ivy Wallet non solo ti aiuta a monitorare passivamente le tue spese, ma crea anche pro-attivamente il tuo futuro finanziario impostando budget e seguendoli. + Puoi vedere lo schema delle tue spese diviso per categorie! Provalo, tocca il pulsante grigio e nero appena sotto il tuo saldo. + Grafico a torta delle uscite + Recensisci Ivy Wallet + Dacci il tuo feedback! Aiuta Ivy Wallet a migliorare e crescere scrivendoci una recensione. Complimenti, idee e critiche sono benvenuti! Facciamo del nostro meglio.\n\nA presto,\nIvy Team + Aiutaci a crescere in modo da poter investire di più nello sviluppo e rendere l\'applicazione migliore per te. Condividendo Ivy Wallet potrai rendere felici due sviluppatori e aiutare un amico a tenere sotto controllo le sue finanze. + Condividi con gli amici + Puoi generare dei resoconti per ottenere un\'analisi approfondita delle tue entrate e delle tue uscite. Filtra le tue transazioni per tipo, periodo di tempo, categoria, conto, parole chiave e non solo per ottenere una vista migliore sulle tue finanze. + Crea un report + Vuoi rendere migliore Ivy Wallet? Scrivici una recensione. Questo, per noi, è l\'unico modo di sviluppare quello che vuoi e che ti serve. Inoltre ci aiuta a ottenere una posizione migliore nel PlayStore così da poter investire sul prodotto pittosto che sul marketing.\n\nFacciamo del nostro meglio.\nIvy Team + Abbiamo bisogno del tuo aiuto! + Siamo solo un designer e uno sviluppatore che lavorano sull\'app dopo i nostri 9–5 lavori. Attualmente, investiamo molto tempo e denaro per generare solo perdite e sfinimento. Se vuoi che continuiamo a sviluppare Ivy Wallet per favore condividilo con amici e familiari.\n\nP.S. Le recensioni sul Google PlayStore aiutano molto! + Ivy Wallet è open-source! + Il codice di Ivy Wallet è aperto e tutti possono vederlo. Crediamo che la trasparenza e l\'etica siano fondamentali per ogni prodotto software. Se ti piace il nostro lavoro e vuoi migliorare l\'app, puoi contribuire nel nostro repository pubblico su Github. + Contribuisci + Sistema il saldo + Autenticazione richiesta + Dimostra di avere accesso a questo dispositivo per sbloccare l\'app. + Budget generale + Budget di categoria + Budget multi-categoria (%1$s) + RICEVUTO + PRESTATO + Gennaio + Febbraio + Marzo + Aprile + Maggio + Giugno + Luglio + Agosto + Settembre + Ottobre + Novembre + Dicembre + giorni + giorno + settimane + settimana + mesi + mese + anni + anno + + Tratta i trasferimenti di conto come entrate o uscite nella schermata dei conti + Casa + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e5f8fdc2aa..21387e1633 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,2 +1,429 @@ + - \ No newline at end of file + Accounts + Total: %1$s %2$s + INCOME THIS MONTH + EXPENSES THIS MONTH + (excluded) + INCOME + EXPENSES + APP LOCKED + Authenticate to enter the app + Unlock + CURRENT BALANCE + BALANCE AFTER PLANNED PAYMENTS + Connect + Sync transactions + Syncing transactions… + Bank sync enabled: + Remove customer + Add budget + No budgets + You don\'t have any budgets set.\nTap the "+ Add budget" to add one. + Budgets + %1$s %2$s for categories + %1$s %2$s app budget + Budget info: %1$s / %2$s + Budget info: %1$s%2$s + Add category + Expenses + Expenses count + Income + Income count + Balance chart + BALANCE %1$s + Charts + Period: + Categories + Export CSV file + Export CSV file with standard options + Please use the standard options and make sure to include headers. + How to import + open + Steps + How to + Video + Article + Upload CSV file + Export Data + Upload CSV/ZIP file + Export to file + Character set: UTF-8\nDecimal separator: Decimal point \'.\'\nDelimiter character: Comma \',\' + Export Excel file + Convert XLS to CSV + !NOTE: If the exported file doesn\'t have ".xls" extension, add it by renaming the file manually. + Online CSV converter FREE + Check your email\'s "Promotions" and "Spam" folders + Download the \"transactions_export…\" file attached to the email. + If you have more than one currency you\'ll have to download each \"transactions_export…\" file and import it in Ivy. + Import from + Please wait + Importing the CSV file + Success + Failure + Imported + %1$d transactions + %1$d accounts + %1$d categories + Failed + %1$d rows from CSV file not recognized + Finish + Add description + Description + Planned for + Add money to + Pay with + From + Account + To + Add account + Income title + Expense title + Transfer title + Expense + Add planned date of payment + Pay + Get + Confirm deletion + Deleting this transaction will remove it from the transaction history and update the balance accordingly. + Confirm Account Change + Note: You are trying to change the account associated with the loan with an account of different currency, \nAll the loan records will be re-calculated based on today\'s exchanges rates + Confirm + Please wait, re-calculating all loan records + Created on + Hi + Hi %1$s + Cashflow: %1$s%2$s %3$s + Search transactions + Ivy Wallet is open-source + Savings goal + Quick access + Settings + Light mode + Dark mode + Auto mode + Planned\nPayments + Share Ivy + Reports + Loans + Set currency + No transactions + You don\'t have any transactions for %1$s.\nYou can add one by tapping the \"+\" button. + Add loan + No loans + You don\'t have any loans.\nTap the \"+ Add loan\" to add one. + Note: Deleting this loan will remove it permanently and delete all associated loan records with it. + Please wait, re-calculating all loan records + Paid + %1$s %2$s left + Loan Interest + %1$s %2$s paid + Add record + Interest + No records + You don\'t have any records for this loan. Tap "Add record" to create one. + Add income + Add expense + Unspecified + %1$s\%% + Account Transfers + You don\'t have any transactions for %1$s.\nYou can add one by scrolling down and tapping "Add income" or "Add expense" button at the top. + Note: Deleting this account will remove it permanently and delete all associated transactions with it. + Note: Deleting this category will remove it permanently. + Edit + transactions + Home + Add planned payment + ADD INCOME + ADD EXPENSE + ACCOUNT TRANSFER + Skip + Add new + From %1$s + To %1$s + Range + Privacy and\ndata collection + Swipe to agree with our Terms and conditions + Agreed with our Terms and conditions + Swipe to agree with our Privacy policy + Agreed with our Privacy policy + Terms and conditions + Privacy policy + Track your income, expenses and budget with Ivy.\n\nIntuitive UI, recurring and planned payments, manage multiple accounts, organize transactions in categories, meaningful statistics, export to CSV and so much more. + Enter your name\nto personalize your\nwallet + What\'s your name? + Enter + Add accounts + Suggestions + Next + Add categories + Suggestions + Set + Your personal money manager + #opensource + Error. Try again: %1$s + Signing in… + Success! + Login with Google + Offline account + SYNC YOUR DATA ON THE IVY CLOUD + Data integrity and protection aren\'t guaranteed! + OR ENTER WITH OFFLINE ACCOUNT + Your data will be saved locally (only on your phone) and won\'t be synced with the cloud. You risk losing it if you uninstall the app or change your device. You can always activate sync later if you decide to. + + By signing in, you agree with our %1$s and %2$s. + Import CSV file + from Ivy or another app + Importing a backup file from another can take up to 5 min. You can always import your data later if you want to. + Import backup file + Start fresh + Deleting this planned payment will delete all non-paid upcoming or overdue transactions associated with it. + Set payment type + Planned start at + REPEATS EVERY %1$d %2$s + deleted + "PLANNED FOR " + null + "STARTS %1$s " + Add payment + One time payments + Recurring payments + No planned payments + You don\'t have any planned payments.\nPress the \'⚡\' bottom at the bottom to add one. + Planned payments + Today + Yesterday + Tomorrow + Due on %1$s + Upcoming + Overdue + expenses + income + Edit account + New account + Account name + Include account + Enter account balance + Choose currency + Calculator + Calculation (+-/*=) + Edit category + Create category + Category name + Choose category + Enter any details here (supports Markdown) + Clear filter + Filter + Apply filter + By Type + Incomes + Time Period + Select time range + Accounts (%1$d) + Categories (%1$d) + Clear all + Select all + Amount (optional) + Keywords (optional) + INCLUDES + Add a keyword + EXCLUDES + You don\'t have any transactions for your filter. + No Filter + To generate a report you must first set a valid filter. + Set Filter + Export + You don\'t have any transactions for "%1$s" query. + + Backup Data + Import Data + App Settings + Lock app + Show notifications + Hide balance + Click on the hidden balance to show the balance for 5s + Other + Rate us on Google Play + Share Ivy Wallet + Product + Danger zone + Delete all user data + Delete all user data? + WARNING! This action will delete all data for %1$s PERMANENTLY and you won\'t be able to recover it. + your account + Confirm permanent deletion for \'%1$s\' + all of your data + FINAL WARNING! After clicking "Delete" your data will be gone forever. + Exporting Data + Please wait, exporting data + Start date of month + Ivy Telegram + Help Center + Roadmap + Request a feature + Contact support + Project Contributors + ACCOUNT + Logout + Login + Syncing… + Data synced to cloud + Tap to sync + Sync failed. Tap to sync + Anonymous + Export to CSV + Left to spend + Budget exceeded by + Buffer exceeded by + Set transaction type + Transfer + Selected + Search (USD, EUR, GBP, BTC, etc) + Pre-selected + Crypto + Exchange Rate + Choose color + Reorder + Keyword + Edit budget + Create budget + Budget name + BUDGET AMOUNT + Are you sure that you want to delete "%1$s" budget? + Edit Savings goal + Choose icon + Choose month + or custom range + Add date + or in the last + or all time + Unselect All Time + Select All Time + Choose start date of month + supports crypto + Delete + Save + Add + Create + Edit loan + New loan + Loan name + Associated Account + Create a Main Transaction + ENTER LOAN AMOUNT + "Note: You are trying to change the account associated with the loan with an account of different currency, \nAll the loan records will be re-calculated based on today's exchanges rates " + Loan type + Borrow money + Lend money + Edit record + New record + Note + Mark as Interest + Recalculate Amount with today\'s Currency exchange Rates + ENTER RECORD AMOUNT + Are you sure that you want to delete "%1$s" record? + "Note: You are trying to change the account associated with the loan record with an account of different currency\nThe amount will be re-calculated based on today's exchanges rates " + Edit name + Plan for + One time + Multiple times + Starts on + Repeats every + Submit + What do you need? + Explain it in one sentence. (supports markdown) + Last 12 months + Last 6 months + Last 4 weeks + Last 7 days + Today, %1$s + Yesterday, %1$s + Tomorrow, %1$s + Expired + Authentication succeeded! + Authentication failed. + Have you made any transactions today? 🏁 + Did you track your expenses today? 💸 + Have you recorded your transactions today? 🏁 + Cash + Bank + Revolut + + + Transport + Groceries + Entertainment + Shopping + Gifts + Health + Investments + Car + Work + Restaurant + Family + Social Life + Order food + Travel + Fitness + Self-development + Clothes + Beauty + Education + Pet + Sports + Adjust your initial balance + To accounts + Tap an account -> Tap its balance -> Enter current balance. That\'s it!]]> + Create your first planned payment + Automate the tracking of recurring transactions like your subscriptions, rent, salary, etc. Stay ahead of your finances by knowing how much you have to pay/get in advance. + Did you know? + Ivy Wallet has a cool widget that lets you add INCOME/EXPENSES/TRANSFER transactions with 1-click from your home\n\nNote: If the "Add widget" button doesn\'t work, please add it manually from your launcher\'s widgets menu. + Add widget + Set a budget + Ivy Wallet not only helps you to passively track your expenses but also proactively create your financial future by setting budgets and sticking to them. + You can see your expenses structure by categories! Try it, tap the gray/black Expenses button just below your balance. + Expenses PieChart + Review Ivy Wallet + Give us your feedback! Help Ivy Wallet become better and grow by writing us a review. Compliments, ideas, and critics are all welcome! We do our best.\n\nCheers,\nIvy Team + Help us grow so we can invest more in development and make the app better for you. By sharing Ivy Wallet you\'ll make two developers happy and also help a friend to take control of their finances. + Share with friends + You can generate reports to get deep insights about your income and spending. Filter your transactions by type, time period, category, accounts, amount, keywords and more to gain better view on your finances. + Make a report + Want to make Ivy Wallet better? Write us a review. That\'s the only way for us to develop what you want and need. Also it help us rank higher in the PlayStore so we can spend money on the product rather than marketing.\n\nWe do our best.\nIvy Team + We need your help! + We\'re just a designer and a developer working on the app after our 9–5 jobs. Currently, we invest a lot of time and money to generate only losses and exhaustion. If you want us to keep developing Ivy Wallet please share it with friends and family.\n\nP.S. Google PlayStore reviews also helps a lot! + Ivy Wallet is open-source! + Ivy Wallet\'s code is open and everyone can see it. We believe that transparency and ethics are must for every software product. If you like our work and want to make the app better you can contribute in our public Github repository. + Contribute + Adjust balance + Authentication required + Prove that you have access to this device to unlock the app. + Total Budget + Category Budget + Multi-Category (%1$s) Budget + BORROWED + LENT + January + February + March + April + May + June + July + August + September + October + November + December + days + day + weeks + week + months + month + years + year + + Treats account transfers as income or expense in Accounts Screen + Home + diff --git a/buildSrc/src/main/java/com/ivy/wallet/buildsrc/dependencies.kt b/buildSrc/src/main/java/com/ivy/wallet/buildsrc/dependencies.kt index c251639973..e4692588dc 100644 --- a/buildSrc/src/main/java/com/ivy/wallet/buildsrc/dependencies.kt +++ b/buildSrc/src/main/java/com/ivy/wallet/buildsrc/dependencies.kt @@ -22,8 +22,8 @@ import org.gradle.kotlin.dsl.project object Project { //Version - const val versionName = "3.1.2-fast" - const val versionCode = 104 + const val versionName = "4.0.0" + const val versionCode = 105 //Compile SDK & Build Tools const val compileSdkVersion = 31 @@ -46,6 +46,7 @@ fun DependencyHandler.appModuleDependencies( kotlinVersion: String = GlobalVersions.kotlinVersion ) { implementation(project(":ivy-design")) + implementation(project(":ivy-fp")) Kotlin(version = kotlinVersion) Coroutines(version = "1.5.0") @@ -91,6 +92,21 @@ fun DependencyHandler.ivyDesignModuleDependencies( Lifecycle(version = "2.3.1") } +fun DependencyHandler.ivyFPModuleDependencies( + kotlinVersion: String = GlobalVersions.kotlinVersion +) { + Kotlin(version = kotlinVersion) + Coroutines(version = "1.5.0") + FunctionalProgramming( + arrowVersion = "1.0.1", + kotestVersion = "5.1.0", + kotlinVersion = kotlinVersion + ) + + AndroidX() + Lifecycle(version = "2.3.1") +} + //--------------------------------------------------------------------------------- @@ -268,6 +284,9 @@ fun DependencyHandler.Coroutines( //URL: https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-play-services implementation("org.jetbrains.kotlinx:kotlinx-coroutines-play-services:$version") + + //URL: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/ + androidTestImplementation ("org.jetbrains.kotlinx:kotlinx-coroutines-test:$version") } fun DependencyHandler.ThirdParty() { diff --git a/docs/Developer-Guidelines.md b/docs/Developer-Guidelines.md new file mode 100644 index 0000000000..aa1bfc720a --- /dev/null +++ b/docs/Developer-Guidelines.md @@ -0,0 +1,31 @@ +# Ivy Developer Guidelines + +A short guide that'll evolve our time with one and only goal - to make you a better developer. + +[![PRs welcome!](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/ILIYANGERMANOV/ivy-wallet/blob/main/CONTRIBUTING.md) + +> Feedback: Welcome! + +> Proposals: Highly appreciated. + +## I. Domain (Business Logic) + +We classify business logic as any domain-specific logic that: is neither UI nor Android stuff nor IO (persistence or network calls). + +Now knowing what the `domain` isn't, lets define what it is + +# _WIP...._ + +### 1. Functional Programming (pure) + +### 2. Actions (use-cases) + +### 3. ViewModel + +## II. UI + +### `:ivy-design` + +## III. Data model + +## IV. IO (network + persistence) \ No newline at end of file diff --git a/docs/Ivy-Dao.md b/docs/Ivy-Dao.md new file mode 100644 index 0000000000..a566064416 --- /dev/null +++ b/docs/Ivy-Dao.md @@ -0,0 +1,33 @@ +# Ivy DAO + +## High-level picture +```mermaid +graph TD; + contribs(Contributors) + users(Users) + dao(Ivy DAO) + product(Ivy Wallet App) + + dao_dev_fund(R&D Fund) + dao_proposals(Proposals) + + tickets(GitHub Issues) + + contribs -- Develop --> product + contribs -- Design --> product + contribs -- Promote --> product + contribs -- Vote with IVY --> dao_proposals + + product -- Acquire --> users + + users -- Reviews --> product + users -- Donate crypto --> dao + + dao -- Store Donations --> dao_dev_fund + dao -- Smart Contract --> dao_proposals + dao_dev_fund -- Bounty --> dao_proposals + + dao_proposals -- If passed voting --> tickets + tickets -- Earn: Bounty + IVY --> contribs + +``` \ No newline at end of file diff --git a/ivy-design/src/main/java/com/ivy/design/l2_components/InputField.kt b/ivy-design/src/main/java/com/ivy/design/l2_components/InputField.kt index ef553c5d53..6a8d778306 100644 --- a/ivy-design/src/main/java/com/ivy/design/l2_components/InputField.kt +++ b/ivy-design/src/main/java/com/ivy/design/l2_components/InputField.kt @@ -31,6 +31,7 @@ import kotlin.math.roundToInt * - font cannot be set * - handles color must be set Theme XML `accentColor` */ +@Deprecated("A new better componenet would be created soon.") @Composable fun InputField( modifier: Modifier = Modifier, diff --git a/ivy-fp/.gitignore b/ivy-fp/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/ivy-fp/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/ivy-fp/build.gradle.kts b/ivy-fp/build.gradle.kts new file mode 100644 index 0000000000..51215e516c --- /dev/null +++ b/ivy-fp/build.gradle.kts @@ -0,0 +1,52 @@ +import com.ivy.wallet.buildsrc.Project +import com.ivy.wallet.buildsrc.ivyFPModuleDependencies + +plugins { + id("com.android.library") + id("org.jetbrains.kotlin.android") + id("kotlin-android") + id("kotlin-kapt") +} + +android { + compileSdk = Project.compileSdkVersion + + defaultConfig { + minSdk = Project.minSdk + targetSdk = Project.targetSdk + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = true + + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = "1.8" + } + + packagingOptions { + //Exclude this files so Jetpack Compose UI tests can build + resources.excludes.add("META-INF/AL2.0") + resources.excludes.add("META-INF/LGPL2.1") + //------------------------------------------------------- + } +} + +dependencies { + ivyFPModuleDependencies() +} \ No newline at end of file diff --git a/ivy-fp/consumer-rules.pro b/ivy-fp/consumer-rules.pro new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ivy-fp/proguard-rules.pro b/ivy-fp/proguard-rules.pro new file mode 100644 index 0000000000..481bb43481 --- /dev/null +++ b/ivy-fp/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/ivy-fp/src/androidTest/java/com/ivy/design/ExampleInstrumentedTest.kt b/ivy-fp/src/androidTest/java/com/ivy/design/ExampleInstrumentedTest.kt new file mode 100644 index 0000000000..272e42be83 --- /dev/null +++ b/ivy-fp/src/androidTest/java/com/ivy/design/ExampleInstrumentedTest.kt @@ -0,0 +1,22 @@ +package com.ivy.design + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.ivy.fp.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/ivy-fp/src/main/AndroidManifest.xml b/ivy-fp/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..6333616ab1 --- /dev/null +++ b/ivy-fp/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/ivy-fp/src/main/java/com/ivy/fp/Composition.kt b/ivy-fp/src/main/java/com/ivy/fp/Composition.kt new file mode 100644 index 0000000000..5e57f85277 --- /dev/null +++ b/ivy-fp/src/main/java/com/ivy/fp/Composition.kt @@ -0,0 +1,60 @@ +package com.ivy.fp + +import com.ivy.fp.action.Action + +@Target(AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.SOURCE) +@MustBeDocumented +annotation class Pure + +@Target(AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.SOURCE) +@MustBeDocumented +annotation class Total(val sideEffect: String = "") + +@Target(AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.SOURCE) +@MustBeDocumented +annotation class Partial(val inCaseOf: String = "") + + +@Target(AnnotationTarget.VALUE_PARAMETER) +@Retention(AnnotationRetention.SOURCE) +@MustBeDocumented +annotation class SideEffect + +infix fun (suspend (A) -> B).then(f: suspend (B) -> C): suspend (A) -> C = { a -> + val b = this(a) + f(b) +} + +infix fun ((A) -> B).then(f: (B) -> C): (A) -> C = { a -> + val b = this(a) + f(b) +} + +infix fun ((B) -> C).after(fn1: (A) -> B): (A) -> C = { a -> + val b = fn1(a) + this(b) +} + +infix fun ((A, B) -> C).then(f: (C) -> D): (A, B) -> D = { a, b -> + val c = this(a, b) + f(c) +} + +infix fun ((A, B) -> C).then(act: Action): suspend (A, B) -> D = { a, b -> + val c = this(a, b) + act(c) +} + +suspend infix fun (suspend (A, B) -> C).then(f: suspend (C) -> D): suspend (A, B) -> D = + { a, b -> + val c = this(a, b) + f(c) + } + +infix fun ((A, B, C) -> D).then(f: (D) -> E): (A, B, C) -> E = { a, b, c -> + val d = this(a, b, c) + f(d) +} \ No newline at end of file diff --git a/ivy-fp/src/main/java/com/ivy/fp/Utils.kt b/ivy-fp/src/main/java/com/ivy/fp/Utils.kt new file mode 100644 index 0000000000..b6fbef2c2e --- /dev/null +++ b/ivy-fp/src/main/java/com/ivy/fp/Utils.kt @@ -0,0 +1,19 @@ +package com.ivy.fp + +suspend fun List.sumOfSuspend( + selector: suspend (A) -> Double +): Double { + var sum = 0.0 + for (item in this) { + sum += selector(item) + } + return sum +} + +suspend fun Collection.filterSuspend( + predicate: suspend (A) -> Boolean +): Collection { + return this.filter { a -> + predicate(a) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ivy/wallet/domain/action/Action.kt b/ivy-fp/src/main/java/com/ivy/fp/action/Action.kt similarity index 51% rename from app/src/main/java/com/ivy/wallet/domain/action/Action.kt rename to ivy-fp/src/main/java/com/ivy/fp/action/Action.kt index 2b2b5abc78..d3f7a0a6d7 100644 --- a/app/src/main/java/com/ivy/wallet/domain/action/Action.kt +++ b/ivy-fp/src/main/java/com/ivy/fp/action/Action.kt @@ -1,6 +1,8 @@ -package com.ivy.wallet.domain.action +package com.ivy.fp.action +import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async import kotlinx.coroutines.withContext abstract class Action { @@ -14,25 +16,18 @@ abstract class Action { return@withContext action() } + protected suspend fun asyncIo(action: suspend () -> T): Deferred = + withContext(Dispatchers.IO) { + return@withContext this.async { action() } + } + protected suspend fun computation(action: suspend () -> T): T = - withContext(Dispatchers.Main) { + withContext(Dispatchers.Default) { return@withContext action() } -} - -infix fun Action.after(act1: Action): Action = object : Action() { - override suspend fun A.willDo(): C { - val b = act1(this@willDo) //A -> B - return this@after(b) //B -> C - } -} -///** -// * Action composition example -// */ -//suspend fun example( -// calcWalletBalance: CalcWalletBalanceAct, -// getBaseCurrency: GetBaseCurrencyAct -//): BigDecimal { -// return (calcWalletBalance after getBaseCurrency)(Unit) -//} \ No newline at end of file + protected suspend fun ui(action: suspend () -> T): T = + withContext(Dispatchers.Main) { + return@withContext action() + } +} \ No newline at end of file diff --git a/ivy-fp/src/main/java/com/ivy/fp/action/Composition.kt b/ivy-fp/src/main/java/com/ivy/fp/action/Composition.kt new file mode 100644 index 0000000000..2e33c0b663 --- /dev/null +++ b/ivy-fp/src/main/java/com/ivy/fp/action/Composition.kt @@ -0,0 +1,58 @@ +package com.ivy.fp.action + + +suspend infix fun (suspend (A) -> B).then(f: suspend (B) -> C): suspend (A) -> C = + { a -> + val b = this@then(a) + f(b) + } + +suspend infix fun (suspend () -> B).then(f: suspend (B) -> C): suspend () -> C = + { + val b = this@then() + f(b) + } + +suspend infix fun (suspend (A) -> B).then(act: Action): suspend (A) -> C = + { a -> + val b = this@then(a) + act(b) + } + +suspend infix fun (suspend () -> B).then(act: Action): suspend () -> C = + { + val b = this@then() + act(b) + } + +suspend infix fun (Action).then(f: suspend (B) -> C): suspend (A) -> C = + { a -> + val b = this@then(a) + f(b) + } + + +suspend infix fun (() -> B).then(f: suspend (B) -> C): suspend () -> C = + { + val b = this@then() + f(b) + } + +fun (() -> C).fixUnit(): suspend (Unit) -> C = + { + this() + } + +fun (suspend () -> C).fixUnit(): suspend (Unit) -> C = + { + this() + } + +fun (suspend (Unit) -> C).fixUnit(): suspend () -> C = + { + this(Unit) + } + +fun (Action).lambda(): suspend (A) -> B = { a -> + this(a) +} \ No newline at end of file diff --git a/ivy-fp/src/main/java/com/ivy/fp/action/CompositionFilter.kt b/ivy-fp/src/main/java/com/ivy/fp/action/CompositionFilter.kt new file mode 100644 index 0000000000..2bc93750e7 --- /dev/null +++ b/ivy-fp/src/main/java/com/ivy/fp/action/CompositionFilter.kt @@ -0,0 +1,28 @@ +package com.ivy.fp.action + +suspend infix fun (suspend (A) -> List).thenFilter( + predicate: (B) -> Boolean +): suspend (A) -> List = + { a -> + val list = this(a) + list.filter(predicate) + } + +suspend infix fun (Action>).thenFilter( + predicate: (B) -> Boolean +): suspend (A) -> List = + { a -> + val list = this(a) + list.filter(predicate) + } + + +suspend infix fun (suspend () -> List).thenFilter( + predicate: suspend (B) -> Boolean +): suspend () -> List = + { + val list = this() + list.filter { + predicate(it) + } + } diff --git a/ivy-fp/src/main/java/com/ivy/fp/action/CompositionMap.kt b/ivy-fp/src/main/java/com/ivy/fp/action/CompositionMap.kt new file mode 100644 index 0000000000..ab160bf533 --- /dev/null +++ b/ivy-fp/src/main/java/com/ivy/fp/action/CompositionMap.kt @@ -0,0 +1,51 @@ +package com.ivy.fp.action + +suspend infix fun (suspend (A) -> List).thenMap( + transform: suspend (B) -> C +): suspend (A) -> List = + { a -> + val list = this(a) + list.map { + transform(it) + } + } + +suspend infix fun (suspend () -> List).thenMap( + transform: suspend (B) -> C +): suspend () -> List = + { + val list = this() + list.map { + transform(it) + } + } + +suspend infix fun (suspend () -> List).thenFlatMap( + transform: suspend (B) -> List +): suspend () -> List = + { + val list = this() + list.flatMap { + transform(it) + } + } + +suspend infix fun (suspend () -> List).thenMap( + act: Action +): suspend () -> List = + { + val list = this() + list.map { + act(it) + } + } + +suspend infix fun (Action>).thenMap( + transform: suspend (B) -> C +): suspend (A) -> List = + { a -> + val list = this(a) + list.map { + transform(it) + } + } diff --git a/ivy-fp/src/main/java/com/ivy/fp/action/CompositionSum.kt b/ivy-fp/src/main/java/com/ivy/fp/action/CompositionSum.kt new file mode 100644 index 0000000000..72b42f7f47 --- /dev/null +++ b/ivy-fp/src/main/java/com/ivy/fp/action/CompositionSum.kt @@ -0,0 +1,29 @@ +package com.ivy.fp.action + +import java.math.BigDecimal + +suspend infix fun (suspend (A) -> List).thenSum( + value: (B) -> BigDecimal +): suspend (A) -> BigDecimal = + { a -> + val list = this(a) + list.fold( + initial = BigDecimal.ZERO, + operation = { acc, b -> + acc + value(b) + } + ) + } + +suspend infix fun (Action>).thenSum( + value: (B) -> BigDecimal +): suspend (A) -> BigDecimal = + { a -> + val list = this(a) + list.fold( + initial = BigDecimal.ZERO, + operation = { acc, b -> + acc + value(b) + } + ) + } \ No newline at end of file diff --git a/ivy-fp/src/main/java/com/ivy/fp/action/FPAction.kt b/ivy-fp/src/main/java/com/ivy/fp/action/FPAction.kt new file mode 100644 index 0000000000..0283a4350d --- /dev/null +++ b/ivy-fp/src/main/java/com/ivy/fp/action/FPAction.kt @@ -0,0 +1,17 @@ +package com.ivy.fp.action + +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +abstract class FPAction : Action() { + protected abstract suspend fun I.compose(): (suspend () -> O) + + protected open fun dispatcher(): CoroutineDispatcher = Dispatchers.IO + + override suspend fun I.willDo(): O { + return withContext(dispatcher()) { + compose().invoke() + } + } +} \ No newline at end of file diff --git a/ivy-design/src/main/java/com/ivy/design/viewmodel/IvyViewModel.kt b/ivy-fp/src/main/java/com/ivy/fp/viewmodel/IvyViewModel.kt similarity index 94% rename from ivy-design/src/main/java/com/ivy/design/viewmodel/IvyViewModel.kt rename to ivy-fp/src/main/java/com/ivy/fp/viewmodel/IvyViewModel.kt index 0773e2c062..9562f5fb97 100644 --- a/ivy-design/src/main/java/com/ivy/design/viewmodel/IvyViewModel.kt +++ b/ivy-fp/src/main/java/com/ivy/fp/viewmodel/IvyViewModel.kt @@ -1,4 +1,4 @@ -package com.ivy.design.viewmodel +package com.ivy.fp.viewmodel import androidx.lifecycle.ViewModel import kotlinx.coroutines.flow.MutableStateFlow diff --git a/ivy-design/src/main/java/com/ivy/design/viewmodel/ViewmodelUtils.kt b/ivy-fp/src/main/java/com/ivy/fp/viewmodel/ViewmodelUtils.kt similarity index 83% rename from ivy-design/src/main/java/com/ivy/design/viewmodel/ViewmodelUtils.kt rename to ivy-fp/src/main/java/com/ivy/fp/viewmodel/ViewmodelUtils.kt index 28f36a8122..4d93e3baa9 100644 --- a/ivy-design/src/main/java/com/ivy/design/viewmodel/ViewmodelUtils.kt +++ b/ivy-fp/src/main/java/com/ivy/fp/viewmodel/ViewmodelUtils.kt @@ -1,4 +1,4 @@ -package com.ivy.design.viewmodel +package com.ivy.fp.viewmodel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow diff --git a/ivy-fp/src/test/java/com/ivy/design/ExampleUnitTest.kt b/ivy-fp/src/test/java/com/ivy/design/ExampleUnitTest.kt new file mode 100644 index 0000000000..08ade6083f --- /dev/null +++ b/ivy-fp/src/test/java/com/ivy/design/ExampleUnitTest.kt @@ -0,0 +1,16 @@ +package com.ivy.design + +import org.junit.Assert.assertEquals +import org.junit.Test + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 707897c694..00c27d4b09 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,8 +1,5 @@ import java.net.URI -include(":ivy-design") - - dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { @@ -15,4 +12,7 @@ dependencyResolutionManagement { } rootProject.name = "Ivy Wallet" include(":app") +include(":ivy-design") +include(":ivy-fp") + \ No newline at end of file