├── .buckconfig
├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .gitattributes
├── .gitignore
├── .husky
└── pre-commit
├── .prettierignore
├── .prettierrc.js
├── .vscode
└── settings.json
├── .watchmanconfig
├── LICENSE
├── README.md
├── __mocks__
├── @react-native-async-storage
│ └── async-storage
│ │ └── index.js
├── database.ts
├── entries.json
├── react-i18next.js
└── react-native-fs.js
├── android
├── app
│ ├── _BUCK
│ ├── build.gradle
│ ├── build_defs.bzl
│ ├── debug.keystore
│ ├── proguard-rules.pro
│ └── src
│ │ ├── debug
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── logfinance
│ │ │ └── ReactNativeFlipper.java
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ ├── assets
│ │ └── fonts
│ │ │ ├── AntDesign.ttf
│ │ │ ├── Entypo.ttf
│ │ │ ├── EvilIcons.ttf
│ │ │ ├── Feather.ttf
│ │ │ ├── FontAwesome.ttf
│ │ │ ├── FontAwesome5_Brands.ttf
│ │ │ ├── FontAwesome5_Regular.ttf
│ │ │ ├── FontAwesome5_Solid.ttf
│ │ │ ├── Fontisto.ttf
│ │ │ ├── Foundation.ttf
│ │ │ ├── Ionicons.ttf
│ │ │ ├── MaterialCommunityIcons.ttf
│ │ │ ├── MaterialIcons.ttf
│ │ │ ├── Octicons.ttf
│ │ │ ├── Poppins-Black.ttf
│ │ │ ├── Poppins-BlackItalic.ttf
│ │ │ ├── Poppins-Bold.ttf
│ │ │ ├── Poppins-BoldItalic.ttf
│ │ │ ├── Poppins-ExtraBold.ttf
│ │ │ ├── Poppins-ExtraBoldItalic.ttf
│ │ │ ├── Poppins-ExtraLight.ttf
│ │ │ ├── Poppins-ExtraLightItalic.ttf
│ │ │ ├── Poppins-Italic.ttf
│ │ │ ├── Poppins-Light.ttf
│ │ │ ├── Poppins-LightItalic.ttf
│ │ │ ├── Poppins-Medium.ttf
│ │ │ ├── Poppins-MediumItalic.ttf
│ │ │ ├── Poppins-Regular.ttf
│ │ │ ├── Poppins-SemiBold.ttf
│ │ │ ├── Poppins-SemiBoldItalic.ttf
│ │ │ ├── Poppins-Thin.ttf
│ │ │ ├── Poppins-ThinItalic.ttf
│ │ │ ├── SimpleLineIcons.ttf
│ │ │ └── Zocial.ttf
│ │ ├── java
│ │ └── com
│ │ │ └── logfinance
│ │ │ ├── MainActivity.java
│ │ │ └── MainApplication.java
│ │ └── res
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_round.png
│ │ ├── ic_launcher_round_adaptive_back.png
│ │ └── ic_launcher_round_adaptive_fore.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_round.png
│ │ ├── ic_launcher_round_adaptive_back.png
│ │ └── ic_launcher_round_adaptive_fore.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_round.png
│ │ ├── ic_launcher_round_adaptive_back.png
│ │ └── ic_launcher_round_adaptive_fore.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_round.png
│ │ ├── ic_launcher_round_adaptive_back.png
│ │ └── ic_launcher_round_adaptive_fore.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_round.png
│ │ ├── ic_launcher_round_adaptive_back.png
│ │ └── ic_launcher_round_adaptive_fore.png
│ │ └── values
│ │ ├── strings.xml
│ │ └── styles.xml
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
├── app.json
├── babel.config.js
├── index.js
├── ios
├── LogFinance.xcodeproj
│ ├── project.pbxproj
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── LogFinance.xcscheme
├── LogFinance.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── LogFinance
│ ├── AppDelegate.h
│ ├── AppDelegate.m
│ ├── Images.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ ├── 100.png
│ │ │ ├── 1024.png
│ │ │ ├── 114.png
│ │ │ ├── 120.png
│ │ │ ├── 128.png
│ │ │ ├── 144.png
│ │ │ ├── 152.png
│ │ │ ├── 16.png
│ │ │ ├── 167.png
│ │ │ ├── 172.png
│ │ │ ├── 180.png
│ │ │ ├── 196.png
│ │ │ ├── 20.png
│ │ │ ├── 216.png
│ │ │ ├── 256.png
│ │ │ ├── 29.png
│ │ │ ├── 32.png
│ │ │ ├── 40.png
│ │ │ ├── 48.png
│ │ │ ├── 50.png
│ │ │ ├── 512.png
│ │ │ ├── 55.png
│ │ │ ├── 57.png
│ │ │ ├── 58.png
│ │ │ ├── 60.png
│ │ │ ├── 64.png
│ │ │ ├── 72.png
│ │ │ ├── 76.png
│ │ │ ├── 80.png
│ │ │ ├── 87.png
│ │ │ ├── 88.png
│ │ │ └── Contents.json
│ │ └── Contents.json
│ ├── Info.plist
│ ├── LaunchScreen.storyboard
│ └── main.m
├── LogFinanceTests
│ ├── Info.plist
│ └── LogFinanceTests.m
├── Podfile
└── Podfile.lock
├── jest.config.js
├── jest.setup.js
├── metro.config.js
├── package-lock.json
├── package.json
├── react-native.config.js
├── src
├── App.tsx
├── __tests__
│ ├── App.test.tsx
│ └── __snapshots__
│ │ └── App.test.tsx.snap
├── assets
│ └── fonts
│ │ └── poppins
│ │ ├── OFL.txt
│ │ ├── Poppins-Black.ttf
│ │ ├── Poppins-BlackItalic.ttf
│ │ ├── Poppins-Bold.ttf
│ │ ├── Poppins-BoldItalic.ttf
│ │ ├── Poppins-ExtraBold.ttf
│ │ ├── Poppins-ExtraBoldItalic.ttf
│ │ ├── Poppins-ExtraLight.ttf
│ │ ├── Poppins-ExtraLightItalic.ttf
│ │ ├── Poppins-Italic.ttf
│ │ ├── Poppins-Light.ttf
│ │ ├── Poppins-LightItalic.ttf
│ │ ├── Poppins-Medium.ttf
│ │ ├── Poppins-MediumItalic.ttf
│ │ ├── Poppins-Regular.ttf
│ │ ├── Poppins-SemiBold.ttf
│ │ ├── Poppins-SemiBoldItalic.ttf
│ │ ├── Poppins-Thin.ttf
│ │ └── Poppins-ThinItalic.ttf
├── components
│ ├── Button
│ │ ├── Button.tsx
│ │ ├── index.ts
│ │ └── styles.ts
│ ├── CategoryIcon
│ │ ├── CategoryIcon.tsx
│ │ ├── __tests__
│ │ │ ├── CategoryIcon.test.tsx
│ │ │ └── __snapshots__
│ │ │ │ └── CategoryIcon.test.tsx.snap
│ │ ├── icons.ts
│ │ ├── index.ts
│ │ └── styles.ts
│ ├── CategoryPick
│ │ ├── CategoryPick.tsx
│ │ ├── __tests__
│ │ │ └── CategoryPick.test.tsx
│ │ ├── index.ts
│ │ └── styles.ts
│ ├── Currency
│ │ ├── Currency.tsx
│ │ ├── __tests__
│ │ │ ├── Currency.test.tsx
│ │ │ └── __snapshots__
│ │ │ │ └── Currency.test.tsx.snap
│ │ └── index.ts
│ ├── Dismiss
│ │ ├── Dismiss.tsx
│ │ ├── __tests__
│ │ │ ├── Dismiss.test.tsx
│ │ │ └── __snapshots__
│ │ │ │ └── Dismiss.test.tsx.snap
│ │ ├── index.ts
│ │ └── styles.ts
│ ├── EntriesList
│ │ ├── EntriesList.tsx
│ │ ├── __tests__
│ │ │ ├── EntriesList.test.tsx
│ │ │ └── __snapshots__
│ │ │ │ └── EntriesList.test.tsx.snap
│ │ ├── index.ts
│ │ └── styles.ts
│ ├── EntryItem
│ │ ├── EntryItem.tsx
│ │ ├── __tests__
│ │ │ ├── EntryItem.test.tsx
│ │ │ └── __snapshots__
│ │ │ │ └── EntryItem.test.tsx.snap
│ │ ├── index.ts
│ │ └── styles.ts
│ ├── InputNumber
│ │ ├── InputNumber.tsx
│ │ ├── __tests__
│ │ │ ├── InputNumber.test.tsx
│ │ │ └── __snapshots__
│ │ │ │ └── InputNumber.test.tsx.snap
│ │ ├── index.ts
│ │ └── styles.ts
│ ├── List
│ │ ├── Item.tsx
│ │ ├── List.tsx
│ │ ├── index.ts
│ │ └── styles.ts
│ ├── NumberKeyboard
│ │ ├── NumberKeyboard.tsx
│ │ ├── __tests__
│ │ │ ├── NumberKeyboard.test.tsx
│ │ │ └── __snapshots__
│ │ │ │ └── NumberKeyboard.test.tsx.snap
│ │ ├── index.ts
│ │ └── styles.ts
│ ├── ProgressBar
│ │ ├── ProgressBar.tsx
│ │ ├── __tests__
│ │ │ ├── ProgressBar.test.tsx
│ │ │ └── __snapshots__
│ │ │ │ └── ProgressBar.test.tsx.snap
│ │ ├── index.ts
│ │ └── styles.ts
│ ├── Row
│ │ ├── Row.tsx
│ │ ├── index.ts
│ │ └── styles.ts
│ ├── Strap
│ │ ├── Strap.tsx
│ │ ├── __tests__
│ │ │ ├── Strap.test.tsx
│ │ │ └── __snapshots__
│ │ │ │ └── Strap.test.tsx.snap
│ │ ├── index.ts
│ │ └── styles.ts
│ ├── SwipeRemoveButton
│ │ ├── SwipeRemoveButton.tsx
│ │ ├── __tests__
│ │ │ ├── SwipeRemoveButton.test.tsx
│ │ │ └── __snapshots__
│ │ │ │ └── SwipeRemoveButton.test.tsx.snap
│ │ ├── index.ts
│ │ └── styles.ts
│ ├── Toolbar
│ │ ├── Toolbar.tsx
│ │ ├── index.ts
│ │ └── styles.ts
│ └── index.ts
├── contexts
│ ├── BudgetContext.tsx
│ └── index.ts
├── database
│ ├── categories.json
│ ├── collections.ts
│ ├── index.ts
│ ├── migrations.ts
│ ├── modelClasses.ts
│ ├── schema.ts
│ └── seeds.ts
├── hooks
│ ├── __tests__
│ │ ├── __snapshots__
│ │ │ └── useEntry.test.tsx.snap
│ │ └── useEntry.test.tsx
│ ├── index.ts
│ ├── useCategory.tsx
│ ├── useDialog.ts
│ └── useEntry.tsx
├── interfaces
│ ├── IBudget.ts
│ ├── ICategory.ts
│ ├── IEntry.ts
│ └── index.ts
├── locales
│ ├── index.ts
│ └── translations
│ │ ├── en_US.json
│ │ └── pt_BR.json
├── models
│ ├── Budget.ts
│ ├── Category.ts
│ ├── Entry.ts
│ └── index.ts
├── repositories
│ ├── BudgetRepository.ts
│ ├── CategoryRepository.ts
│ ├── EntryRepository.ts
│ └── index.ts
├── routes
│ ├── StacksRoute.tsx
│ ├── TabsRoute.tsx
│ ├── components
│ │ ├── BottomTabs
│ │ │ ├── BottomTabs.tsx
│ │ │ ├── index.ts
│ │ │ └── styles.ts
│ │ ├── TabIcon
│ │ │ ├── TabIcon.tsx
│ │ │ ├── __tests__
│ │ │ │ ├── TabIcon.test.tsx
│ │ │ │ └── __snapshots__
│ │ │ │ │ └── TabIcon.test.tsx.snap
│ │ │ ├── index.ts
│ │ │ └── styles.ts
│ │ └── index.ts
│ └── index.tsx
├── screens
│ ├── AddBudget
│ │ ├── AddBudgetScreen.tsx
│ │ ├── index.ts
│ │ └── styles.ts
│ ├── AddEntry
│ │ ├── AddEntryScreen.tsx
│ │ ├── __tests__
│ │ │ ├── AddEntryScreen.test.tsx
│ │ │ └── __snapshots__
│ │ │ │ └── AddEntryScreen.test.tsx.snap
│ │ ├── components
│ │ │ ├── EntryCategory
│ │ │ │ ├── EntryCategory.tsx
│ │ │ │ ├── __tests__
│ │ │ │ │ ├── EntryCategory.test.tsx
│ │ │ │ │ └── __snapshots__
│ │ │ │ │ │ └── EntryCategory.test.tsx.snap
│ │ │ │ ├── index.ts
│ │ │ │ └── styles.ts
│ │ │ ├── EntryDate
│ │ │ │ ├── EntryDate.tsx
│ │ │ │ ├── __tests__
│ │ │ │ │ ├── EntryDate.test.tsx
│ │ │ │ │ └── __snapshots__
│ │ │ │ │ │ └── EntryDate.test.tsx.snap
│ │ │ │ ├── index.ts
│ │ │ │ └── styles.ts
│ │ │ ├── EntryDescription
│ │ │ │ ├── EntryDescription.tsx
│ │ │ │ ├── __tests__
│ │ │ │ │ ├── EntryDescription.test.tsx
│ │ │ │ │ └── __snapshots__
│ │ │ │ │ │ └── EntryDescription.test.tsx.snap
│ │ │ │ ├── index.ts
│ │ │ │ └── styles.ts
│ │ │ ├── MenuEntryType
│ │ │ │ ├── MenuEntryType.tsx
│ │ │ │ ├── __tests__
│ │ │ │ │ ├── MenuEntryType.test.tsx
│ │ │ │ │ └── __snapshots__
│ │ │ │ │ │ └── MenuEntryType.test.tsx.snap
│ │ │ │ ├── index.ts
│ │ │ │ └── styles.ts
│ │ │ └── index.ts
│ │ ├── index.ts
│ │ └── styles.ts
│ ├── Budget
│ │ ├── BudgetScreen.tsx
│ │ ├── components
│ │ │ ├── BudgetList
│ │ │ │ ├── BudgetItem.tsx
│ │ │ │ ├── BudgetList.tsx
│ │ │ │ ├── index.ts
│ │ │ │ └── styles.ts
│ │ │ └── ListEmpty
│ │ │ │ ├── LICENSE.md
│ │ │ │ ├── ListEmpty.tsx
│ │ │ │ ├── __tests__
│ │ │ │ ├── ListEmpty.test.tsx
│ │ │ │ └── __snapshots__
│ │ │ │ │ └── ListEmpty.test.tsx.snap
│ │ │ │ ├── empty.json
│ │ │ │ ├── index.ts
│ │ │ │ └── styles.ts
│ │ ├── index.tsx
│ │ └── styles.ts
│ ├── Categories
│ │ ├── CategoriesScreen.tsx
│ │ ├── __tests__
│ │ │ ├── CategoriesScreen.test.tsx
│ │ │ └── __snapshots__
│ │ │ │ └── CategoriesScreen.test.tsx.snap
│ │ ├── components
│ │ │ ├── CategoryItem
│ │ │ │ ├── CategoryItem.tsx
│ │ │ │ ├── __tests__
│ │ │ │ │ ├── CategoryItem.test.tsx
│ │ │ │ │ └── __snapshots__
│ │ │ │ │ │ └── CategoryItem.test.tsx.snap
│ │ │ │ ├── index.ts
│ │ │ │ └── styles.ts
│ │ │ ├── CategoryList
│ │ │ │ ├── CategoryList.tsx
│ │ │ │ ├── __tests__
│ │ │ │ │ ├── CategoryList.test.tsx
│ │ │ │ │ └── __snapshots__
│ │ │ │ │ │ └── CategoryList.test.tsx.snap
│ │ │ │ ├── index.ts
│ │ │ │ └── styles.ts
│ │ │ └── index.ts
│ │ ├── index.ts
│ │ └── styles.ts
│ ├── Currency
│ │ ├── CurrencyScreen.tsx
│ │ ├── currencies.ts
│ │ ├── index.tsx
│ │ └── styles.ts
│ ├── Historic
│ │ ├── HistoricScreen.tsx
│ │ ├── components
│ │ │ ├── HistoricList
│ │ │ │ ├── HistoricHeader.tsx
│ │ │ │ ├── HistoricItem.tsx
│ │ │ │ ├── HistoricList.tsx
│ │ │ │ ├── index.ts
│ │ │ │ └── styles.ts
│ │ │ └── ListEmpty
│ │ │ │ ├── LICENSE.md
│ │ │ │ ├── ListEmpty.tsx
│ │ │ │ ├── empty.json
│ │ │ │ ├── index.ts
│ │ │ │ └── styles.ts
│ │ ├── index.tsx
│ │ └── styles.ts
│ ├── Home
│ │ ├── HomeScreen.tsx
│ │ ├── __tests__
│ │ │ ├── HomeScreen.test.tsx
│ │ │ └── __snapshots__
│ │ │ │ └── HomeScreen.test.tsx.snap
│ │ ├── components
│ │ │ ├── Header
│ │ │ │ ├── Header.tsx
│ │ │ │ ├── __tests__
│ │ │ │ │ ├── Header.test.tsx
│ │ │ │ │ └── __snapshots__
│ │ │ │ │ │ └── Header.test.tsx.snap
│ │ │ │ ├── index.ts
│ │ │ │ └── styles.ts
│ │ │ ├── ListEmpty
│ │ │ │ ├── LICENSE.md
│ │ │ │ ├── ListEmpty.tsx
│ │ │ │ ├── __tests__
│ │ │ │ │ ├── ListEmpty.test.tsx
│ │ │ │ │ └── __snapshots__
│ │ │ │ │ │ └── ListEmpty.test.tsx.snap
│ │ │ │ ├── empty.json
│ │ │ │ ├── index.ts
│ │ │ │ └── styles.ts
│ │ │ └── index.ts
│ │ ├── index.ts
│ │ └── styles.ts
│ ├── Language
│ │ ├── LanguageScreen.tsx
│ │ ├── index.tsx
│ │ └── styles.ts
│ ├── Reports
│ │ ├── ReportsScreen.tsx
│ │ ├── __tests__
│ │ │ ├── ReportsScreen.test.tsx
│ │ │ └── __snapshots__
│ │ │ │ └── ReportsScreen.test.tsx.snap
│ │ ├── components
│ │ │ ├── FilterPeriod
│ │ │ │ ├── FilterPeriod.tsx
│ │ │ │ ├── Item.tsx
│ │ │ │ ├── __tests__
│ │ │ │ │ ├── FilterPeriod.test.tsx
│ │ │ │ │ └── __snapshots__
│ │ │ │ │ │ └── FilterPeriod.test.tsx.snap
│ │ │ │ ├── data.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── styles.ts
│ │ │ ├── LegendList
│ │ │ │ ├── LegendItem.tsx
│ │ │ │ ├── LegendList.tsx
│ │ │ │ ├── __tests__
│ │ │ │ │ ├── LegendItem.test.tsx
│ │ │ │ │ ├── LegendList.test.tsx
│ │ │ │ │ └── __snapshots__
│ │ │ │ │ │ ├── LegendItem.test.tsx.snap
│ │ │ │ │ │ └── LegendList.test.tsx.snap
│ │ │ │ ├── index.ts
│ │ │ │ └── styles.ts
│ │ │ └── ListEmpty
│ │ │ │ ├── LICENSE.md
│ │ │ │ ├── ListEmpty.tsx
│ │ │ │ ├── __tests__
│ │ │ │ ├── ListEmpty.test.tsx
│ │ │ │ └── __snapshots__
│ │ │ │ │ └── ListEmpty.test.tsx.snap
│ │ │ │ ├── index.ts
│ │ │ │ ├── reports.json
│ │ │ │ └── styles.ts
│ │ ├── index.ts
│ │ └── styles.ts
│ └── Settings
│ │ ├── SettingsScreen.tsx
│ │ ├── index.ts
│ │ └── styles.ts
├── services
│ ├── currency.ts
│ ├── database.ts
│ ├── index.ts
│ ├── language.ts
│ ├── reports.ts
│ └── storage.ts
├── styles
│ ├── colors.ts
│ ├── index.ts
│ ├── layout.ts
│ ├── mixins.ts
│ ├── shadow.ts
│ ├── spacing.ts
│ ├── styled.d.ts
│ ├── theme.tsx
│ └── typography.ts
└── utils
│ ├── dates.ts
│ ├── index.ts
│ ├── strings.ts
│ └── test-utils.tsx
├── tsconfig.json
└── yarn.lock
/.buckconfig:
--------------------------------------------------------------------------------
1 |
2 | [android]
3 | target = Google Inc.:Google APIs:23
4 |
5 | [maven_repositories]
6 | central = https://repo1.maven.org/maven2
7 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Windows files
2 | [*.bat]
3 | end_of_line = crlf
4 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | es2021: true,
4 | node: true,
5 | jest: true,
6 | },
7 | extends: [
8 | 'eslint:recommended',
9 | 'plugin:react/recommended',
10 | 'plugin:react-hooks/recommended',
11 | 'plugin:@typescript-eslint/eslint-recommended',
12 | 'plugin:@typescript-eslint/recommended',
13 | ],
14 | parser: '@typescript-eslint/parser',
15 | parserOptions: {
16 | project: './tsconfig.json',
17 | ecmaFeatures: {
18 | jsx: true,
19 | },
20 | ecmaVersion: 12,
21 | sourceType: 'module',
22 | },
23 | plugins: ['react', 'react-hooks', '@typescript-eslint', 'prettier'],
24 |
25 | rules: {
26 | 'no-tabs': ['error', {allowIndentationTabs: true}],
27 | quotes: ['error', 'single', {avoidEscape: true}],
28 | semi: 'off',
29 | 'no-empty-function': 'off',
30 | '@typescript-eslint/no-empty-function': 'off',
31 | 'react/display-name': 'off',
32 | 'react/prop-types': 'off',
33 | 'prettier/prettier': 'error',
34 | '@typescript-eslint/unbound-method': 'error',
35 | '@typescript-eslint/semi': ['error'],
36 | },
37 | overrides: [
38 | {
39 | files: ['*.jsx', '*.tsx'],
40 | rules: {
41 | indent: ['error', 2, {SwitchCase: 1}],
42 | '@typescript-eslint/explicit-module-boundary-types': ['off'],
43 | },
44 | },
45 | ],
46 | settings: {
47 | react: {
48 | version: 'detect',
49 | },
50 | },
51 | };
52 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Windows files should use crlf line endings
2 | # https://help.github.com/articles/dealing-with-line-endings/
3 | *.bat text eol=crlf
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Xcode
6 | #
7 | build/
8 | *.pbxuser
9 | !default.pbxuser
10 | *.mode1v3
11 | !default.mode1v3
12 | *.mode2v3
13 | !default.mode2v3
14 | *.perspectivev3
15 | !default.perspectivev3
16 | xcuserdata
17 | *.xccheckout
18 | *.moved-aside
19 | DerivedData
20 | *.hmap
21 | *.ipa
22 | *.xcuserstate
23 |
24 | # Android/IntelliJ
25 | #
26 | build/
27 | .idea
28 | .gradle
29 | local.properties
30 | *.iml
31 | *.hprof
32 |
33 | # node.js
34 | #
35 | node_modules/
36 | npm-debug.log
37 | yarn-error.log
38 |
39 | # BUCK
40 | buck-out/
41 | \.buckd/
42 | *.keystore
43 | !debug.keystore
44 |
45 | # fastlane
46 | #
47 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
48 | # screenshots whenever they are needed.
49 | # For more information about the recommended setup visit:
50 | # https://docs.fastlane.tools/best-practices/source-control/
51 |
52 | */fastlane/report.xml
53 | */fastlane/Preview.html
54 | */fastlane/screenshots
55 |
56 | # Bundle artifact
57 | *.jsbundle
58 |
59 | # CocoaPods
60 | /ios/Pods/
61 |
62 | android/gradle.properties
63 |
64 | android/app/release/app-release.apk
65 |
66 | android/app/release/output-metadata.json
67 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | yarn lint-staged
5 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | ios
3 | android
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | bracketSpacing: false,
3 | singleQuote: true,
4 | trailingComma: 'all',
5 | arrowParens: 'avoid',
6 | };
7 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.codeActionsOnSave": {
3 | "source.fixAll": true,
4 | "source.fixAll.eslint": true,
5 | "source.organizeImports": false
6 | },
7 | "editor.formatOnSave": false,
8 | "editor.folding": false
9 | }
10 |
--------------------------------------------------------------------------------
/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Felipe Rosas
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/__mocks__/@react-native-async-storage/async-storage/index.js:
--------------------------------------------------------------------------------
1 | let cache = {};
2 | export default {
3 | setItem: (key, value) => {
4 | return new Promise((resolve, reject) => {
5 | return typeof key !== 'string' || typeof value !== 'string'
6 | ? reject(new Error('key and value must be string'))
7 | : resolve((cache[key] = value));
8 | });
9 | },
10 | getItem: (key, value) => {
11 | return new Promise(resolve => {
12 | return cache.hasOwnProperty(key) ? resolve(cache[key]) : resolve(null);
13 | });
14 | },
15 | removeItem: key => {
16 | return new Promise((resolve, reject) => {
17 | return cache.hasOwnProperty(key)
18 | ? resolve(delete cache[key])
19 | : reject('No such key!');
20 | });
21 | },
22 | clear: key => {
23 | return new Promise((resolve, reject) => resolve((cache = {})));
24 | },
25 |
26 | getAllKeys: key => {
27 | return new Promise((resolve, reject) => resolve(Object.keys(cache)));
28 | },
29 | };
30 |
--------------------------------------------------------------------------------
/__mocks__/database.ts:
--------------------------------------------------------------------------------
1 | import { Database } from '@nozbe/watermelondb';
2 | import LokiJSAdapter, { LokiAdapterOptions } from '@nozbe/watermelondb/adapters/lokijs'
3 |
4 | import { schema, migrations, modelClasses } from 'database';
5 |
6 | let database: Database;
7 |
8 | export function getDatabase(): Database {
9 | if (!database) {
10 | const adapterConfig: LokiAdapterOptions = {
11 | schema,
12 | migrations,
13 | useWebWorker: false,
14 | useIncrementalIndexedDB: true,
15 | extraLokiOptions: {
16 | autosave: false
17 | }
18 | };
19 |
20 | const adapter = new LokiJSAdapter(adapterConfig);
21 |
22 | database = new Database({
23 | adapter,
24 | modelClasses,
25 | });
26 | }
27 | return database;
28 | }
29 |
30 | export function runSeeds(): void {
31 | jest.fn();
32 | }
33 |
34 | export default getDatabase;
35 |
--------------------------------------------------------------------------------
/__mocks__/entries.json:
--------------------------------------------------------------------------------
1 | [{
2 | "id": 1,
3 | "description": "Salário",
4 | "type": "earning",
5 | "value": 10000,
6 | "category": {
7 | "id": 1,
8 | "description": "Salario",
9 | "key": "money"
10 | }
11 | }, {
12 | "id": 2,
13 | "description": "Pizza de carne seca",
14 | "type": "expense",
15 | "value": 49.90,
16 | "category": {
17 | "id": 1,
18 | "description": "Restaurante",
19 | "key": "food"
20 | }
21 | },
22 | {
23 | "id": 3,
24 | "description": "Gasolina",
25 | "type": "expense",
26 | "value": 100,
27 | "category": {
28 | "id": 1,
29 | "description": "Conveniência",
30 | "key": "others"
31 | }
32 |
33 | }
34 | ]
--------------------------------------------------------------------------------
/__mocks__/react-native-fs.js:
--------------------------------------------------------------------------------
1 | jest.mock('react-native-fs', () => {
2 | return {
3 | mkdir: jest.fn(),
4 | moveFile: jest.fn(),
5 | copyFile: jest.fn(),
6 | pathForBundle: jest.fn(),
7 | pathForGroup: jest.fn(),
8 | getFSInfo: jest.fn(),
9 | getAllExternalFilesDirs: jest.fn(),
10 | unlink: jest.fn(),
11 | exists: jest.fn(),
12 | stopDownload: jest.fn(),
13 | resumeDownload: jest.fn(),
14 | isResumable: jest.fn(),
15 | stopUpload: jest.fn(),
16 | completeHandlerIOS: jest.fn(),
17 | readDir: jest.fn(),
18 | readDirAssets: jest.fn(),
19 | existsAssets: jest.fn(),
20 | readdir: jest.fn(),
21 | setReadable: jest.fn(),
22 | stat: jest.fn(),
23 | readFile: jest.fn(),
24 | read: jest.fn(),
25 | readFileAssets: jest.fn(),
26 | hash: jest.fn(),
27 | copyFileAssets: jest.fn(),
28 | copyFileAssetsIOS: jest.fn(),
29 | copyAssetsVideoIOS: jest.fn(),
30 | writeFile: jest.fn(),
31 | appendFile: jest.fn(),
32 | write: jest.fn(),
33 | downloadFile: jest.fn(),
34 | uploadFiles: jest.fn(),
35 | touch: jest.fn(),
36 | MainBundlePath: jest.fn(),
37 | CachesDirectoryPath: jest.fn(),
38 | DocumentDirectoryPath: jest.fn(),
39 | ExternalDirectoryPath: jest.fn(),
40 | ExternalStorageDirectoryPath: jest.fn(),
41 | TemporaryDirectoryPath: jest.fn(),
42 | LibraryDirectoryPath: jest.fn(),
43 | PicturesDirectoryPath: jest.fn(),
44 | };
45 | });
46 |
--------------------------------------------------------------------------------
/android/app/_BUCK:
--------------------------------------------------------------------------------
1 | # To learn about Buck see [Docs](https://buckbuild.com/).
2 | # To run your application with Buck:
3 | # - install Buck
4 | # - `npm start` - to start the packager
5 | # - `cd android`
6 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
7 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
8 | # - `buck install -r android/app` - compile, install and run application
9 | #
10 |
11 | load(":build_defs.bzl", "create_aar_targets", "create_jar_targets")
12 |
13 | lib_deps = []
14 |
15 | create_aar_targets(glob(["libs/*.aar"]))
16 |
17 | create_jar_targets(glob(["libs/*.jar"]))
18 |
19 | android_library(
20 | name = "all-libs",
21 | exported_deps = lib_deps,
22 | )
23 |
24 | android_library(
25 | name = "app-code",
26 | srcs = glob([
27 | "src/main/java/**/*.java",
28 | ]),
29 | deps = [
30 | ":all-libs",
31 | ":build_config",
32 | ":res",
33 | ],
34 | )
35 |
36 | android_build_config(
37 | name = "build_config",
38 | package = "com.logfinance",
39 | )
40 |
41 | android_resource(
42 | name = "res",
43 | package = "com.logfinance",
44 | res = "src/main/res",
45 | )
46 |
47 | android_binary(
48 | name = "app",
49 | keystore = "//android/keystores:debug",
50 | manifest = "src/main/AndroidManifest.xml",
51 | package_type = "debug",
52 | deps = [
53 | ":app-code",
54 | ],
55 | )
56 |
--------------------------------------------------------------------------------
/android/app/build_defs.bzl:
--------------------------------------------------------------------------------
1 | """Helper definitions to glob .aar and .jar targets"""
2 |
3 | def create_aar_targets(aarfiles):
4 | for aarfile in aarfiles:
5 | name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")]
6 | lib_deps.append(":" + name)
7 | android_prebuilt_aar(
8 | name = name,
9 | aar = aarfile,
10 | )
11 |
12 | def create_jar_targets(jarfiles):
13 | for jarfile in jarfiles:
14 | name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")]
15 | lib_deps.append(":" + name)
16 | prebuilt_jar(
17 | name = name,
18 | binary_jar = jarfile,
19 | )
20 |
--------------------------------------------------------------------------------
/android/app/debug.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/debug.keystore
--------------------------------------------------------------------------------
/android/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | -keep class com.facebook.hermes.unicode.** { *; }
13 | -keep class com.facebook.jni.** { *; }
14 |
15 | -keep class com.swmansion.reanimated.** { *; }
16 | -keep class com.facebook.react.turbomodule.** { *; }
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
13 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/AntDesign.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/assets/fonts/AntDesign.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Entypo.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/assets/fonts/Entypo.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/EvilIcons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/assets/fonts/EvilIcons.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Feather.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/assets/fonts/Feather.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/FontAwesome.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/assets/fonts/FontAwesome.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/FontAwesome5_Brands.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/assets/fonts/FontAwesome5_Brands.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/FontAwesome5_Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/assets/fonts/FontAwesome5_Regular.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/FontAwesome5_Solid.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/assets/fonts/FontAwesome5_Solid.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Fontisto.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/assets/fonts/Fontisto.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Foundation.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/assets/fonts/Foundation.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Ionicons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/assets/fonts/Ionicons.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/MaterialCommunityIcons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/assets/fonts/MaterialCommunityIcons.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/MaterialIcons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/assets/fonts/MaterialIcons.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Octicons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/assets/fonts/Octicons.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Poppins-Black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/assets/fonts/Poppins-Black.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Poppins-BlackItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/assets/fonts/Poppins-BlackItalic.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Poppins-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/assets/fonts/Poppins-Bold.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Poppins-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/assets/fonts/Poppins-BoldItalic.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Poppins-ExtraBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/assets/fonts/Poppins-ExtraBold.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Poppins-ExtraBoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/assets/fonts/Poppins-ExtraBoldItalic.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Poppins-ExtraLight.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/assets/fonts/Poppins-ExtraLight.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Poppins-ExtraLightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/assets/fonts/Poppins-ExtraLightItalic.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Poppins-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/assets/fonts/Poppins-Italic.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Poppins-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/assets/fonts/Poppins-Light.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Poppins-LightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/assets/fonts/Poppins-LightItalic.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Poppins-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/assets/fonts/Poppins-Medium.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Poppins-MediumItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/assets/fonts/Poppins-MediumItalic.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Poppins-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/assets/fonts/Poppins-Regular.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Poppins-SemiBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/assets/fonts/Poppins-SemiBold.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Poppins-SemiBoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/assets/fonts/Poppins-SemiBoldItalic.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Poppins-Thin.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/assets/fonts/Poppins-Thin.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Poppins-ThinItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/assets/fonts/Poppins-ThinItalic.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/SimpleLineIcons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/assets/fonts/SimpleLineIcons.ttf
--------------------------------------------------------------------------------
/android/app/src/main/assets/fonts/Zocial.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/assets/fonts/Zocial.ttf
--------------------------------------------------------------------------------
/android/app/src/main/java/com/logfinance/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.logfinance;
2 |
3 | import com.facebook.react.ReactActivity;
4 |
5 | public class MainActivity extends ReactActivity {
6 |
7 | /**
8 | * Returns the name of the main component registered from JavaScript. This is used to schedule
9 | * rendering of the component.
10 | */
11 | @Override
12 | protected String getMainComponentName() {
13 | return "LogFinance";
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher_round_adaptive_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/res/mipmap-hdpi/ic_launcher_round_adaptive_back.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher_round_adaptive_fore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/res/mipmap-hdpi/ic_launcher_round_adaptive_fore.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher_round_adaptive_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/res/mipmap-mdpi/ic_launcher_round_adaptive_back.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher_round_adaptive_fore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/res/mipmap-mdpi/ic_launcher_round_adaptive_fore.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round_adaptive_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round_adaptive_back.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round_adaptive_fore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round_adaptive_fore.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round_adaptive_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round_adaptive_back.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round_adaptive_fore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round_adaptive_fore.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round_adaptive_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round_adaptive_back.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round_adaptive_fore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round_adaptive_fore.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | LogFinance
3 |
4 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | ext {
5 | buildToolsVersion = "30.0.2"
6 | minSdkVersion = 21
7 | compileSdkVersion = 30
8 | targetSdkVersion = 30
9 | ndkVersion = "21.4.7075529"
10 | kotlinVersion = '1.3.50'
11 | }
12 | repositories {
13 | google()
14 | mavenCentral()
15 | }
16 | dependencies {
17 | classpath("com.android.tools.build:gradle:4.2.2")
18 | // NOTE: Do not place your application dependencies here; they belong
19 | // in the individual module build.gradle files
20 | }
21 | }
22 |
23 | allprojects {
24 | repositories {
25 | mavenCentral()
26 | mavenLocal()
27 | maven {
28 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
29 | url("$rootDir/../node_modules/react-native/android")
30 | }
31 | maven {
32 | // Android JSC is installed from npm
33 | url("$rootDir/../node_modules/jsc-android/dist")
34 | }
35 |
36 | google()
37 | maven { url 'https://www.jitpack.io' }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 |
20 | # AndroidX package structure to make it clearer which packages are bundled with the
21 | # Android operating system, and which are packaged with your app's APK
22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
23 | android.useAndroidX=true
24 | # Automatically convert third-party libraries to use AndroidX
25 | android.enableJetifier=true
26 |
27 | # Version of flipper SDK to use with React Native
28 | FLIPPER_VERSION=0.99.0
29 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-all.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'LogFinance'
2 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
3 | include ':app'
4 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "LogFinance",
3 | "displayName": "LogFinance"
4 | }
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ['module:metro-react-native-babel-preset'],
3 | plugins: [
4 | 'react-native-reanimated/plugin',
5 | ['@babel/plugin-proposal-decorators', {legacy: true}],
6 | [
7 | 'module-resolver',
8 | {
9 | cwd: 'babelrc',
10 | root: ['./src'],
11 | extensions: [
12 | '.ts',
13 | '.tsx',
14 | '.js',
15 | '.jsx',
16 | '.ios.js',
17 | 'android.js',
18 | '.json',
19 | ],
20 | },
21 | ],
22 | ],
23 | };
24 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | import {AppRegistry, LogBox} from 'react-native';
2 |
3 | import App from './src/App';
4 | import {name as appName} from './app.json';
5 | import 'react-native-gesture-handler';
6 |
7 | LogBox.ignoreLogs([
8 | 'new NativeEventEmitter',
9 | 'Non-serializable values were found in the navigation state',
10 | ]);
11 |
12 | AppRegistry.registerComponent(appName, () => App);
13 |
--------------------------------------------------------------------------------
/ios/LogFinance.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ios/LogFinance.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/LogFinance/AppDelegate.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | @interface AppDelegate : UIResponder
5 |
6 | @property (nonatomic, strong) UIWindow *window;
7 |
8 | @end
9 |
--------------------------------------------------------------------------------
/ios/LogFinance/Images.xcassets/AppIcon.appiconset/100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/ios/LogFinance/Images.xcassets/AppIcon.appiconset/100.png
--------------------------------------------------------------------------------
/ios/LogFinance/Images.xcassets/AppIcon.appiconset/1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/ios/LogFinance/Images.xcassets/AppIcon.appiconset/1024.png
--------------------------------------------------------------------------------
/ios/LogFinance/Images.xcassets/AppIcon.appiconset/114.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/ios/LogFinance/Images.xcassets/AppIcon.appiconset/114.png
--------------------------------------------------------------------------------
/ios/LogFinance/Images.xcassets/AppIcon.appiconset/120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/ios/LogFinance/Images.xcassets/AppIcon.appiconset/120.png
--------------------------------------------------------------------------------
/ios/LogFinance/Images.xcassets/AppIcon.appiconset/128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/ios/LogFinance/Images.xcassets/AppIcon.appiconset/128.png
--------------------------------------------------------------------------------
/ios/LogFinance/Images.xcassets/AppIcon.appiconset/144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/ios/LogFinance/Images.xcassets/AppIcon.appiconset/144.png
--------------------------------------------------------------------------------
/ios/LogFinance/Images.xcassets/AppIcon.appiconset/152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/ios/LogFinance/Images.xcassets/AppIcon.appiconset/152.png
--------------------------------------------------------------------------------
/ios/LogFinance/Images.xcassets/AppIcon.appiconset/16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/ios/LogFinance/Images.xcassets/AppIcon.appiconset/16.png
--------------------------------------------------------------------------------
/ios/LogFinance/Images.xcassets/AppIcon.appiconset/167.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/ios/LogFinance/Images.xcassets/AppIcon.appiconset/167.png
--------------------------------------------------------------------------------
/ios/LogFinance/Images.xcassets/AppIcon.appiconset/172.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/ios/LogFinance/Images.xcassets/AppIcon.appiconset/172.png
--------------------------------------------------------------------------------
/ios/LogFinance/Images.xcassets/AppIcon.appiconset/180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/ios/LogFinance/Images.xcassets/AppIcon.appiconset/180.png
--------------------------------------------------------------------------------
/ios/LogFinance/Images.xcassets/AppIcon.appiconset/196.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/ios/LogFinance/Images.xcassets/AppIcon.appiconset/196.png
--------------------------------------------------------------------------------
/ios/LogFinance/Images.xcassets/AppIcon.appiconset/20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/ios/LogFinance/Images.xcassets/AppIcon.appiconset/20.png
--------------------------------------------------------------------------------
/ios/LogFinance/Images.xcassets/AppIcon.appiconset/216.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/ios/LogFinance/Images.xcassets/AppIcon.appiconset/216.png
--------------------------------------------------------------------------------
/ios/LogFinance/Images.xcassets/AppIcon.appiconset/256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/ios/LogFinance/Images.xcassets/AppIcon.appiconset/256.png
--------------------------------------------------------------------------------
/ios/LogFinance/Images.xcassets/AppIcon.appiconset/29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/ios/LogFinance/Images.xcassets/AppIcon.appiconset/29.png
--------------------------------------------------------------------------------
/ios/LogFinance/Images.xcassets/AppIcon.appiconset/32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/ios/LogFinance/Images.xcassets/AppIcon.appiconset/32.png
--------------------------------------------------------------------------------
/ios/LogFinance/Images.xcassets/AppIcon.appiconset/40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/ios/LogFinance/Images.xcassets/AppIcon.appiconset/40.png
--------------------------------------------------------------------------------
/ios/LogFinance/Images.xcassets/AppIcon.appiconset/48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/ios/LogFinance/Images.xcassets/AppIcon.appiconset/48.png
--------------------------------------------------------------------------------
/ios/LogFinance/Images.xcassets/AppIcon.appiconset/50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/ios/LogFinance/Images.xcassets/AppIcon.appiconset/50.png
--------------------------------------------------------------------------------
/ios/LogFinance/Images.xcassets/AppIcon.appiconset/512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/ios/LogFinance/Images.xcassets/AppIcon.appiconset/512.png
--------------------------------------------------------------------------------
/ios/LogFinance/Images.xcassets/AppIcon.appiconset/55.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/ios/LogFinance/Images.xcassets/AppIcon.appiconset/55.png
--------------------------------------------------------------------------------
/ios/LogFinance/Images.xcassets/AppIcon.appiconset/57.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/ios/LogFinance/Images.xcassets/AppIcon.appiconset/57.png
--------------------------------------------------------------------------------
/ios/LogFinance/Images.xcassets/AppIcon.appiconset/58.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/ios/LogFinance/Images.xcassets/AppIcon.appiconset/58.png
--------------------------------------------------------------------------------
/ios/LogFinance/Images.xcassets/AppIcon.appiconset/60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/ios/LogFinance/Images.xcassets/AppIcon.appiconset/60.png
--------------------------------------------------------------------------------
/ios/LogFinance/Images.xcassets/AppIcon.appiconset/64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/ios/LogFinance/Images.xcassets/AppIcon.appiconset/64.png
--------------------------------------------------------------------------------
/ios/LogFinance/Images.xcassets/AppIcon.appiconset/72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/ios/LogFinance/Images.xcassets/AppIcon.appiconset/72.png
--------------------------------------------------------------------------------
/ios/LogFinance/Images.xcassets/AppIcon.appiconset/76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/ios/LogFinance/Images.xcassets/AppIcon.appiconset/76.png
--------------------------------------------------------------------------------
/ios/LogFinance/Images.xcassets/AppIcon.appiconset/80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/ios/LogFinance/Images.xcassets/AppIcon.appiconset/80.png
--------------------------------------------------------------------------------
/ios/LogFinance/Images.xcassets/AppIcon.appiconset/87.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/ios/LogFinance/Images.xcassets/AppIcon.appiconset/87.png
--------------------------------------------------------------------------------
/ios/LogFinance/Images.xcassets/AppIcon.appiconset/88.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/ios/LogFinance/Images.xcassets/AppIcon.appiconset/88.png
--------------------------------------------------------------------------------
/ios/LogFinance/Images.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/ios/LogFinance/main.m:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | #import "AppDelegate.h"
4 |
5 | int main(int argc, char * argv[]) {
6 | @autoreleasepool {
7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/ios/LogFinanceTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/ios/Podfile:
--------------------------------------------------------------------------------
1 | require_relative '../node_modules/react-native/scripts/react_native_pods'
2 | require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
3 |
4 | platform :ios, '11.0'
5 |
6 | target 'LogFinance' do
7 | config = use_native_modules!
8 |
9 | use_react_native!(
10 | :path => config[:reactNativePath],
11 | # to enable hermes on iOS, change `false` to `true` and then install pods
12 | :hermes_enabled => false
13 | )
14 |
15 | pod 'React-jsi', :path => '../node_modules/react-native/ReactCommon/jsi', :modular_headers => true
16 | pod 'simdjson', path: '../node_modules/@nozbe/simdjson'
17 |
18 | target 'LogFinanceTests' do
19 | inherit! :complete
20 | # Pods for testing
21 | end
22 |
23 | # Enables Flipper.
24 | #
25 | # Note that if you have use_frameworks! enabled, Flipper will not work and
26 | # you should disable the next line.
27 | use_flipper!()
28 |
29 | post_install do |installer|
30 | react_native_post_install(installer)
31 | __apply_Xcode_12_5_M1_post_install_workaround(installer)
32 | end
33 | end
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | preset: 'react-native',
3 | verbose: true,
4 | setupFilesAfterEnv: [
5 | '@testing-library/react-hooks/dont-cleanup-after-each.js',
6 | ],
7 | moduleFileExtensions: ['js', 'jsx', 'ts', 'tsx'],
8 | setupFiles: [
9 | './node_modules/react-native-gesture-handler/jestSetup.js',
10 | '/jest.setup.js',
11 | ],
12 | transformIgnorePatterns: [
13 | 'node_modules/(?!(jest-)?react-native|react-clone-referenced-element|@react-native-community|rollbar-react-native|@fortawesome|@react-native|@react-navigation)',
14 | ],
15 | moduleDirectories: ['node_modules', 'utils', __dirname],
16 | };
17 |
--------------------------------------------------------------------------------
/jest.setup.js:
--------------------------------------------------------------------------------
1 | import 'react-native-gesture-handler/jestSetup';
2 | jest.runAllTimers();
3 |
4 | jest.mock('react-native/Libraries/Animated/NativeAnimatedHelper');
5 | jest.mock('react-native-vector-icons/MaterialIcons', () => 'Icon');
6 | jest.mock('react-native-reanimated', () => {
7 | // eslint-disable-next-line @typescript-eslint/no-var-requires
8 | const Reanimated = require('react-native-reanimated/mock');
9 |
10 | // The mock for `call` immediately calls the callback which is incorrect
11 | // So we override it with a no-op
12 | Reanimated.default.call = () => {};
13 |
14 | return Reanimated;
15 | });
16 |
17 | jest.mock('react-i18next', () => ({
18 | useTranslation: () => ({
19 | t: (key: any) => key,
20 | i18n: {
21 | changeLanguage: () => new Promise(() => {}),
22 | },
23 | }),
24 | }));
25 |
26 | jest.mock('services/database', () => ({
27 | getDatabase: () => ({
28 | collections: {
29 | get: jest.fn(() => ({
30 | query: jest.fn(() => ({
31 | fetch: jest.fn(() => Promise.resolve(true)),
32 | })),
33 | })),
34 | },
35 | }),
36 | }));
37 |
--------------------------------------------------------------------------------
/metro.config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Metro configuration for React Native
3 | * https://github.com/facebook/react-native
4 | *
5 | * @format
6 | */
7 |
8 | module.exports = {
9 | transformer: {
10 | getTransformOptions: async () => ({
11 | transform: {
12 | experimentalImportSupport: false,
13 | inlineRequires: true,
14 | },
15 | }),
16 | },
17 | };
18 |
--------------------------------------------------------------------------------
/react-native.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | project: {
3 | ios: {},
4 | android: {},
5 | },
6 | assets: ['./src/assets/fonts/poppins'],
7 | };
8 |
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React, {Suspense, useEffect} from 'react';
2 | import {ActivityIndicator} from 'react-native';
3 | import DatabaseProvider from '@nozbe/watermelondb/DatabaseProvider';
4 | import Routes from 'routes';
5 | import Theme from 'styles/theme';
6 |
7 | import {getDatabase, runSeeds} from 'services/database';
8 |
9 | import 'locales';
10 |
11 | const App = () => {
12 | const database = getDatabase();
13 |
14 | useEffect(() => runSeeds(), []);
15 |
16 | return (
17 |
18 |
19 | }>
20 |
21 |
22 |
23 |
24 | );
25 | };
26 |
27 | export default App;
28 |
--------------------------------------------------------------------------------
/src/__tests__/App.test.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/unbound-method */
2 | import React from 'react';
3 | import {render} from '@testing-library/react-native';
4 | import App from '../App';
5 |
6 | describe('App', () => {
7 | it('Test match snapshot App', () => {
8 | // given
9 | const props = {};
10 |
11 | // when
12 | const {toJSON} = render();
13 |
14 | // then
15 | expect(toJSON()).toMatchSnapshot();
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/src/__tests__/__snapshots__/App.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`App Test match snapshot App 1`] = ``;
4 |
--------------------------------------------------------------------------------
/src/assets/fonts/poppins/Poppins-Black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/src/assets/fonts/poppins/Poppins-Black.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/poppins/Poppins-BlackItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/src/assets/fonts/poppins/Poppins-BlackItalic.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/poppins/Poppins-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/src/assets/fonts/poppins/Poppins-Bold.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/poppins/Poppins-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/src/assets/fonts/poppins/Poppins-BoldItalic.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/poppins/Poppins-ExtraBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/src/assets/fonts/poppins/Poppins-ExtraBold.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/poppins/Poppins-ExtraBoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/src/assets/fonts/poppins/Poppins-ExtraBoldItalic.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/poppins/Poppins-ExtraLight.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/src/assets/fonts/poppins/Poppins-ExtraLight.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/poppins/Poppins-ExtraLightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/src/assets/fonts/poppins/Poppins-ExtraLightItalic.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/poppins/Poppins-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/src/assets/fonts/poppins/Poppins-Italic.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/poppins/Poppins-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/src/assets/fonts/poppins/Poppins-Light.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/poppins/Poppins-LightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/src/assets/fonts/poppins/Poppins-LightItalic.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/poppins/Poppins-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/src/assets/fonts/poppins/Poppins-Medium.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/poppins/Poppins-MediumItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/src/assets/fonts/poppins/Poppins-MediumItalic.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/poppins/Poppins-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/src/assets/fonts/poppins/Poppins-Regular.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/poppins/Poppins-SemiBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/src/assets/fonts/poppins/Poppins-SemiBold.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/poppins/Poppins-SemiBoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/src/assets/fonts/poppins/Poppins-SemiBoldItalic.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/poppins/Poppins-Thin.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/src/assets/fonts/poppins/Poppins-Thin.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/poppins/Poppins-ThinItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/src/assets/fonts/poppins/Poppins-ThinItalic.ttf
--------------------------------------------------------------------------------
/src/components/Button/Button.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import {Title, PrimaryButton, DangerButton, InverseButton} from './styles';
4 |
5 | export type ButtonType = 'primary' | 'danger';
6 |
7 | interface Props {
8 | title: string;
9 | disabled?: boolean;
10 | inverse?: boolean;
11 | type?: ButtonType;
12 | onPress: () => void;
13 | }
14 |
15 | const Button = ({
16 | title,
17 | onPress,
18 | disabled = false,
19 | inverse = false,
20 | type,
21 | }: Props): JSX.Element => {
22 | const Label = (
23 |
24 | {title}
25 |
26 | );
27 |
28 | if (inverse) {
29 | return (
30 |
31 | {Label}
32 |
33 | );
34 | }
35 |
36 | if (type === 'danger') {
37 | return (
38 |
39 | {Label}
40 |
41 | );
42 | }
43 |
44 | return (
45 |
46 | {Label}
47 |
48 | );
49 | };
50 |
51 | export default Button;
52 |
--------------------------------------------------------------------------------
/src/components/Button/index.ts:
--------------------------------------------------------------------------------
1 | export {default} from './Button';
2 |
--------------------------------------------------------------------------------
/src/components/Button/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 | import Colors from 'styles/colors';
3 | import {Text, TouchableOpacity} from 'styles/layout';
4 | import {ButtonType} from './Button';
5 |
6 | interface ButtonProps {
7 | disabled?: boolean;
8 | inverse?: boolean;
9 | type?: ButtonType;
10 | }
11 |
12 | export const BaseButton = styled(TouchableOpacity)`
13 | margin: ${({theme}) => theme.spacing.small}px;
14 | padding: ${({theme}) => theme.spacing.great}px
15 | ${({theme}) => theme.spacing.tall}px;
16 | border-radius: ${({theme}) => theme.spacing.tall}px;
17 | flex-direction: row;
18 | justify-content: center;
19 | align-items: center;
20 | min-width: 120px;
21 | border: 2px solid transparent;
22 | `;
23 |
24 | export const PrimaryButton = styled(BaseButton)`
25 | background: ${({disabled}) =>
26 | disabled ? `${Colors.seconday}40` : Colors.seconday};
27 | `;
28 |
29 | export const InverseButton = styled(BaseButton)`
30 | background: transparent;
31 | border: 2px solid ${Colors.seconday};
32 | `;
33 |
34 | export const DangerButton = styled(BaseButton)`
35 | background: ${({disabled}) =>
36 | disabled ? `${Colors.danger}40` : Colors.danger};
37 | `;
38 |
39 | export const Title = styled(Text)`
40 | color: ${({theme, inverse = false}) =>
41 | inverse ? theme.colors.seconday : theme.colors.white};
42 | font-size: ${({theme}) => theme.fontSizes.medium};
43 | font-weight: ${({theme}) => theme.fontWeight.bold};
44 | text-align: center;
45 | `;
46 |
--------------------------------------------------------------------------------
/src/components/CategoryIcon/CategoryIcon.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import icons from './icons';
3 |
4 | interface Props {
5 | name?: string;
6 | contained?: boolean;
7 | }
8 |
9 | import {IconItem, Contained} from './styles';
10 |
11 | const CategoryIcon = ({
12 | name = 'others',
13 | contained = false,
14 | }: Props): JSX.Element => {
15 | const iconOption = name in icons ? icons[name] : icons.others;
16 |
17 | if (contained) {
18 | return (
19 |
20 |
21 |
22 | );
23 | }
24 |
25 | return ;
26 | };
27 |
28 | export default CategoryIcon;
29 |
--------------------------------------------------------------------------------
/src/components/CategoryIcon/__tests__/CategoryIcon.test.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/unbound-method */
2 | import React from 'react';
3 | import {render} from 'utils/test-utils';
4 | import CategoryIcon from '../CategoryIcon';
5 |
6 | describe('CategoryIcon', () => {
7 | it('Test match snapshot CategoryIcon', () => {
8 | // given
9 | const props = {
10 | name: 'food',
11 | };
12 |
13 | // when
14 | const {toJSON} = render();
15 |
16 | // then
17 | expect(toJSON()).toMatchSnapshot();
18 | });
19 |
20 | it('Test match snapshot CategoryIcon default value', () => {
21 | // given
22 | const props = {};
23 |
24 | // when
25 | const {toJSON} = render();
26 |
27 | // then
28 | expect(toJSON()).toMatchSnapshot();
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/src/components/CategoryIcon/__tests__/__snapshots__/CategoryIcon.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`CategoryIcon Test match snapshot CategoryIcon 1`] = `
4 |
14 | `;
15 |
16 | exports[`CategoryIcon Test match snapshot CategoryIcon default value 1`] = `
17 |
27 | `;
28 |
--------------------------------------------------------------------------------
/src/components/CategoryIcon/index.ts:
--------------------------------------------------------------------------------
1 | export {default} from './CategoryIcon';
2 |
--------------------------------------------------------------------------------
/src/components/CategoryIcon/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 |
3 | import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
4 | import Colors from 'styles/colors';
5 |
6 | const CONTAINED_SIZE = 30;
7 | const EXTRA_SIZE_FOR_LARGE = 20;
8 |
9 | export const Contained = styled.View<{color?: string; large?: boolean}>`
10 | background: ${({color}) => (color ? color : Colors.seconday)};
11 | width: ${({large = false}) =>
12 | large ? CONTAINED_SIZE + EXTRA_SIZE_FOR_LARGE : CONTAINED_SIZE}px;
13 | height: ${({large = false}) =>
14 | large ? CONTAINED_SIZE + EXTRA_SIZE_FOR_LARGE : CONTAINED_SIZE}px;
15 |
16 | border-radius: ${({large = false}) =>
17 | (large ? CONTAINED_SIZE + EXTRA_SIZE_FOR_LARGE : CONTAINED_SIZE) / 2}px;
18 | justify-content: center;
19 | align-items: center;
20 | `;
21 |
22 | export const IconItem = styled(MaterialIcons).attrs(
23 | ({large, color}: {large: boolean; color: string}) => ({
24 | size: !large ? 24 : 32,
25 | color: color ?? Colors.gray,
26 | }),
27 | )``;
28 |
--------------------------------------------------------------------------------
/src/components/CategoryPick/CategoryPick.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import {useNavigation} from '@react-navigation/native';
4 | import {useTranslation} from 'react-i18next';
5 | import {CategoriesNavigationProp} from 'routes/StacksRoute';
6 |
7 | import {CategoryIcon, Row} from 'components';
8 |
9 | import {Label, Title, Touchable} from './styles';
10 | import {Category} from 'models';
11 |
12 | interface CategoryPickProps {
13 | category?: Category;
14 | setCategory: (value: Category) => void;
15 | }
16 |
17 | const CategoryPick = ({
18 | category,
19 | setCategory,
20 | }: CategoryPickProps): JSX.Element => {
21 | const navigation = useNavigation();
22 | const {t} = useTranslation('categories');
23 |
24 | const handleCategories = () => {
25 | navigation.navigate('CategoriesStack', {
26 | screen: 'Categories',
27 | params: {setCategory},
28 | });
29 | };
30 |
31 | return (
32 |
33 |
34 |
35 |
36 | {category?.key ? t(category.key) : t('category-default')}
37 |
38 |
39 | );
40 | };
41 |
42 | export default CategoryPick;
43 |
--------------------------------------------------------------------------------
/src/components/CategoryPick/__tests__/CategoryPick.test.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/unbound-method */
2 | /* eslint-disable @typescript-eslint/no-explicit-any */
3 | import React from 'react';
4 | import {render} from 'utils/test-utils';
5 | import CategoryPick from '../CategoryPick';
6 |
7 | jest.mock('react-i18next', () => ({
8 | useTranslation: () => ({t: (key: any) => key}),
9 | }));
10 |
11 | describe('CategoryPick', () => {
12 | it('Test match snapshot CategoryPick', () => {
13 | // given
14 | const props = {};
15 |
16 | // when
17 | const {toJSON} = render();
18 |
19 | // then
20 | expect(toJSON()).toMatchSnapshot();
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/src/components/CategoryPick/index.ts:
--------------------------------------------------------------------------------
1 | export {default} from './CategoryPick';
2 |
--------------------------------------------------------------------------------
/src/components/CategoryPick/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 |
3 | import {Text, TouchableOpacity} from 'styles/layout';
4 |
5 | export const Label = styled(Text)`
6 | color: ${({theme}) => theme.colors.white}50;
7 | font-size: ${({theme}) => theme.fontSizes.small};
8 | `;
9 |
10 | export const Title = styled(Label)`
11 | color: ${({theme}) => theme.colors.white};
12 | font-size: ${({theme}) => theme.fontSizes.small};
13 | padding-left: ${({theme}) => theme.spacing.great}px;
14 | font-weight: ${({theme}) => theme.fontWeight.bold};
15 | `;
16 |
17 | export const Touchable = styled(TouchableOpacity)`
18 | margin-top: 5px;
19 | flex-direction: row;
20 | justify-content: center;
21 | align-items: center;
22 | `;
23 |
--------------------------------------------------------------------------------
/src/components/Currency/__tests__/Currency.test.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/unbound-method */
2 | import React from 'react';
3 | import {Text} from 'react-native';
4 | import {render} from 'utils/test-utils';
5 |
6 | import Currency from '../Currency';
7 |
8 | describe('Currency', () => {
9 | it('Test match snapshot Currency', () => {
10 | // given
11 | const props = {
12 | value: 100.28,
13 | render: (text: string) => {text},
14 | };
15 |
16 | // when
17 | const {toJSON} = render();
18 |
19 | // then
20 | expect(toJSON()).toMatchSnapshot();
21 | });
22 |
23 | it('Test if the value renders correctly', () => {
24 | // given
25 | const props = {
26 | value: 99.45,
27 | render: (text: string) => {text},
28 | };
29 |
30 | // when
31 | const {getByText} = render();
32 |
33 | // then
34 | expect(getByText('R$ 99,45')).toBeTruthy();
35 | expect(() => getByText(/20,45/i)).toThrow('Unable to find an element');
36 | });
37 | });
38 |
--------------------------------------------------------------------------------
/src/components/Currency/__tests__/__snapshots__/Currency.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Currency Test match snapshot Currency 1`] = `
4 |
5 | R$ 100,28
6 |
7 | `;
8 |
--------------------------------------------------------------------------------
/src/components/Currency/index.ts:
--------------------------------------------------------------------------------
1 | export {default} from './Currency';
2 |
--------------------------------------------------------------------------------
/src/components/Dismiss/Dismiss.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {useNavigation} from '@react-navigation/core';
3 |
4 | import {Container, DismissIcon} from './styles';
5 |
6 | const Dismiss = (): JSX.Element => {
7 | const {goBack} = useNavigation();
8 | return (
9 |
10 |
11 |
12 | );
13 | };
14 |
15 | export default Dismiss;
16 |
--------------------------------------------------------------------------------
/src/components/Dismiss/__tests__/Dismiss.test.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/unbound-method */
2 | /* eslint-disable @typescript-eslint/no-explicit-any */
3 | import React from 'react';
4 | import {render} from 'utils/test-utils';
5 | import Dismiss from '../Dismiss';
6 |
7 | describe('Dismiss', () => {
8 | it('Test match snapshot Dismiss', () => {
9 | // given
10 | const props = {};
11 |
12 | // when
13 | const {toJSON} = render();
14 |
15 | // then
16 | expect(toJSON()).toMatchSnapshot();
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/src/components/Dismiss/__tests__/__snapshots__/Dismiss.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Dismiss Test match snapshot Dismiss 1`] = `
4 |
22 |
42 |
43 |
44 |
45 | `;
46 |
--------------------------------------------------------------------------------
/src/components/Dismiss/index.ts:
--------------------------------------------------------------------------------
1 | export {default} from './Dismiss';
2 |
--------------------------------------------------------------------------------
/src/components/Dismiss/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 | import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
3 | import Colors from 'styles/colors';
4 |
5 | export const Container = styled.TouchableOpacity``;
6 |
7 | export const DismissIcon = styled(Icon).attrs({
8 | name: 'chevron-down-circle',
9 | color: `${Colors.white}50`,
10 | size: 32,
11 | })``;
12 |
--------------------------------------------------------------------------------
/src/components/EntriesList/EntriesList.tsx:
--------------------------------------------------------------------------------
1 | import React, {useCallback} from 'react';
2 | import {useNavigation} from '@react-navigation/native';
3 |
4 | import EntryItem from 'components/EntryItem';
5 | import {AddEntryNavigationProp} from 'routes/StacksRoute';
6 |
7 | import {useEntry} from 'hooks/useEntry';
8 | import {FlatList} from './styles';
9 | import {Entry} from 'models';
10 | interface Props {
11 | entries: Entry[];
12 | }
13 |
14 | const EntriesList = ({entries}: Props): JSX.Element => {
15 | const navigation = useNavigation();
16 | const {removeEntry} = useEntry();
17 |
18 | const onPressEntry = useCallback(
19 | (entry?: Entry) => {
20 | navigation.navigate('AddEntryStack', {
21 | screen: 'AddEntry',
22 | params: {entry},
23 | });
24 | },
25 | [navigation],
26 | );
27 |
28 | const onPressRemoveEntry = useCallback(
29 | (entry: Entry) => {
30 | removeEntry(entry);
31 | },
32 | [removeEntry],
33 | );
34 |
35 | const ListKeyExtractor = useCallback(item => item.id, []);
36 |
37 | const renderItem = useCallback(
38 | ({item}) => (
39 | onPressEntry(item)}
42 | onPressRemoveEntry={onPressRemoveEntry}
43 | />
44 | ),
45 | [onPressRemoveEntry, onPressEntry],
46 | );
47 |
48 | return (
49 |
55 | );
56 | };
57 |
58 | export default EntriesList;
59 |
--------------------------------------------------------------------------------
/src/components/EntriesList/index.ts:
--------------------------------------------------------------------------------
1 | export {default} from './EntriesList';
2 |
--------------------------------------------------------------------------------
/src/components/EntriesList/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 |
3 | export const FlatList = styled.FlatList``;
4 |
--------------------------------------------------------------------------------
/src/components/EntryItem/index.ts:
--------------------------------------------------------------------------------
1 | export {default} from './EntryItem';
2 |
--------------------------------------------------------------------------------
/src/components/EntryItem/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 |
3 | import {Text, TouchableOpacity} from 'styles/layout';
4 |
5 | export const Container = styled(TouchableOpacity)`
6 | background: ${({theme}) => theme.colors.white};
7 | margin: ${({theme}) => theme.spacing.small}px
8 | ${({theme}) => theme.spacing.tall}px;
9 | padding: ${({theme}) => theme.spacing.tall}px
10 | ${({theme}) => theme.spacing.great}px;
11 | border-radius: ${({theme}) => theme.spacing.great}px;
12 | flex-direction: row;
13 | justify-content: space-between;
14 | align-items: center;
15 | `;
16 |
17 | export const CategoryContainer = styled.View`
18 | margin-right: ${({theme}) => theme.spacing.great}px;
19 | `;
20 | export const Description = styled.View`
21 | flex: 1;
22 | justify-content: center;
23 | `;
24 |
25 | export const BaseText = styled(Text)`
26 | color: ${({theme}) => theme.colors.paragraph};
27 | font-size: ${({theme}) => theme.fontSizes.small};
28 | `;
29 |
30 | export const Title = styled(BaseText)`
31 | font-size: ${({theme}) => theme.fontSizes.medium};
32 | `;
33 | export const SubTitle = styled(BaseText)``;
34 |
35 | export const Value = styled(BaseText)<{isExpense: boolean}>`
36 | color: ${({theme, isExpense = true}) =>
37 | isExpense ? theme.colors.danger : theme.colors.success};
38 | font-weight: ${({theme}) => theme.fontWeight.bold};
39 | `;
40 |
--------------------------------------------------------------------------------
/src/components/InputNumber/InputNumber.tsx:
--------------------------------------------------------------------------------
1 | import React, {useEffect, useState} from 'react';
2 |
3 | import {Container, Input, InputValue, Touchable, BackspaceIcon} from './styles';
4 | import {Currency} from 'components';
5 |
6 | interface Props {
7 | setShowKeyboard: () => void;
8 | value?: number;
9 | setValue: (value: number) => void;
10 | }
11 |
12 | const InputNumber = ({
13 | value = 0,
14 | setValue,
15 | setShowKeyboard,
16 | }: Props): JSX.Element => {
17 | const [showBackSpace, setShowBackSpace] = useState(false);
18 |
19 | const onBackSpace = () => {
20 | if (value === 0) return;
21 | setValue(Number(value.toString().slice(0, -1)));
22 | };
23 |
24 | useEffect(() => {
25 | setShowBackSpace(value > 0);
26 | }, [value]);
27 |
28 | return (
29 | <>
30 |
31 | {text}}
34 | />
35 |
36 | {!!showBackSpace && (
37 |
38 |
39 |
40 | )}
41 |
42 |
43 | >
44 | );
45 | };
46 |
47 | export default InputNumber;
48 |
--------------------------------------------------------------------------------
/src/components/InputNumber/__tests__/InputNumber.test.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/unbound-method */
2 | /* eslint-disable @typescript-eslint/no-explicit-any */
3 | import React from 'react';
4 | import {render} from 'utils/test-utils';
5 | import InputNumber from '../InputNumber';
6 |
7 | jest.mock('react-i18next', () => ({
8 | useTranslation: () => ({t: (key: any) => key}),
9 | }));
10 |
11 | describe('InputNumber', () => {
12 | it('Test match snapshot InputNumber', () => {
13 | // given
14 | const props = {
15 | setShowKeyboard: jest.fn(),
16 | };
17 |
18 | // when
19 | const {toJSON} = render();
20 |
21 | // then
22 | expect(toJSON()).toMatchSnapshot();
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/src/components/InputNumber/__tests__/__snapshots__/InputNumber.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`InputNumber Test match snapshot InputNumber 1`] = `
4 | Array [
5 |
16 |
28 | R$ 0,00
29 |
30 | ,
31 | ,
57 | ]
58 | `;
59 |
--------------------------------------------------------------------------------
/src/components/InputNumber/index.ts:
--------------------------------------------------------------------------------
1 | export {default} from './InputNumber';
2 |
--------------------------------------------------------------------------------
/src/components/InputNumber/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 | import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
3 | import {TouchableOpacity, Colors, Text} from 'styles/layout';
4 | import {width} from 'styles/mixins';
5 |
6 | export const Container = styled.View`
7 | align-self: flex-end;
8 | flex-direction: row;
9 | margin-right: ${({theme}) => theme.spacing.great}px;
10 | `;
11 |
12 | export const Input = styled(Text)`
13 | font-family: ${({theme}) => theme.fontFamily.regular};
14 | font-size: ${({theme}) => theme.fontSizes.extraBig};
15 | font-weight: ${({theme}) => theme.fontWeight.regular};
16 | color: ${({theme}) => theme.colors.white};
17 | `;
18 |
19 | export const InputValue = styled.TouchableOpacity.attrs({})<{
20 | addSpaceRight: boolean;
21 | }>`
22 | background: transparent;
23 | width: ${({addSpaceRight = false}) => (addSpaceRight ? width - 40 : width)}px;
24 | height: 60px;
25 | position: absolute;
26 | left: 0px;
27 | right: 60px;
28 | top: 100px;
29 | bottom: 0px;
30 | `;
31 |
32 | export const Touchable = styled(TouchableOpacity)`
33 | justify-content: center;
34 | `;
35 | export const BackspaceIcon = styled(MaterialIcons).attrs({
36 | name: 'backspace',
37 | size: 24,
38 | color: Colors.white,
39 | })`
40 | margin-left: ${({theme}) => theme.spacing.great}px;
41 | `;
42 |
--------------------------------------------------------------------------------
/src/components/List/Item.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {ListItemProps} from './List';
3 |
4 | import {
5 | Container,
6 | Content,
7 | Description,
8 | Title,
9 | Selected,
10 | SubTitle,
11 | BaseIcon,
12 | } from './styles';
13 |
14 | const Item = ({
15 | title,
16 | subtitle,
17 | selected,
18 | icon,
19 | onPress,
20 | }: ListItemProps): JSX.Element => {
21 | return (
22 |
23 | {icon && }
24 |
25 |
26 | {title}
27 | {subtitle && {subtitle}}
28 |
29 | {selected}
30 |
31 |
32 | );
33 | };
34 |
35 | export default Item;
36 |
--------------------------------------------------------------------------------
/src/components/List/List.tsx:
--------------------------------------------------------------------------------
1 | import React, {useCallback} from 'react';
2 |
3 | import {FlatList} from './styles';
4 | import Item from './Item';
5 |
6 | export interface ListItemProps {
7 | id: number;
8 | title: string;
9 | subtitle?: string;
10 | selected?: string;
11 | icon?: string;
12 | onPress: () => void;
13 | }
14 |
15 | interface Props {
16 | items: ListItemProps[];
17 | }
18 |
19 | const List = ({items}: Props): JSX.Element => {
20 | const ListKeyExtractor = useCallback(item => item.id, []);
21 | const renderItem = useCallback(({item}) => , []);
22 |
23 | return (
24 |
31 | );
32 | };
33 |
34 | export default List;
35 |
--------------------------------------------------------------------------------
/src/components/List/index.ts:
--------------------------------------------------------------------------------
1 | export {default} from './List';
2 |
3 | export * from './List';
4 |
--------------------------------------------------------------------------------
/src/components/List/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 | import {Text, TouchableOpacity, Colors} from 'styles/layout';
3 | import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
4 |
5 | export const FlatList = styled.FlatList``;
6 |
7 | export const Container = styled(TouchableOpacity)`
8 | background: ${({theme}) => theme.colors.white};
9 | padding: ${({theme}) => theme.spacing.tall}px
10 | ${({theme}) => theme.spacing.great}px;
11 |
12 | border-bottom-width: 1px;
13 | border-color: ${({theme}) => theme.colors.grayLight};
14 | flex-direction: row;
15 | justify-content: space-between;
16 | align-items: center;
17 | `;
18 |
19 | export const Content = styled.View`
20 | justify-content: space-between;
21 | flex-direction: row;
22 | flex: 1;
23 | align-items: center;
24 | padding-left: ${({theme}) => theme.spacing.small}px;
25 | `;
26 |
27 | export const Description = styled.View``;
28 |
29 | export const Title = styled(Text)`
30 | color: ${({theme}) => theme.colors.paragraph};
31 | font-size: ${({theme}) => theme.fontSizes.small};
32 | `;
33 |
34 | export const SubTitle = styled(Title)`
35 | font-size: ${({theme}) => theme.fontSizes.tiny};
36 | `;
37 | export const Selected = styled(Title)``;
38 |
39 | export const BaseIcon = styled(MaterialIcons).attrs(
40 | ({name}: {name?: string}) => ({
41 | size: 24,
42 | color: Colors.paragraph,
43 | name,
44 | }),
45 | )<{name?: string}>``;
46 |
--------------------------------------------------------------------------------
/src/components/NumberKeyboard/__tests__/NumberKeyboard.test.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/unbound-method */
2 | /* eslint-disable @typescript-eslint/no-explicit-any */
3 | import React from 'react';
4 | import {render} from 'utils/test-utils';
5 | import NumberKeyboard from '../NumberKeyboard';
6 |
7 | jest.mock('react-native/Libraries/Animated/NativeAnimatedHelper');
8 | jest.mock('react-native-vector-icons/MaterialIcons', () => 'Icon');
9 |
10 | jest.mock('react-i18next', () => ({
11 | useTranslation: () => ({t: (key: any) => key}),
12 | }));
13 |
14 | const MOCK = {
15 | valueDefault: 0,
16 | onDismiss: jest.fn(),
17 | onDone: jest.fn(),
18 | };
19 |
20 | describe('NumberKeyboard', () => {
21 | it('Test match snapshot NumberKeyboard', () => {
22 | // given
23 | const props = MOCK;
24 |
25 | // when
26 | const {toJSON} = render();
27 |
28 | // then
29 | expect(toJSON()).toMatchSnapshot();
30 | });
31 | });
32 |
--------------------------------------------------------------------------------
/src/components/NumberKeyboard/index.ts:
--------------------------------------------------------------------------------
1 | export {default} from './NumberKeyboard';
2 |
--------------------------------------------------------------------------------
/src/components/ProgressBar/ProgressBar.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {Container, Progress} from './styles';
3 |
4 | interface ProgressBarProps {
5 | percent: number;
6 | color?: string;
7 | }
8 |
9 | const ProgressBar = ({percent, color}: ProgressBarProps): JSX.Element => {
10 | return (
11 |
12 |
13 |
14 | );
15 | };
16 |
17 | export default ProgressBar;
18 |
--------------------------------------------------------------------------------
/src/components/ProgressBar/__tests__/ProgressBar.test.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/unbound-method */
2 | /* eslint-disable @typescript-eslint/no-explicit-any */
3 | import React from 'react';
4 | import {render} from 'utils/test-utils';
5 | import ProgressBar from '../ProgressBar';
6 |
7 | describe('ProgressBar', () => {
8 | it('Test match snapshot ProgressBar', () => {
9 | // given
10 | const props = {
11 | percent: 10,
12 | };
13 |
14 | // when
15 | const {toJSON} = render();
16 |
17 | // then
18 | expect(toJSON()).toMatchSnapshot();
19 | });
20 |
21 | it('Test match snapshot ProgressBar with color', () => {
22 | // given
23 | const props = {
24 | percent: 45,
25 | color: "#0000000"
26 | };
27 |
28 | // when
29 | const {toJSON} = render();
30 |
31 | // then
32 | expect(toJSON()).toMatchSnapshot();
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/src/components/ProgressBar/__tests__/__snapshots__/ProgressBar.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`ProgressBar Test match snapshot ProgressBar 1`] = `
4 |
14 |
27 |
28 | `;
29 |
30 | exports[`ProgressBar Test match snapshot ProgressBar with color 1`] = `
31 |
41 |
55 |
56 | `;
57 |
--------------------------------------------------------------------------------
/src/components/ProgressBar/index.ts:
--------------------------------------------------------------------------------
1 | export {default} from './ProgressBar';
2 |
--------------------------------------------------------------------------------
/src/components/ProgressBar/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 |
3 | const patternHexColor = /^#([0-9a-f]{3}){1,2}$/i;
4 |
5 | export const Container = styled.View`
6 | background: ${({theme}) => theme.colors.accent};
7 | border-radius: ${({theme}) => theme.spacing.small}px;
8 | `;
9 |
10 | export const Progress = styled.View<{percent?: number; color?: string}>`
11 | background: ${({theme, color}) =>
12 | color && patternHexColor.test(color) ? color : theme.colors.seconday}50;
13 | border-radius: ${({theme}) => theme.spacing.small}px;
14 | width: ${({percent = 0}) => (percent > 100 ? 100 : percent)}%;
15 | height: 10px;
16 | `;
17 | export const Title = styled.Text`
18 | font-size: 16px;
19 | `;
20 |
--------------------------------------------------------------------------------
/src/components/Row/Row.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import {Container, Content, Icon} from './styles';
4 |
5 | interface Props {
6 | icon?: string;
7 | children?: React.ReactNode;
8 | }
9 | const Row = ({icon, children}: Props): JSX.Element => {
10 | return (
11 |
12 | {!!icon && }
13 | {children}
14 |
15 | );
16 | };
17 |
18 | export default Row;
19 |
--------------------------------------------------------------------------------
/src/components/Row/index.ts:
--------------------------------------------------------------------------------
1 | export {default} from './Row';
2 |
--------------------------------------------------------------------------------
/src/components/Row/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 | import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
3 |
4 | import Colors from 'styles/colors';
5 |
6 | export const Container = styled.View`
7 | margin: ${({theme}) => theme.spacing.great}px
8 | ${({theme}) => theme.spacing.tall}px 0px
9 | ${({theme}) => theme.spacing.tall}px;
10 | flex-direction: row;
11 | align-items: center;
12 |
13 | padding-bottom: ${({theme}) => theme.spacing.tall}px;
14 | border-bottom-width: 1px;
15 | border-bottom-color: ${({theme}) => theme.colors.grayLight}10;
16 | `;
17 |
18 | export const Content = styled.View`
19 | justify-content: center;
20 | `;
21 |
22 | export const Icon = styled(MaterialIcons).attrs(({name}: {name: string}) => ({
23 | name,
24 | size: 24,
25 | color: Colors.white,
26 | }))`
27 | margin-right: ${({theme}) => theme.spacing.great}px;
28 | `;
29 |
--------------------------------------------------------------------------------
/src/components/Strap/Strap.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import {Container} from './styles';
4 |
5 | const Strap = (): JSX.Element => {
6 | return ;
7 | };
8 |
9 | export default Strap;
10 |
--------------------------------------------------------------------------------
/src/components/Strap/__tests__/Strap.test.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/unbound-method */
2 | /* eslint-disable @typescript-eslint/no-explicit-any */
3 | import React from 'react';
4 | import {render} from 'utils/test-utils';
5 | import Strap from '../Strap';
6 |
7 | describe('Strap', () => {
8 | it('Test match snapshot Strap', () => {
9 | // given
10 | const props = {};
11 |
12 | // when
13 | const {toJSON} = render();
14 |
15 | // then
16 | expect(toJSON()).toMatchSnapshot();
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/src/components/Strap/__tests__/__snapshots__/Strap.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Strap Test match snapshot Strap 1`] = `
4 |
20 | `;
21 |
--------------------------------------------------------------------------------
/src/components/Strap/index.ts:
--------------------------------------------------------------------------------
1 | export {default} from './Strap';
2 |
--------------------------------------------------------------------------------
/src/components/Strap/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 |
3 | export const Container = styled.View`
4 | height: 5px;
5 | width: 100px;
6 | border-radius: 2.5px;
7 | margin: 10px 0px;
8 | background: ${({theme}) => theme.colors.gray}10;
9 | `;
10 |
--------------------------------------------------------------------------------
/src/components/SwipeRemoveButton/SwipeRemoveButton.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {useTranslation} from 'react-i18next';
3 |
4 | import {Container, RemoveIcon, Label} from './styles';
5 |
6 | interface Props {
7 | onPressRemove: () => void;
8 | }
9 |
10 | const SwipeRemoveButton = ({onPressRemove}: Props): JSX.Element => {
11 | const {t} = useTranslation('global');
12 |
13 | return (
14 |
21 |
22 |
23 |
24 | );
25 | };
26 |
27 | export default SwipeRemoveButton;
28 |
--------------------------------------------------------------------------------
/src/components/SwipeRemoveButton/__tests__/__snapshots__/SwipeRemoveButton.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`SwipeRemoveButton Test match snapshot SwipeRemoveButton 1`] = `
4 |
38 |
48 |
60 | Remover
61 |
62 |
63 | `;
64 |
--------------------------------------------------------------------------------
/src/components/SwipeRemoveButton/index.ts:
--------------------------------------------------------------------------------
1 | export {default} from './SwipeRemoveButton';
2 |
--------------------------------------------------------------------------------
/src/components/SwipeRemoveButton/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 | import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
3 |
4 | import {Text, TouchableOpacity} from 'styles/layout';
5 | import Colors from 'styles/colors';
6 |
7 | export const Container = styled(TouchableOpacity)`
8 | margin: ${({theme}) => theme.spacing.small}px
9 | ${({theme}) => theme.spacing.great}px ${({theme}) => theme.spacing.small}px
10 | 0px;
11 | padding: 0px ${({theme}) => theme.spacing.tall}px;
12 | background: ${({theme}) => theme.colors.danger};
13 | border-radius: ${({theme}) => theme.spacing.great}px;
14 | justify-content: center;
15 | align-items: center;
16 | flex-direction: row;
17 | `;
18 | export const Label = styled(Text)`
19 | color: ${({theme}) => theme.colors.white};
20 | font-size: ${({theme}) => theme.fontSizes.small};
21 | `;
22 |
23 | export const RemoveIcon = styled(MaterialIcons).attrs({
24 | size: 24,
25 | color: Colors.white,
26 | name: 'delete',
27 | })``;
28 |
--------------------------------------------------------------------------------
/src/components/Toolbar/Toolbar.tsx:
--------------------------------------------------------------------------------
1 | import React, {useState} from 'react';
2 | import {useTranslation} from 'react-i18next';
3 |
4 | import {Dismiss} from 'components';
5 | import {Container, Search, Title, Touchable, SearchIcon} from './styles';
6 |
7 | interface Props {
8 | query?: string;
9 | hideSearch?: boolean;
10 | title?: string;
11 | onChangeText?: (query: string) => void;
12 | }
13 |
14 | const Toolbar = ({
15 | title,
16 | hideSearch = false,
17 | query,
18 | onChangeText,
19 | }: Props): JSX.Element => {
20 | const {t} = useTranslation('global');
21 | const [showSearch, setShowSearch] = useState(false);
22 |
23 | return (
24 |
25 |
26 |
27 | {title && !showSearch && {title}}
28 |
29 | {!hideSearch && onChangeText && (
30 | <>
31 | {(!title || showSearch) && (
32 | onChangeText(term)}
39 | />
40 | )}
41 |
42 | {(title || showSearch) && (
43 | setShowSearch(prev => !prev)}>
44 |
45 |
46 | )}
47 | >
48 | )}
49 |
50 | );
51 | };
52 |
53 | export default Toolbar;
54 |
--------------------------------------------------------------------------------
/src/components/Toolbar/index.ts:
--------------------------------------------------------------------------------
1 | export {default} from './Toolbar';
2 |
--------------------------------------------------------------------------------
/src/components/Toolbar/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 | import {Text, Colors, TouchableOpacity} from 'styles/layout';
3 |
4 | import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
5 |
6 | export const Container = styled.View`
7 | height: 60px;
8 | align-items: center;
9 | padding: 0px ${({theme}) => theme.spacing.tall}px;
10 | background: ${({theme}) => theme.colors.accent};
11 | flex-direction: row;
12 | justify-content: space-between;
13 | `;
14 |
15 | export const Touchable = styled(TouchableOpacity)``;
16 |
17 | export const Title = styled(Text)`
18 | flex: 1;
19 | color: ${({theme}) => theme.colors.white};
20 | padding-left: ${({theme}) => theme.spacing.great}px;
21 | `;
22 |
23 | export const Search = styled.TextInput.attrs({
24 | placeholderTextColor: `${Colors.white}50`,
25 | })`
26 | flex: 1;
27 | background: ${({theme}) => theme.colors.primary}80;
28 | padding: ${({theme}) => theme.spacing.great}px;
29 | margin-left: ${({theme}) => theme.spacing.great}px;
30 | border-radius: ${({theme}) => theme.spacing.great / 2}px;
31 | color: ${({theme}) => theme.colors.white};
32 | `;
33 |
34 | export const SearchIcon = styled(MaterialIcons).attrs(
35 | ({name}: {name: string}) => ({
36 | size: 24,
37 | color: Colors.paragraph,
38 | name: name ? name : 'search',
39 | }),
40 | )``;
41 |
--------------------------------------------------------------------------------
/src/components/index.ts:
--------------------------------------------------------------------------------
1 | export {default as CategoryIcon} from './CategoryIcon';
2 | export {default as EntriesList} from './EntriesList';
3 | export {default as Currency} from './Currency';
4 | export {default as Button} from './Button';
5 | export {default as Dismiss} from './Dismiss';
6 | export {default as SwipeRemoveButton} from './SwipeRemoveButton';
7 | export {default as NumberKeyboard} from './NumberKeyboard';
8 | export {default as Strap} from './Strap';
9 | export {default as List} from './List';
10 | export {default as Row} from './Row';
11 | export {default as Toolbar} from './Toolbar';
12 | export {default as InputNumber} from './InputNumber';
13 | export {default as CategoryPick} from './CategoryPick';
14 | export {default as ProgressBar} from './ProgressBar';
15 |
--------------------------------------------------------------------------------
/src/contexts/index.ts:
--------------------------------------------------------------------------------
1 | export {default as BudgetProvider} from './BudgetContext';
2 |
--------------------------------------------------------------------------------
/src/database/collections.ts:
--------------------------------------------------------------------------------
1 | export enum COLLECTIONS {
2 | ENTRIES = 'entries',
3 | CATEGORIES = 'categories',
4 | BUDGETS = 'budgets',
5 | }
6 |
7 | export default COLLECTIONS;
8 |
--------------------------------------------------------------------------------
/src/database/index.ts:
--------------------------------------------------------------------------------
1 | export * from './collections';
2 |
3 | export {default as migrations} from './migrations';
4 | export {default as modelClasses} from './modelClasses';
5 | export {default as schema} from './schema';
6 | export {default as seeds} from './seeds';
7 |
--------------------------------------------------------------------------------
/src/database/migrations.ts:
--------------------------------------------------------------------------------
1 | import {schemaMigrations} from '@nozbe/watermelondb/Schema/migrations';
2 |
3 | export const VERSION_MIGRATION = 1;
4 |
5 | export default schemaMigrations({
6 | migrations: [],
7 | });
8 |
--------------------------------------------------------------------------------
/src/database/modelClasses.ts:
--------------------------------------------------------------------------------
1 | import {Entry, Category, Budget} from 'models';
2 |
3 | export const modelClasses = [Entry, Category, Budget];
4 |
5 | export default modelClasses;
6 |
--------------------------------------------------------------------------------
/src/database/schema.ts:
--------------------------------------------------------------------------------
1 | import {appSchema, tableSchema} from '@nozbe/watermelondb';
2 |
3 | import COLLECTIONS from './collections';
4 | import {VERSION_MIGRATION} from './migrations';
5 |
6 | import {COLUMNS as EntryColumns} from 'models/Entry';
7 | import {COLUMNS as CategoryColumns} from 'models/Category';
8 | import {COLUMNS as BudgetColumns} from 'models/Budget';
9 |
10 | export const DATABASE_NAME = 'LogFinanceDb';
11 |
12 | export const dbScheme = appSchema({
13 | version: VERSION_MIGRATION,
14 | tables: [
15 | tableSchema({
16 | name: COLLECTIONS.ENTRIES,
17 | columns: EntryColumns,
18 | }),
19 | tableSchema({
20 | name: COLLECTIONS.CATEGORIES,
21 | columns: CategoryColumns,
22 | }),
23 | tableSchema({
24 | name: COLLECTIONS.BUDGETS,
25 | columns: BudgetColumns,
26 | }),
27 | ],
28 | });
29 |
30 | export default dbScheme;
31 |
--------------------------------------------------------------------------------
/src/database/seeds.ts:
--------------------------------------------------------------------------------
1 | import RNFS from 'react-native-fs';
2 |
3 | import {getDatabase} from 'services/database';
4 |
5 | import {getCategoryCollection} from 'repositories/CategoryRepository';
6 | import CATEGORIES from './categories.json';
7 |
8 | export const run = async (): Promise => {
9 | const path = RNFS.DocumentDirectoryPath + '/seed.json';
10 |
11 | const alreadyCreated = await RNFS.exists(path);
12 | if (alreadyCreated) return;
13 |
14 | await getDatabase().write(() => getDatabase().unsafeResetDatabase());
15 |
16 | await getDatabase().write(async () => {
17 | const records = CATEGORIES.map(({description, key, color}) => {
18 | return getCategoryCollection().prepareCreate(record => {
19 | record.description = description;
20 | record.key = key;
21 | record.color = color;
22 | });
23 | });
24 |
25 | await getDatabase().batch(...records);
26 |
27 | const datetime = new Date().toISOString();
28 | await RNFS.writeFile(path, `{"datetime": "${datetime}"}`);
29 | });
30 | };
31 |
32 | export default {run};
33 |
--------------------------------------------------------------------------------
/src/hooks/__tests__/__snapshots__/useEntry.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`EntryProvider Test match snapshot useEntry 1`] = `
4 |
5 | Teste
6 |
7 | `;
8 |
--------------------------------------------------------------------------------
/src/hooks/index.ts:
--------------------------------------------------------------------------------
1 | export {default as useEntry} from './useEntry';
2 | export {default as EntryContext} from './useEntry';
3 |
4 | export {default as useCategory} from './useCategory';
5 | export {default as useDialog} from './useDialog';
6 |
--------------------------------------------------------------------------------
/src/hooks/useCategory.tsx:
--------------------------------------------------------------------------------
1 | import {useState, useCallback, useEffect} from 'react';
2 |
3 | import {Category} from 'models';
4 | import {CategoryRepository} from 'repositories';
5 | import {Subscription} from 'rxjs';
6 |
7 | let categoryDefaultSubscription = new Subscription();
8 |
9 | interface UseCategoryProps {
10 | categoryDefault?: Category;
11 | setCategoryDefault: (value: Category) => void;
12 | }
13 |
14 | export const useCategory = (): UseCategoryProps => {
15 | const [categoryDefault, setCategoryDefault] = useState();
16 |
17 | const loadCategoryDefault = useCallback(() => {
18 | categoryDefaultSubscription =
19 | CategoryRepository.getDefaultCategory().subscribe(categories => {
20 | if (!categories.length) return;
21 |
22 | const [firstCategory] = categories;
23 | setCategoryDefault(firstCategory);
24 | });
25 | }, []);
26 |
27 | useEffect(() => {
28 | loadCategoryDefault();
29 | return categoryDefaultSubscription.unsubscribe();
30 | }, [loadCategoryDefault]);
31 |
32 | return {
33 | categoryDefault,
34 | setCategoryDefault,
35 | };
36 | };
37 |
38 | export default useCategory;
39 |
--------------------------------------------------------------------------------
/src/hooks/useDialog.ts:
--------------------------------------------------------------------------------
1 | import {Alert} from 'react-native';
2 | import {useTranslation} from 'react-i18next';
3 |
4 | export const DIALOG_OPTIONS = {
5 | YES: 'yes',
6 | NO: 'no',
7 | };
8 |
9 | interface AlertYesOrNoProps {
10 | title: string;
11 | message: string;
12 | }
13 |
14 | interface UseDialogProps {
15 | dialog: (argments: AlertYesOrNoProps) => void;
16 | alertYesOrNo: (argments: AlertYesOrNoProps) => Promise;
17 | }
18 |
19 | export const useDialog = (): UseDialogProps => {
20 | const {t} = useTranslation('global');
21 |
22 | const dialog = ({title, message}: AlertYesOrNoProps) =>
23 | new Promise(resolve => {
24 | const actions = [
25 | {
26 | text: t('yes'),
27 | onPress: () => resolve(DIALOG_OPTIONS.YES),
28 | },
29 | {
30 | text: t('no'),
31 | },
32 | ];
33 | Alert.alert(title, message, actions, {cancelable: false});
34 | });
35 |
36 | const alertYesOrNo = async ({
37 | title,
38 | message,
39 | }: AlertYesOrNoProps): Promise =>
40 | (await dialog({title, message})) === DIALOG_OPTIONS.YES;
41 |
42 | return {
43 | dialog,
44 | alertYesOrNo,
45 | };
46 | };
47 |
48 | export default useDialog;
49 |
--------------------------------------------------------------------------------
/src/interfaces/IBudget.ts:
--------------------------------------------------------------------------------
1 | import {Category} from 'models';
2 |
3 | export default interface IBudget {
4 | description?: string;
5 | category: Category;
6 | value: number;
7 | }
8 |
--------------------------------------------------------------------------------
/src/interfaces/ICategory.ts:
--------------------------------------------------------------------------------
1 | export default interface ICategory {
2 | id: number;
3 | description: string;
4 | key: string;
5 | color: string;
6 | }
7 |
--------------------------------------------------------------------------------
/src/interfaces/IEntry.ts:
--------------------------------------------------------------------------------
1 | import {Category} from 'models';
2 |
3 | export type EntryType = 'expense' | 'earning';
4 | export default interface IEntry {
5 | description?: string;
6 | type: EntryType;
7 | value: number;
8 | dateAt: Date;
9 | category: Category;
10 | }
11 |
--------------------------------------------------------------------------------
/src/interfaces/index.ts:
--------------------------------------------------------------------------------
1 | export {default as ICategory} from './ICategory';
2 | export {default as IEntry} from './IEntry';
3 | export {default as IBudget} from './IBudget';
4 |
--------------------------------------------------------------------------------
/src/locales/index.ts:
--------------------------------------------------------------------------------
1 | import i18n from 'i18next';
2 | import {initReactI18next} from 'react-i18next';
3 | import {DEFAULT_LANGUAGE, languageDetector, resources} from 'services/language';
4 |
5 | i18n
6 | .use(languageDetector)
7 | .use(initReactI18next)
8 | .init({
9 | compatibilityJSON: 'v3',
10 | resources,
11 | fallbackLng: DEFAULT_LANGUAGE,
12 | debug: true,
13 | interpolation: {
14 | escapeValue: false,
15 | },
16 | });
17 |
18 | export default i18n;
19 |
--------------------------------------------------------------------------------
/src/models/Budget.ts:
--------------------------------------------------------------------------------
1 | import {ColumnSchema, Model, Relation} from '@nozbe/watermelondb';
2 | import {
3 | field,
4 | text,
5 | relation,
6 | readonly,
7 | date,
8 | } from '@nozbe/watermelondb/decorators';
9 |
10 | import COLLECTIONS from 'database/collections';
11 | import {Category} from 'models';
12 |
13 | export default class Budget extends Model {
14 | static table = COLLECTIONS.BUDGETS;
15 |
16 | @field('value') value!: number;
17 | @text('description') description!: string;
18 |
19 | @relation(COLLECTIONS.CATEGORIES, 'category_id')
20 | category!: Relation;
21 |
22 | @readonly
23 | @date('created_at')
24 | createdAt!: Date;
25 | }
26 |
27 | export const COLUMNS: ColumnSchema[] = [
28 | {name: 'description', type: 'string', isOptional: true},
29 | {name: 'value', type: 'number'},
30 | {name: 'category_id', type: 'string', isIndexed: true},
31 | {name: 'created_at', type: 'number'},
32 | ];
33 |
--------------------------------------------------------------------------------
/src/models/Category.ts:
--------------------------------------------------------------------------------
1 | import {ColumnSchema, Model} from '@nozbe/watermelondb';
2 | import {field, text} from '@nozbe/watermelondb/decorators';
3 |
4 | import COLLECTIONS from 'database/collections';
5 |
6 | export default class Category extends Model {
7 | static table = COLLECTIONS.CATEGORIES;
8 |
9 | @text('description') description!: string;
10 | @field('key') key!: string;
11 |
12 | @field('color') color!: string;
13 | }
14 |
15 | export const COLUMNS: ColumnSchema[] = [
16 | {name: 'description', type: 'string'},
17 | {name: 'key', type: 'string'},
18 | {name: 'color', type: 'string'},
19 | ];
20 |
--------------------------------------------------------------------------------
/src/models/Entry.ts:
--------------------------------------------------------------------------------
1 | import {ColumnSchema, Model, Relation} from '@nozbe/watermelondb';
2 | import {
3 | field,
4 | text,
5 | date,
6 | readonly,
7 | relation,
8 | } from '@nozbe/watermelondb/decorators';
9 |
10 | import COLLECTIONS from 'database/collections';
11 | import {EntryType} from 'interfaces/IEntry';
12 | import Category from './Category';
13 |
14 | export default class Entry extends Model {
15 | static table = COLLECTIONS.ENTRIES;
16 |
17 | @text('description') description?: string;
18 | @field('type') type!: EntryType;
19 | @field('value') value!: number;
20 | @date('date_at') dateAt!: Date;
21 |
22 | @relation(COLLECTIONS.CATEGORIES, 'category_id')
23 | category!: Relation;
24 |
25 | @readonly
26 | @date('created_at')
27 | createdAt!: Date;
28 |
29 | @readonly
30 | @date('updated_at')
31 | updatedAt!: Date;
32 | }
33 |
34 | export const COLUMNS: ColumnSchema[] = [
35 | {name: 'description', type: 'string', isOptional: true},
36 | {name: 'type', type: 'string'},
37 | {name: 'value', type: 'number'},
38 | {name: 'date_at', type: 'number', isIndexed: true},
39 | {name: 'category_id', type: 'string', isIndexed: true},
40 | {name: 'created_at', type: 'number'},
41 | {name: 'updated_at', type: 'number'},
42 | ];
43 |
--------------------------------------------------------------------------------
/src/models/index.ts:
--------------------------------------------------------------------------------
1 | export {default as Category} from './Category';
2 | export {default as Entry} from './Entry';
3 | export {default as Budget} from './Budget';
4 |
--------------------------------------------------------------------------------
/src/repositories/BudgetRepository.ts:
--------------------------------------------------------------------------------
1 | import {Collection} from '@nozbe/watermelondb';
2 | import {Observable} from 'rxjs';
3 | import {Budget} from 'models';
4 | import {getDatabase} from 'services/database';
5 | import {COLLECTIONS} from 'database';
6 | import {IBudget} from 'interfaces';
7 |
8 | const getBudgetCollection = (): Collection =>
9 | getDatabase().collections.get(COLLECTIONS.BUDGETS);
10 |
11 | export const getBudgetsObserve = (): Observable =>
12 | getBudgetCollection().query().observe();
13 |
14 | const fill = (record: Budget, data: IBudget) => {
15 | const {description, value, category} = data;
16 | if (description) record.description = description;
17 | record.value = value;
18 | record.category.set(category);
19 | };
20 |
21 | export const addBudget = async (data: IBudget): Promise => {
22 | return getDatabase().write(async () => {
23 | return getBudgetCollection().create(record => {
24 | fill(record, data);
25 | });
26 | });
27 | };
28 |
29 | export const updateBudget = async (
30 | budget: Budget,
31 | data: IBudget,
32 | ): Promise => {
33 | return getDatabase().write(async () => {
34 | return budget.update(record => {
35 | fill(record, data);
36 | });
37 | });
38 | };
39 |
40 | export const removeBudget = async (budget: Budget): Promise => {
41 | return getDatabase().write(async () => {
42 | await budget.destroyPermanently();
43 | });
44 | };
45 |
46 | export default {getBudgetsObserve, addBudget, updateBudget, removeBudget};
47 |
--------------------------------------------------------------------------------
/src/repositories/CategoryRepository.ts:
--------------------------------------------------------------------------------
1 | import {Collection, Q} from '@nozbe/watermelondb';
2 | import {Observable} from 'rxjs';
3 | import {Category} from 'models';
4 | import {getDatabase} from '../services/database';
5 | import {COLLECTIONS} from 'database';
6 |
7 | export const getCategoryCollection = (): Collection =>
8 | getDatabase().collections.get(COLLECTIONS.CATEGORIES);
9 |
10 | export const getCategories = (): Observable =>
11 | getCategoryCollection().query().observe();
12 |
13 | export const getDefaultCategory = (): Observable =>
14 | getCategoryCollection().query(Q.where('key', 'others')).observe();
15 |
16 | export default {getCategoryCollection, getCategories, getDefaultCategory};
17 |
--------------------------------------------------------------------------------
/src/repositories/index.ts:
--------------------------------------------------------------------------------
1 | export {default as CategoryRepository} from './CategoryRepository';
2 | export {default as EntryRepository} from './EntryRepository';
3 | export {default as BudgetRepository} from './BudgetRepository';
4 |
--------------------------------------------------------------------------------
/src/routes/components/BottomTabs/index.ts:
--------------------------------------------------------------------------------
1 | export {default} from './BottomTabs';
2 |
--------------------------------------------------------------------------------
/src/routes/components/BottomTabs/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 | import {getBottomSpace} from 'react-native-iphone-x-helper';
3 |
4 | import {Text, TouchableOpacity} from 'styles/layout';
5 | import shadow from 'styles/shadow';
6 | import {isIos, width} from 'styles/mixins';
7 |
8 | const TAB_BAR_HEIGHT_WITH_TITLE = (isIos ? 40 : 80) + getBottomSpace();
9 | const TAB_BAR_HEIGHT_WITHOUT_TITLE = (isIos ? 60 : 100) + getBottomSpace();
10 |
11 | export const Button = styled(TouchableOpacity).attrs(
12 | ({disableActiveOpacity = false}: {disableActiveOpacity: boolean}) => ({
13 | activeOpacity: disableActiveOpacity ? 1 : 0.8,
14 | }),
15 | )<{disableActiveOpacity: boolean}>`
16 | display: flex;
17 | flex-direction: column;
18 | align-items: center;
19 | width: ${width * 0.2}px;
20 | `;
21 |
22 | export const Label = styled(Text)<{isFocused: boolean}>`
23 | color: ${({theme, isFocused = false}) =>
24 | isFocused ? theme.colors.white : theme.colors.paragraph};
25 | font-size: ${({theme}) => theme.fontSizes.small};
26 | `;
27 |
28 | export const Container = styled.View<{showTitle: boolean}>`
29 | background-color: ${({theme}) => theme.colors.primary};
30 | height: ${({showTitle = false}) =>
31 | showTitle ? TAB_BAR_HEIGHT_WITH_TITLE : TAB_BAR_HEIGHT_WITHOUT_TITLE}px;
32 | flex-direction: row;
33 | padding-bottom: ${({theme}) => theme.spacing.tall}px;
34 | justify-content: space-around;
35 | align-items: center;
36 | padding-vertical: 10px;
37 | border-top-left-radius: ${({theme}) => theme.spacing.tall}px;
38 | border-top-right-radius: ${({theme}) => theme.spacing.tall}px;
39 | position: absolute;
40 | bottom: 0;
41 | left: 0;
42 | right: 0;
43 | ${shadow}
44 | `;
45 |
--------------------------------------------------------------------------------
/src/routes/components/TabIcon/TabIcon.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import {
4 | TAB_KEY_HOME,
5 | TAB_KEY_ADD_ENTRY,
6 | TAB_KEY_REPORTS,
7 | TAB_KEY_HISTORIC,
8 | TAB_KEY_BUDGET,
9 | } from 'routes/TabsRoute';
10 |
11 | import Colors from 'styles/colors';
12 |
13 | import {Icon, AddButtonContainer} from './styles';
14 |
15 | interface Props {
16 | route: string;
17 | focused?: boolean;
18 | }
19 |
20 | const TabIcon = ({route, focused}: Props): JSX.Element => {
21 | const icons = {
22 | [`${TAB_KEY_HOME}`]: 'home',
23 | [`${TAB_KEY_ADD_ENTRY}`]: 'add',
24 | [`${TAB_KEY_REPORTS}`]: 'assessment',
25 | [`${TAB_KEY_HISTORIC}`]: 'article',
26 | [`${TAB_KEY_BUDGET}`]: 'gps-fixed',
27 | };
28 |
29 | const icon = icons[route] ?? 'crop-square';
30 |
31 | if (route === TAB_KEY_ADD_ENTRY) {
32 | return (
33 |
34 |
35 |
36 | );
37 | }
38 |
39 | return ;
40 | };
41 |
42 | export default TabIcon;
43 |
--------------------------------------------------------------------------------
/src/routes/components/TabIcon/__tests__/TabIcon.test.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/unbound-method */
2 | import React from 'react';
3 |
4 | import {render} from 'utils/test-utils';
5 |
6 | import TabIcon from '../TabIcon';
7 |
8 | describe('TabIcon', () => {
9 | it('Test match snapshot TabIcon', () => {
10 | // given
11 | const props = {
12 | route: 'Home',
13 | focused: true,
14 | };
15 |
16 | // when
17 | const {toJSON} = render();
18 |
19 | // then
20 | expect(toJSON()).toMatchSnapshot();
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/src/routes/components/TabIcon/__tests__/__snapshots__/TabIcon.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`TabIcon Test match snapshot TabIcon 1`] = `
4 |
14 | `;
15 |
--------------------------------------------------------------------------------
/src/routes/components/TabIcon/index.ts:
--------------------------------------------------------------------------------
1 | export {default} from './TabIcon';
2 |
--------------------------------------------------------------------------------
/src/routes/components/TabIcon/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 | import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
3 | import shadow from 'styles/shadow';
4 |
5 | export const Icon = styled(MaterialIcons).attrs({
6 | size: 32,
7 | })``;
8 |
9 | export const AddButtonContainer = styled.View`
10 | background-color: ${({theme}) => theme.colors.seconday};
11 | padding: ${({theme}) => theme.spacing.tall}px;
12 | justify-content: center;
13 | align-items: center;
14 | position: absolute;
15 | top: -60px;
16 | align-self: center;
17 | z-index: 100;
18 | border-radius: 50px;
19 | elevation: 20;
20 | ${shadow}
21 | `;
22 |
--------------------------------------------------------------------------------
/src/routes/components/index.ts:
--------------------------------------------------------------------------------
1 | export {default as BottomTabs} from './BottomTabs';
2 |
--------------------------------------------------------------------------------
/src/screens/AddBudget/index.ts:
--------------------------------------------------------------------------------
1 | export {default} from './AddBudgetScreen';
2 |
--------------------------------------------------------------------------------
/src/screens/AddBudget/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 | import Colors from 'styles/colors';
3 | import {Text} from 'styles/layout';
4 |
5 | export const Container = styled.View`
6 | flex: 1;
7 | background: ${({theme}) => theme.colors.gray};
8 | `;
9 |
10 | export const Content = styled.View`
11 | padding: ${({theme}) => theme.spacing.tall}px 0px;
12 | `;
13 |
14 | export const Title = styled.Text`
15 | font-size: 16px;
16 | `;
17 |
18 | export const Label = styled(Text)`
19 | color: ${({theme}) => theme.colors.white}50;
20 | font-size: ${({theme}) => theme.fontSizes.small};
21 | `;
22 |
23 | export const Input = styled.TextInput.attrs({
24 | numberOfLines: 1,
25 | placeholderTextColor: Colors.grayLight,
26 | blurOnSubmit: true,
27 | })`
28 | font-family: ${({theme}) => theme.fontFamily.regular};
29 | font-size: ${({theme}) => theme.fontSizes.small};
30 | color: ${({theme}) => theme.colors.grayLight};
31 | font-weight: ${({theme}) => theme.fontWeight.bold};
32 | `;
33 |
34 | export const SubmitContainer = styled.View`
35 | width: 100%;
36 | padding: ${({theme}) => theme.spacing.tall}px
37 | ${({theme}) => theme.spacing.venti}px;
38 | `;
39 |
--------------------------------------------------------------------------------
/src/screens/AddEntry/__tests__/AddEntryScreen.test.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/unbound-method */
2 | /* eslint-disable @typescript-eslint/no-explicit-any */
3 | import React from 'react';
4 | import {render} from 'utils/test-utils';
5 | import AddEntryScreen from '../AddEntryScreen';
6 |
7 | jest.mock('react-i18next', () => ({
8 | useTranslation: () => ({t: (key: any) => key}),
9 | }));
10 |
11 | jest.mock('@react-navigation/native', () => {
12 | const actualNav = jest.requireActual('@react-navigation/native');
13 | return {
14 | ...actualNav,
15 | useRoute: () => jest.fn(),
16 | };
17 | });
18 |
19 | describe('AddEntryScreen', () => {
20 | it('Test match snapshot AddEntryScreen', () => {
21 | // given
22 | const props = {};
23 |
24 | // when
25 | const {toJSON} = render();
26 |
27 | // then
28 | expect(toJSON()).toMatchSnapshot();
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/src/screens/AddEntry/components/EntryCategory/EntryCategory.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import {useNavigation} from '@react-navigation/native';
4 | import {useTranslation} from 'react-i18next';
5 | import {CategoriesNavigationProp} from 'routes/StacksRoute';
6 | import {useEntry} from 'hooks/useEntry';
7 |
8 | import {CategoryIcon, Row} from 'components';
9 |
10 | import {Label, Title, Touchable} from './styles';
11 |
12 | const EntryCategory = (): JSX.Element => {
13 | const navigation = useNavigation();
14 | const {t} = useTranslation('categories');
15 | const {category, setCategory} = useEntry();
16 |
17 | const handleCategories = () => {
18 | navigation.navigate('CategoriesStack', {
19 | screen: 'Categories',
20 | params: {setCategory},
21 | });
22 | };
23 |
24 | return (
25 |
26 |
27 |
28 |
29 | {category?.key ? t(category.key) : t('category-default')}
30 |
31 |
32 | );
33 | };
34 |
35 | export default EntryCategory;
36 |
--------------------------------------------------------------------------------
/src/screens/AddEntry/components/EntryCategory/__tests__/EntryCategory.test.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/unbound-method */
2 | /* eslint-disable @typescript-eslint/no-explicit-any */
3 | import React from 'react';
4 | import {render} from 'utils/test-utils';
5 | import EntryCategory from '../EntryCategory';
6 |
7 | jest.mock('react-i18next', () => ({
8 | useTranslation: () => ({t: (key: any) => key}),
9 | }));
10 |
11 | describe('EntryCategory', () => {
12 | it('Test match snapshot EntryCategory', () => {
13 | // given
14 | const props = {};
15 |
16 | // when
17 | const {toJSON} = render();
18 |
19 | // then
20 | expect(toJSON()).toMatchSnapshot();
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/src/screens/AddEntry/components/EntryCategory/index.ts:
--------------------------------------------------------------------------------
1 | export {default} from './EntryCategory';
2 |
--------------------------------------------------------------------------------
/src/screens/AddEntry/components/EntryCategory/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 |
3 | import {Text, TouchableOpacity} from 'styles/layout';
4 |
5 | export const Label = styled(Text)`
6 | color: ${({theme}) => theme.colors.white}50;
7 | font-size: ${({theme}) => theme.fontSizes.small};
8 | `;
9 |
10 | export const Title = styled(Label)`
11 | color: ${({theme}) => theme.colors.white};
12 | font-size: ${({theme}) => theme.fontSizes.small};
13 | padding-left: ${({theme}) => theme.spacing.great}px;
14 | font-weight: ${({theme}) => theme.fontWeight.bold};
15 | `;
16 |
17 | export const Touchable = styled(TouchableOpacity)`
18 | margin-top: 5px;
19 | flex-direction: row;
20 | justify-content: center;
21 | align-items: center;
22 | `;
23 |
--------------------------------------------------------------------------------
/src/screens/AddEntry/components/EntryDate/EntryDate.tsx:
--------------------------------------------------------------------------------
1 | import React, {useState} from 'react';
2 | import DateTimePickerModal from 'react-native-modal-datetime-picker';
3 | import {useTranslation} from 'react-i18next';
4 | import {isToday, format} from 'date-fns';
5 |
6 | import {useEntry} from 'hooks/useEntry';
7 | import {Row} from 'components';
8 |
9 | import {Label, Title, Touchable} from './styles';
10 |
11 | const EntryDate = (): JSX.Element => {
12 | const {t} = useTranslation('add');
13 | const {dateAt = new Date(), setDateAt} = useEntry();
14 | const [isDatePickerVisible, setDatePickerVisibility] = useState(false);
15 |
16 | const showDatePicker = () => {
17 | setDatePickerVisibility(true);
18 | };
19 |
20 | const hideDatePicker = () => {
21 | setDatePickerVisibility(false);
22 | };
23 |
24 | const handleConfirm = (dateSelected: Date) => {
25 | setDateAt(dateSelected);
26 | hideDatePicker();
27 | };
28 |
29 | const prefix = `${isToday(dateAt) ? t('today') + ', ' : t('day')}`;
30 | const formatPattern = `'${prefix}' dd '${t('of')}' MMMM', ${t(
31 | 'at',
32 | )} ' HH:mm'h'`;
33 |
34 | const formattedDate = format(dateAt, formatPattern);
35 |
36 | return (
37 | <>
38 |
39 |
40 |
41 | {formattedDate}
42 |
43 |
44 |
51 | >
52 | );
53 | };
54 |
55 | export default EntryDate;
56 |
--------------------------------------------------------------------------------
/src/screens/AddEntry/components/EntryDate/__tests__/EntryDate.test.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/unbound-method */
2 | /* eslint-disable @typescript-eslint/no-explicit-any */
3 | import React from 'react';
4 | import {render} from 'utils/test-utils';
5 | import EntryDate from '../EntryDate';
6 |
7 | jest.mock('react-i18next', () => ({
8 | useTranslation: () => ({t: (key: any) => key}),
9 | }));
10 |
11 | describe('EntryDate', () => {
12 | it('Test match snapshot EntryDate', () => {
13 | // given
14 | const props = {};
15 |
16 | // when
17 | const {toJSON} = render();
18 |
19 | // then
20 | expect(toJSON()).toMatchSnapshot();
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/src/screens/AddEntry/components/EntryDate/index.ts:
--------------------------------------------------------------------------------
1 | export {default} from './EntryDate';
2 |
--------------------------------------------------------------------------------
/src/screens/AddEntry/components/EntryDate/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 |
3 | import {Text, TouchableOpacity} from 'styles/layout';
4 |
5 | export const Label = styled(Text)`
6 | color: ${({theme}) => theme.colors.white}50;
7 | font-size: ${({theme}) => theme.fontSizes.small};
8 | `;
9 |
10 | export const Touchable = styled(TouchableOpacity)``;
11 |
12 | export const Title = styled(Label)`
13 | color: ${({theme}) => theme.colors.white};
14 | font-size: ${({theme}) => theme.fontSizes.small};
15 | font-weight: ${({theme}) => theme.fontWeight.bold};
16 | `;
17 |
--------------------------------------------------------------------------------
/src/screens/AddEntry/components/EntryDescription/EntryDescription.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {useTranslation} from 'react-i18next';
3 | import {Row} from 'components';
4 |
5 | import {Label, Input} from './styles';
6 | import {useEntry} from 'hooks/useEntry';
7 |
8 | const EntryDescription = (): JSX.Element => {
9 | const {description, setDescription} = useEntry();
10 | const {t} = useTranslation('add');
11 |
12 | return (
13 |
14 |
15 |
20 |
21 | );
22 | };
23 |
24 | export default EntryDescription;
25 |
--------------------------------------------------------------------------------
/src/screens/AddEntry/components/EntryDescription/__tests__/EntryDescription.test.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/unbound-method */
2 | /* eslint-disable @typescript-eslint/no-explicit-any */
3 | import React from 'react';
4 | import {render} from 'utils/test-utils';
5 | import EntryDescription from '../EntryDescription';
6 |
7 | jest.mock('react-i18next', () => ({
8 | useTranslation: () => ({t: (key: any) => key}),
9 | }));
10 |
11 | describe('EntryDescription', () => {
12 | it('Test match snapshot EntryDescription', () => {
13 | // given
14 | const props = {};
15 |
16 | // when
17 | const {toJSON} = render();
18 |
19 | // then
20 | expect(toJSON()).toMatchSnapshot();
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/src/screens/AddEntry/components/EntryDescription/__tests__/__snapshots__/EntryDescription.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`EntryDescription Test match snapshot EntryDescription 1`] = `
4 |
21 |
30 |
42 | description
43 |
44 |
60 |
61 |
62 | `;
63 |
--------------------------------------------------------------------------------
/src/screens/AddEntry/components/EntryDescription/index.ts:
--------------------------------------------------------------------------------
1 | export {default} from './EntryDescription';
2 |
--------------------------------------------------------------------------------
/src/screens/AddEntry/components/EntryDescription/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 |
3 | import Colors from 'styles/colors';
4 | import {Text} from 'styles/layout';
5 |
6 | export const Label = styled(Text)`
7 | color: ${({theme}) => theme.colors.white}50;
8 | font-size: ${({theme}) => theme.fontSizes.small};
9 | `;
10 |
11 | export const Input = styled.TextInput.attrs({
12 | numberOfLines: 1,
13 | placeholderTextColor: Colors.grayLight,
14 | blurOnSubmit: true,
15 | })`
16 | font-family: ${({theme}) => theme.fontFamily.regular};
17 | font-size: ${({theme}) => theme.fontSizes.small};
18 | color: ${({theme}) => theme.colors.grayLight};
19 | font-weight: ${({theme}) => theme.fontWeight.bold};
20 | `;
21 |
--------------------------------------------------------------------------------
/src/screens/AddEntry/components/MenuEntryType/MenuEntryType.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import {useTranslation} from 'react-i18next';
4 | import {useEntry} from 'hooks/useEntry';
5 | import {Container, Touchable, Label, LineActive} from './styles';
6 |
7 | const MenuEntryType = (): JSX.Element => {
8 | const {entryType, setEntryType} = useEntry();
9 |
10 | const {t} = useTranslation('add');
11 |
12 | const isExpense = entryType === 'expense';
13 | const isEarning = entryType === 'earning';
14 |
15 | return (
16 |
17 | setEntryType('expense')}>
18 |
19 |
20 |
21 |
22 | setEntryType('earning')}>
23 |
24 |
25 |
26 |
27 | );
28 | };
29 |
30 | export default MenuEntryType;
31 |
--------------------------------------------------------------------------------
/src/screens/AddEntry/components/MenuEntryType/__tests__/MenuEntryType.test.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/unbound-method */
2 | /* eslint-disable @typescript-eslint/no-explicit-any */
3 | import React from 'react';
4 | import {render} from 'utils/test-utils';
5 | import MenuEntryType from '../MenuEntryType';
6 |
7 | jest.mock('react-i18next', () => ({
8 | useTranslation: () => ({t: (key: any) => key}),
9 | }));
10 |
11 | describe('MenuEntryType', () => {
12 | it('Test match snapshot MenuEntryType', () => {
13 | // given
14 | const props = {};
15 |
16 | // when
17 | const {toJSON} = render();
18 |
19 | // then
20 | expect(toJSON()).toMatchSnapshot();
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/src/screens/AddEntry/components/MenuEntryType/index.ts:
--------------------------------------------------------------------------------
1 | export {default} from './MenuEntryType';
2 |
--------------------------------------------------------------------------------
/src/screens/AddEntry/components/MenuEntryType/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 | import {TouchableOpacity, Text} from 'styles/layout';
3 |
4 | export const Container = styled.View`
5 | flex-direction: row;
6 | padding-top: ${({theme}) => theme.spacing.small}px;
7 | `;
8 |
9 | export const Touchable = styled(TouchableOpacity)`
10 | margin: 0px ${({theme}) => theme.spacing.great}px
11 | ${({theme}) => theme.spacing.tall}px;
12 | `;
13 |
14 | export const LineActive = styled.View<{active: boolean}>`
15 | height: 3px;
16 | width: 30px;
17 | align-self: center;
18 | border-radius: 2px;
19 | margin: 8px 0px 10px;
20 | background: ${({theme, active = false}) =>
21 | active ? theme.colors.white : 'transparent'};
22 | `;
23 |
24 | export const Label = styled(Text)<{active: boolean}>`
25 | font-family: ${({theme}) => theme.fontFamily.regular};
26 | font-size: ${({theme}) => theme.fontSizes.medium};
27 | font-weight: ${({theme}) => theme.fontWeight.bold};
28 | color: ${({theme, active = false}) =>
29 | active ? theme.colors.white : theme.colors.gray};
30 | `;
31 |
--------------------------------------------------------------------------------
/src/screens/AddEntry/components/index.ts:
--------------------------------------------------------------------------------
1 | export {default as MenuEntryType} from './MenuEntryType';
2 | export {default as EntryDescription} from './EntryDescription';
3 | export {default as EntryCategory} from './EntryCategory';
4 | export {default as EntryDate} from './EntryDate';
5 |
--------------------------------------------------------------------------------
/src/screens/AddEntry/index.ts:
--------------------------------------------------------------------------------
1 | export {default} from './AddEntryScreen';
2 |
--------------------------------------------------------------------------------
/src/screens/AddEntry/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 |
3 | export const Screen = styled.TouchableOpacity.attrs({
4 | activeOpacity: 1,
5 | })`
6 | flex: 1;
7 | background: ${({theme}) => theme.colors.accent};
8 | `;
9 |
10 | export const Container = styled.View`
11 | flex: 1;
12 | align-items: center;
13 | `;
14 |
15 | export const Content = styled.View`
16 | flex: 1;
17 | width: 100%;
18 | margin-top: ${({theme}) => theme.spacing.great}px;
19 | padding-top: ${({theme}) => theme.spacing.great}px;
20 | background: ${({theme}) => theme.colors.gray};
21 | border-top-right-radius: ${({theme}) => theme.spacing.great}px;
22 | border-top-left-radius: ${({theme}) => theme.spacing.great}px;
23 | `;
24 |
25 | export const SubmitContainer = styled.View`
26 | width: 100%;
27 | padding: ${({theme}) => theme.spacing.tall}px
28 | ${({theme}) => theme.spacing.venti}px;
29 | `;
30 |
--------------------------------------------------------------------------------
/src/screens/Budget/BudgetScreen.tsx:
--------------------------------------------------------------------------------
1 | import {useNavigation} from '@react-navigation/native';
2 | import React from 'react';
3 | import {useTranslation} from 'react-i18next';
4 | import BudgetList from './components/BudgetList/BudgetList';
5 |
6 | import {AddBudgetNavigationProp} from 'routes/StacksRoute';
7 | import {Container, Header, Title, Touchable, AddIcon} from './styles';
8 |
9 | const BudgetScreen = (): JSX.Element => {
10 | const navigation = useNavigation();
11 | const {t} = useTranslation('budget');
12 |
13 | const addBudget = () => {
14 | navigation.navigate('AddBudgetStack', {
15 | screen: 'AddBudget',
16 | });
17 | };
18 |
19 | return (
20 |
21 |
22 | {t('title')}
23 |
24 |
25 |
26 |
27 |
28 |
29 | );
30 | };
31 |
32 | export default BudgetScreen;
33 |
--------------------------------------------------------------------------------
/src/screens/Budget/components/BudgetList/BudgetList.tsx:
--------------------------------------------------------------------------------
1 | import React, {useCallback} from 'react';
2 | import withObservables from '@nozbe/with-observables';
3 |
4 | import {useBudgetContext} from 'contexts/BudgetContext';
5 |
6 | import {Budget} from 'models';
7 | import {BudgetRepository} from 'repositories';
8 |
9 | import BudgetItem from './BudgetItem';
10 | import ListEmpty from '../ListEmpty';
11 | import {Container, List} from './styles';
12 |
13 | interface BudgetListProps {
14 | budgets: Budget[];
15 | }
16 |
17 | const BudgetList = ({budgets = []}: BudgetListProps): JSX.Element => {
18 | const {removeBudget} = useBudgetContext();
19 |
20 | const listKeyExtractor = useCallback(item => item.id, []);
21 | const renderItem = useCallback(
22 | ({item}) => ,
23 | [removeBudget],
24 | );
25 |
26 | return (
27 |
28 | {!budgets.length && }
29 |
30 |
36 |
37 | );
38 | };
39 |
40 | const enhance = withObservables([], () => ({
41 | budgets: BudgetRepository.getBudgetsObserve(),
42 | }));
43 |
44 | export default enhance(BudgetList);
45 |
--------------------------------------------------------------------------------
/src/screens/Budget/components/BudgetList/index.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eufelipe/log-finance-react-native/5e725c09be4f962bfbdc21b18aa42777db43a1f5/src/screens/Budget/components/BudgetList/index.ts
--------------------------------------------------------------------------------
/src/screens/Budget/components/BudgetList/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 | import {Text} from 'styles/layout';
3 |
4 | export const Container = styled.View`
5 | flex: 1;
6 | `;
7 |
8 | export const List = styled.FlatList`
9 | margin-top: ${({theme}) => theme.spacing.tall}px;
10 | `;
11 |
12 | export const Title = styled(Text)`
13 | font-family: ${({theme}) => theme.fontFamily.regular};
14 | font-size: ${({theme}) => theme.fontSizes.medium};
15 | font-weight: ${({theme}) => theme.fontWeight.regular};
16 | color: ${({theme}) => theme.colors.white};
17 | `;
18 |
19 | export const Value = styled(Title)`
20 | font-size: ${({theme}) => theme.fontSizes.medium};
21 | font-weight: ${({theme}) => theme.fontWeight.bold};
22 | `;
23 |
24 | export const Card = styled.View<{color?: string}>`
25 | background: ${({theme}) => theme.colors.primary};
26 | margin: ${({theme}) => theme.spacing.small}px
27 | ${({theme}) => theme.spacing.tall}px;
28 | padding: ${({theme}) => theme.spacing.great}px;
29 | height: 110px;
30 | border-radius: ${({theme}) => theme.spacing.great}px;
31 | justify-content: center;
32 | `;
33 |
34 | export const CardBody = styled.View`
35 | flex-direction: row;
36 | `;
37 | export const CardRow = styled.View`
38 | padding: 10px;
39 | `;
40 |
--------------------------------------------------------------------------------
/src/screens/Budget/components/ListEmpty/LICENSE.md:
--------------------------------------------------------------------------------
1 | Eduardo Henrique Costa
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/screens/Budget/components/ListEmpty/ListEmpty.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {useTranslation} from 'react-i18next';
3 |
4 | import {Container, EmptyAnimation, Title, SubTitle} from './styles';
5 |
6 | import EMPTY from './empty.json';
7 |
8 | const ListEmpty = (): JSX.Element => {
9 | const {t} = useTranslation('budget');
10 |
11 | return (
12 |
13 |
14 |
15 | {t('no-entries')}
16 | {t('message-add-new')}
17 |
18 | );
19 | };
20 |
21 | export default ListEmpty;
22 |
--------------------------------------------------------------------------------
/src/screens/Budget/components/ListEmpty/__tests__/ListEmpty.test.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/unbound-method */
2 | /* eslint-disable @typescript-eslint/no-explicit-any */
3 | import React from 'react';
4 | import {render} from 'utils/test-utils';
5 | import ListEmpty from '../ListEmpty';
6 |
7 | jest.mock('react-i18next', () => ({
8 | useTranslation: () => ({t: (key: any) => key}),
9 | }));
10 |
11 | describe('ListEmpty', () => {
12 | it('Test match snapshot ListEmpty', () => {
13 | // given
14 | const props = {};
15 |
16 | // when
17 | const {toJSON} = render();
18 |
19 | // then
20 | expect(toJSON()).toMatchSnapshot();
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/src/screens/Budget/components/ListEmpty/index.ts:
--------------------------------------------------------------------------------
1 | export {default} from './ListEmpty';
2 |
--------------------------------------------------------------------------------
/src/screens/Budget/components/ListEmpty/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 | import LottieView from 'lottie-react-native';
3 | import {Text} from 'styles/layout';
4 |
5 | export const Container = styled.View`
6 | justify-content: center;
7 | align-items: center;
8 | margin: 30px 20px;
9 | `;
10 |
11 | export const EmptyAnimation = styled(LottieView).attrs({
12 | autoPlay: true,
13 | loop: true,
14 | })`
15 | width: 300px;
16 | height: 300px;
17 | `;
18 |
19 | export const Title = styled(Text)`
20 | text-align: center;
21 | margin: ${({theme}) => theme.spacing.venti}px 0px;
22 | font-size: ${({theme}) => theme.fontSizes.large};
23 | font-weight: ${({theme}) => theme.fontWeight.bold};
24 | color: ${({theme}) => theme.colors.primary};
25 | `;
26 | export const SubTitle = styled(Text)`
27 | text-align: center;
28 | color: ${({theme}) => theme.colors.gray};
29 | font-size: ${({theme}) => theme.fontSizes.small};
30 | `;
31 |
--------------------------------------------------------------------------------
/src/screens/Budget/index.tsx:
--------------------------------------------------------------------------------
1 | export {default} from './BudgetScreen';
2 |
--------------------------------------------------------------------------------
/src/screens/Budget/styles.ts:
--------------------------------------------------------------------------------
1 | import {getStatusBarHeight} from 'react-native-iphone-x-helper';
2 | import styled from 'styled-components/native';
3 | import {Text, TouchableOpacity} from 'styles/layout';
4 |
5 | import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
6 | import Colors from 'styles/colors';
7 |
8 | export const Container = styled.View`
9 | flex: 1;
10 | `;
11 |
12 | export const Title = styled(Text)`
13 | font-size: ${({theme}) => theme.fontSizes.large};
14 | color: ${({theme}) => theme.colors.white};
15 | `;
16 |
17 | export const Header = styled.View`
18 | align-items: center;
19 | justify-content: space-between;
20 | flex-direction: row;
21 | background: ${({theme}) => theme.colors.accent};
22 | padding: 0px ${({theme}) => theme.spacing.tall}px;
23 | padding-top: ${({theme}) => theme.spacing.tall + getStatusBarHeight()}px;
24 | padding-bottom: ${({theme}) => theme.spacing.great}px;
25 | `;
26 |
27 | export const Touchable = styled(TouchableOpacity)``;
28 |
29 | export const AddIcon = styled(Icon).attrs({
30 | name: 'plus',
31 | color: `${Colors.white}`,
32 | size: 32,
33 | })``;
34 |
--------------------------------------------------------------------------------
/src/screens/Categories/CategoriesScreen.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {RouteProp, useRoute} from '@react-navigation/native';
3 | import {Container, Loader} from './styles';
4 | import {CategoryList} from './components';
5 | import {Category} from 'models';
6 |
7 | import {StackParamList} from 'routes/StacksRoute';
8 |
9 | interface CategoriesScreenProps {
10 | categories?: Category[];
11 | }
12 |
13 | const CategoriesScreen = ({categories}: CategoriesScreenProps): JSX.Element => {
14 | const route = useRoute>();
15 |
16 | if (!route.params?.setCategory) {
17 | return ;
18 | }
19 |
20 | return (
21 |
22 |
26 |
27 | );
28 | };
29 |
30 | export default CategoriesScreen;
31 |
--------------------------------------------------------------------------------
/src/screens/Categories/__tests__/CategoriesScreen.test.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-explicit-any */
2 | /* eslint-disable @typescript-eslint/unbound-method */
3 | import React from 'react';
4 | import {render} from 'utils/test-utils';
5 |
6 | import CategoriesScreen from '../CategoriesScreen';
7 |
8 | jest.mock('react-i18next', () => ({
9 | useTranslation: () => ({t: (key: any) => key}),
10 | }));
11 |
12 | describe('CategoriesScreen', () => {
13 | it('Test match snapshot CategoriesScreen', () => {
14 | // given
15 | const props = {};
16 |
17 | // when
18 | const {toJSON} = render();
19 |
20 | // then
21 | expect(toJSON()).toMatchSnapshot();
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/src/screens/Categories/components/CategoryItem/CategoryItem.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {useTranslation} from 'react-i18next';
3 | import {CategoryIcon} from 'components';
4 |
5 | import {Container, Title} from './styles';
6 | import {Category} from 'models';
7 |
8 | interface Props {
9 | category: Category;
10 | onPressCategory: (category: Category) => void;
11 | }
12 |
13 | const CategoryItem = ({category, onPressCategory}: Props): JSX.Element => {
14 | const {t} = useTranslation('categories');
15 |
16 | const categoryKey = category.key;
17 | return (
18 | onPressCategory(category)}
20 | testID="category-item-button"
21 | accessible={true}
22 | accessibilityLabel={`${t('accessibility-button-label')} ${t(
23 | categoryKey,
24 | )}`}
25 | accessibilityHint={`${t('accessibility-button-hint')} ${t(categoryKey)}`}
26 | >
27 |
28 | {t(categoryKey)}
29 |
30 | );
31 | };
32 |
33 | export default CategoryItem;
34 |
--------------------------------------------------------------------------------
/src/screens/Categories/components/CategoryItem/index.ts:
--------------------------------------------------------------------------------
1 | export {default} from './CategoryItem';
2 |
--------------------------------------------------------------------------------
/src/screens/Categories/components/CategoryItem/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 | import {TouchableOpacity, Text} from 'styles/layout';
3 |
4 | export const Container = styled(TouchableOpacity)`
5 | margin: ${({theme}) => theme.spacing.great}px
6 | ${({theme}) => theme.spacing.small}px;
7 | align-items: center;
8 | flex-grow: 1;
9 | flex-basis: 0;
10 | `;
11 |
12 | export const Title = styled(Text)`
13 | font-size: ${({theme}) => theme.fontSizes.small};
14 | color: ${({theme}) => theme.colors.white};
15 | padding-top: ${({theme}) => theme.spacing.small}px;
16 | `;
17 |
--------------------------------------------------------------------------------
/src/screens/Categories/components/CategoryList/index.ts:
--------------------------------------------------------------------------------
1 | export {default} from './CategoryList';
2 |
--------------------------------------------------------------------------------
/src/screens/Categories/components/CategoryList/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 |
3 | export const FlatList = styled.FlatList.attrs({
4 | numColumns: 3,
5 | columnWrapperStyle: {
6 | flex: 1,
7 | justifyContent: 'space-between',
8 | },
9 | })``;
10 |
--------------------------------------------------------------------------------
/src/screens/Categories/components/index.ts:
--------------------------------------------------------------------------------
1 | export {default as CategoryList} from './CategoryList';
2 | export {default as CategoryItem} from './CategoryItem';
3 |
--------------------------------------------------------------------------------
/src/screens/Categories/index.ts:
--------------------------------------------------------------------------------
1 | export {default} from './CategoriesScreen';
2 |
--------------------------------------------------------------------------------
/src/screens/Categories/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 |
3 | export const Container = styled.View`
4 | flex: 1;
5 | background: ${({theme}) => theme.colors.primary};
6 | `;
7 |
8 | export const Loader = styled.ActivityIndicator`
9 | margin-top: 20px;
10 | `;
11 |
--------------------------------------------------------------------------------
/src/screens/Currency/index.tsx:
--------------------------------------------------------------------------------
1 | export {default} from './CurrencyScreen';
2 |
--------------------------------------------------------------------------------
/src/screens/Currency/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 |
3 | export const Container = styled.View`
4 | flex: 1;
5 | `;
6 |
7 | export const Title = styled.Text``;
8 |
--------------------------------------------------------------------------------
/src/screens/Historic/components/HistoricList/HistoricHeader.tsx:
--------------------------------------------------------------------------------
1 | import {parseISO, format, isYesterday, isTomorrow} from 'date-fns';
2 | import {isToday} from 'date-fns/esm';
3 | import React from 'react';
4 | import {useTranslation} from 'react-i18next';
5 | import {DATE_FORMAT_BR} from 'utils/dates';
6 |
7 | import {Headering} from './styles';
8 |
9 | interface HistoricHeaderProps {
10 | title: string;
11 | }
12 |
13 | const HistoricHeader = ({title}: HistoricHeaderProps): JSX.Element => {
14 | const {t} = useTranslation('historic');
15 |
16 | const date = parseISO(title);
17 |
18 | const today = isToday(date);
19 | const yerterday = isYesterday(date);
20 | const tomorow = isTomorrow(date);
21 |
22 | let prefix = 'EEEE';
23 |
24 | if (today) prefix = `'${t('today')}'`;
25 | else if (yerterday) prefix = `'${t('yerterday')}'`;
26 | else if (tomorow) prefix = `'${t('tomorow')}'`;
27 |
28 | const formated = format(date, `${prefix}, ${DATE_FORMAT_BR}`);
29 |
30 | return {formated};
31 | };
32 |
33 | export default HistoricHeader;
34 |
--------------------------------------------------------------------------------
/src/screens/Historic/components/HistoricList/HistoricItem.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import withObservables from '@nozbe/with-observables';
3 | import {CategoryIcon, Currency} from 'components';
4 | import {Category, Entry} from 'models';
5 |
6 | import {getTimeFriendly} from 'utils/dates';
7 | import {
8 | Container,
9 | Content,
10 | CategoryContainer,
11 | ValueContainer,
12 | Value,
13 | Title,
14 | Time,
15 | Line,
16 | } from './styles';
17 |
18 | interface HistoricItemProps {
19 | entry: Entry;
20 | category?: Category;
21 | }
22 |
23 | const HistoricItem = ({entry, category}: HistoricItemProps): JSX.Element => {
24 | const {description, value, type} = entry;
25 | const isExpense = type === 'expense';
26 |
27 | return (
28 |
29 | {category && (
30 | <>
31 |
32 |
33 |
34 |
35 | >
36 | )}
37 |
38 |
39 | {description && {description}}
40 | {category?.description}
41 |
42 |
43 |
44 |
45 | {text}}
48 | />
49 |
50 |
51 | );
52 | };
53 |
54 | const enhance = withObservables(
55 | ['entry'],
56 | ({entry}: HistoricItemProps) => ({
57 | category: entry.category.observe(),
58 | }),
59 | );
60 |
61 | export default enhance(HistoricItem);
62 |
--------------------------------------------------------------------------------
/src/screens/Historic/components/HistoricList/HistoricList.tsx:
--------------------------------------------------------------------------------
1 | import React, {useCallback} from 'react';
2 |
3 | import {SectionList} from './styles';
4 | import Item from './HistoricItem';
5 |
6 | import {HistoricSection} from 'screens/Historic/HistoricScreen';
7 |
8 | import HistoricHeader from './HistoricHeader';
9 | import {SectionListData} from 'react-native';
10 |
11 | interface Props {
12 | historic: HistoricSection[];
13 | }
14 |
15 | const HistoricList = ({historic = []}: Props): JSX.Element => {
16 | const listKeyExtractor = useCallback(item => item.id, []);
17 | const renderItem = useCallback(({item}) => , []);
18 |
19 | const renderSectionHeader = ({
20 | section,
21 | }: {
22 | section: SectionListData;
23 | }): JSX.Element | null => ;
24 |
25 | return (
26 |
27 | testID="historic-list-items"
28 | sections={historic}
29 | keyExtractor={listKeyExtractor}
30 | renderItem={renderItem}
31 | renderSectionHeader={renderSectionHeader}
32 | />
33 | );
34 | };
35 |
36 | export default HistoricList;
37 |
--------------------------------------------------------------------------------
/src/screens/Historic/components/HistoricList/index.ts:
--------------------------------------------------------------------------------
1 | export {default} from './HistoricList';
2 |
--------------------------------------------------------------------------------
/src/screens/Historic/components/ListEmpty/LICENSE.md:
--------------------------------------------------------------------------------
1 | musa adanur
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/screens/Historic/components/ListEmpty/ListEmpty.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {useTranslation} from 'react-i18next';
3 |
4 | import {Container, EmptyAnimation, Title, SubTitle} from './styles';
5 |
6 | import EMPTY from './empty.json';
7 |
8 | const ListEmpty = (): JSX.Element => {
9 | const {t} = useTranslation('historic');
10 |
11 | return (
12 |
13 |
14 |
15 | {t('no-entries')}
16 | {t('message-add-new')}
17 |
18 | );
19 | };
20 |
21 | export default ListEmpty;
22 |
--------------------------------------------------------------------------------
/src/screens/Historic/components/ListEmpty/index.ts:
--------------------------------------------------------------------------------
1 | export {default} from './ListEmpty';
2 |
--------------------------------------------------------------------------------
/src/screens/Historic/components/ListEmpty/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 | import LottieView from 'lottie-react-native';
3 | import {Text} from 'styles/layout';
4 |
5 | export const Container = styled.View`
6 | justify-content: center;
7 | align-items: center;
8 | margin-top: 30px;
9 | `;
10 |
11 | export const EmptyAnimation = styled(LottieView).attrs({
12 | autoPlay: true,
13 | loop: true,
14 | })`
15 | width: 350px;
16 | height: 153px;
17 | margin-top: ${({theme}) => theme.spacing.small}px;
18 | `;
19 |
20 | export const Title = styled(Text)`
21 | text-align: center;
22 | margin: ${({theme}) => theme.spacing.venti}px 0px;
23 | font-size: ${({theme}) => theme.fontSizes.large};
24 | font-weight: ${({theme}) => theme.fontWeight.bold};
25 | color: ${({theme}) => theme.colors.primary};
26 | `;
27 | export const SubTitle = styled(Text)`
28 | text-align: center;
29 | color: ${({theme}) => theme.colors.gray};
30 | font-size: ${({theme}) => theme.fontSizes.small};
31 | `;
32 |
--------------------------------------------------------------------------------
/src/screens/Historic/index.tsx:
--------------------------------------------------------------------------------
1 | export {default} from './HistoricScreen';
2 |
--------------------------------------------------------------------------------
/src/screens/Historic/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 | import {getStatusBarHeight} from 'react-native-iphone-x-helper';
3 |
4 | import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
5 | import {Text, TouchableOpacity, Colors} from 'styles/layout';
6 |
7 | export const Container = styled.View`
8 | flex: 1;
9 | background: ${({theme}) => theme.colors.grayLight};
10 | `;
11 |
12 | export const Loader = styled.ActivityIndicator`
13 | margin-top: 20px;
14 | `;
15 |
16 | export const Header = styled.View`
17 | width: 100%;
18 | height: 150px;
19 | align-items: center;
20 | padding-top: ${({theme}) => theme.spacing.tall + getStatusBarHeight()}px;
21 | background: ${({theme}) => theme.colors.accent};
22 | `;
23 |
24 | export const PeriodNavigation = styled.View`
25 | flex-direction: row;
26 | `;
27 |
28 | export const Title = styled(Text)`
29 | font-size: ${({theme}) => theme.fontSizes.large};
30 | color: ${({theme}) => theme.colors.white};
31 | margin-bottom: ${({theme}) => theme.spacing.tall}px;
32 | `;
33 |
34 | export const Touchable = styled(TouchableOpacity)`
35 | margin: 0px ${({theme}) => theme.spacing.great}px
36 | ${({theme}) => theme.spacing.tall}px;
37 | `;
38 |
39 | export const Label = styled(Text)`
40 | font-family: ${({theme}) => theme.fontFamily.regular};
41 | font-size: ${({theme}) => theme.fontSizes.medium};
42 | font-weight: ${({theme}) => theme.fontWeight.regular};
43 | color: ${({theme}) => theme.colors.grayLight};
44 | `;
45 |
46 | export const BaseIcon = styled(MaterialIcons).attrs({
47 | size: 24,
48 | color: Colors.grayLight,
49 | })``;
50 |
51 | export const LeftIcon = styled(BaseIcon).attrs({
52 | name: 'keyboard-arrow-left',
53 | })``;
54 |
55 | export const RightIcon = styled(BaseIcon).attrs({
56 | name: 'keyboard-arrow-right',
57 | })``;
58 |
--------------------------------------------------------------------------------
/src/screens/Home/HomeScreen.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import withObservables from '@nozbe/with-observables';
3 |
4 | import {Container, Content, Salutation} from './styles';
5 | import {useTranslation} from 'react-i18next';
6 |
7 | import {Header, ListEmpty} from './components';
8 | import {EntriesList} from 'components';
9 |
10 | import {Entry} from 'models';
11 | import EntryRepository from 'repositories/EntryRepository';
12 |
13 | interface HomeScreenProps {
14 | entries: Entry[];
15 | }
16 |
17 | const HomeScreen = ({entries = []}: HomeScreenProps): JSX.Element => {
18 | const {t} = useTranslation('home');
19 |
20 | return (
21 |
22 |
23 |
24 | {t('today')}
25 | {!!entries.length && }
26 |
27 | {!entries.length && }
28 |
29 |
30 | );
31 | };
32 |
33 | const enhance = withObservables(['entries'], () => ({
34 | entries: EntryRepository.getTodayEntries(),
35 | }));
36 |
37 | export default enhance(HomeScreen);
38 |
--------------------------------------------------------------------------------
/src/screens/Home/__tests__/HomeScreen.test.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-explicit-any */
2 | /* eslint-disable @typescript-eslint/unbound-method */
3 | import React from 'react';
4 | import {render} from 'utils/test-utils';
5 |
6 | import HomeScreen from '../HomeScreen';
7 |
8 | jest.runAllTimers();
9 |
10 | describe('HomeScreen', () => {
11 | it('Test match snapshot HomeScreen', () => {
12 | // given
13 | const props = {};
14 |
15 | // when
16 | const {toJSON} = render();
17 |
18 | // then
19 | expect(toJSON()).toMatchSnapshot();
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/src/screens/Home/components/Header/Header.tsx:
--------------------------------------------------------------------------------
1 | import React, {useCallback, useState} from 'react';
2 | import {useFocusEffect, useNavigation} from '@react-navigation/native';
3 | import {SettingsNavigationProp} from 'routes/StacksRoute';
4 | import {useTranslation} from 'react-i18next';
5 |
6 | import {Currency} from 'components';
7 | import {Entry} from 'models';
8 |
9 | import {
10 | Container,
11 | Content,
12 | Title,
13 | Description,
14 | SettingTouchable,
15 | SettingIcon,
16 | } from './styles';
17 | import {useEntry} from 'hooks/useEntry';
18 |
19 | interface HeaderProps {
20 | entries?: Entry[];
21 | }
22 |
23 | const Header = ({entries = []}: HeaderProps): JSX.Element => {
24 | const navigation = useNavigation();
25 | const {t} = useTranslation('home');
26 | const [balance, setBalance] = useState(0);
27 |
28 | const {calculateCurrentBalance} = useEntry();
29 |
30 | const handleSettings = () => navigation.navigate('Settings');
31 |
32 | useFocusEffect(
33 | useCallback(() => {
34 | const sumValues = calculateCurrentBalance(entries);
35 | setBalance(sumValues);
36 | }, [calculateCurrentBalance, entries]),
37 | );
38 |
39 | return (
40 |
41 |
42 | {t('balance')}
43 | {value}} />
44 |
45 |
46 |
47 |
48 |
49 | );
50 | };
51 |
52 | export default Header;
53 |
--------------------------------------------------------------------------------
/src/screens/Home/components/Header/__tests__/Header.test.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-explicit-any */
2 | /* eslint-disable @typescript-eslint/unbound-method */
3 | import React from 'react';
4 | import {render} from 'utils/test-utils';
5 |
6 | import Header from '../Header';
7 |
8 | jest.mock('react-i18next', () => ({
9 | useTranslation: () => ({t: (key: any) => key}),
10 | }));
11 |
12 | describe('Header', () => {
13 | it('Test match snapshot Header', () => {
14 | // given
15 | const props = {};
16 |
17 | // when
18 | const {toJSON} = render();
19 |
20 | // then
21 | expect(toJSON()).toMatchSnapshot();
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/src/screens/Home/components/Header/index.ts:
--------------------------------------------------------------------------------
1 | export {default} from './Header';
2 |
--------------------------------------------------------------------------------
/src/screens/Home/components/Header/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 | import {getStatusBarHeight} from 'react-native-iphone-x-helper';
3 | import {Text, TouchableOpacity} from 'styles/layout';
4 |
5 | import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
6 | import Colors from 'styles/colors';
7 |
8 | export const Container = styled.View`
9 | background: ${({theme}) => theme.colors.primary};
10 | padding: 0px ${({theme}) => theme.spacing.great}px;
11 | padding-top: ${({theme}) => theme.spacing.tall + getStatusBarHeight()}px;
12 | justify-content: space-between;
13 | align-items: center;
14 | height: 150px;
15 | flex-direction: row;
16 | `;
17 |
18 | export const Content = styled.View``;
19 |
20 | export const Title = styled(Text)`
21 | font-size: ${({theme}) => theme.fontSizes.big};
22 | font-weight: ${({theme}) => theme.fontWeight.bold};
23 | font-family: ${({theme}) => theme.fontFamily.semiBold};
24 | color: ${({theme}) => theme.colors.white};
25 | `;
26 |
27 | export const Description = styled(Text)`
28 | font-size: ${({theme}) => theme.fontSizes.small};
29 | color: ${({theme}) => theme.colors.white};
30 | `;
31 |
32 | export const SettingTouchable = styled(TouchableOpacity).attrs({})`
33 | padding: 10px;
34 | `;
35 |
36 | export const SettingIcon = styled(MaterialIcons).attrs({
37 | size: 28,
38 | name: 'settings',
39 | backgroundColor: 'transparent',
40 | color: Colors.white,
41 | })`
42 | padding-right: 0px;
43 | `;
44 |
--------------------------------------------------------------------------------
/src/screens/Home/components/ListEmpty/LICENSE.md:
--------------------------------------------------------------------------------
1 | Muhammad Hazwan Bin Mohd Hanizul
2 |
3 | https://lottiefiles.com/user/611359
4 |
5 | https://lottiefiles.com/66934-tumbleweed-rolling
6 |
--------------------------------------------------------------------------------
/src/screens/Home/components/ListEmpty/ListEmpty.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {useTranslation} from 'react-i18next';
3 |
4 | import {Container, EmptyAnimation, Title, SubTitle} from './styles';
5 |
6 | import EMPTY from './empty.json';
7 |
8 | const ListEmpty = (): JSX.Element => {
9 | const {t} = useTranslation('home');
10 |
11 | return (
12 |
13 |
14 |
15 | {t('no-entries')}
16 | {t('message-add-new')}
17 |
18 | );
19 | };
20 |
21 | export default ListEmpty;
22 |
--------------------------------------------------------------------------------
/src/screens/Home/components/ListEmpty/__tests__/ListEmpty.test.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-explicit-any */
2 | /* eslint-disable @typescript-eslint/unbound-method */
3 | import React from 'react';
4 | import {render} from 'utils/test-utils';
5 |
6 | import ListEmpty from '../ListEmpty';
7 |
8 | jest.mock('react-i18next', () => ({
9 | useTranslation: () => ({t: (key: any) => key}),
10 | }));
11 |
12 | describe('ListEmpty', () => {
13 | it('Test match snapshot ListEmpty', () => {
14 | // given
15 | const props = {};
16 |
17 | // when
18 | const {toJSON} = render();
19 |
20 | // then
21 | expect(toJSON()).toMatchSnapshot();
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/src/screens/Home/components/ListEmpty/index.ts:
--------------------------------------------------------------------------------
1 | export {default} from './ListEmpty';
2 |
--------------------------------------------------------------------------------
/src/screens/Home/components/ListEmpty/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 | import LottieView from 'lottie-react-native';
3 | import {Text} from 'styles/layout';
4 |
5 | export const Container = styled.View`
6 | justify-content: center;
7 | align-items: center;
8 | `;
9 |
10 | export const EmptyAnimation = styled(LottieView).attrs({
11 | autoPlay: true,
12 | loop: true,
13 | })`
14 | width: 350px;
15 | height: 153px;
16 | margin-top: ${({theme}) => theme.spacing.small}px;
17 | `;
18 |
19 | export const Title = styled(Text)`
20 | text-align: center;
21 | margin: ${({theme}) => theme.spacing.tall}px 0px;
22 | font-size: ${({theme}) => theme.fontSizes.large};
23 | font-weight: ${({theme}) => theme.fontWeight.bold};
24 | color: ${({theme}) => theme.colors.primary};
25 | `;
26 | export const SubTitle = styled(Text)`
27 | text-align: center;
28 | color: ${({theme}) => theme.colors.gray};
29 | font-size: ${({theme}) => theme.fontSizes.small};
30 | `;
31 |
--------------------------------------------------------------------------------
/src/screens/Home/components/index.ts:
--------------------------------------------------------------------------------
1 | export {default as Header} from './Header';
2 | export {default as ListEmpty} from './ListEmpty';
3 |
--------------------------------------------------------------------------------
/src/screens/Home/index.ts:
--------------------------------------------------------------------------------
1 | export {default} from './HomeScreen';
2 |
--------------------------------------------------------------------------------
/src/screens/Home/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 | import {Text} from 'styles/layout';
3 |
4 | export const Container = styled.View`
5 | background: ${({theme}) => theme.colors.primary};
6 | flex: 1;
7 | `;
8 |
9 | export const Content = styled.View`
10 | background: ${({theme}) => theme.colors.grayLight};
11 | border-top-right-radius: ${({theme}) => theme.spacing.tall}px;
12 | border-top-left-radius: ${({theme}) => theme.spacing.tall}px;
13 | flex: 1;
14 | `;
15 |
16 | export const Salutation = styled(Text)`
17 | color: ${({theme}) => theme.colors.paragraph};
18 | font-size: ${({theme}) => theme.fontSizes.small};
19 |
20 | margin: ${({theme}) => theme.spacing.tall}px
21 | ${({theme}) => theme.spacing.tall}px ${({theme}) => theme.spacing.small}px;
22 | `;
23 |
--------------------------------------------------------------------------------
/src/screens/Language/LanguageScreen.tsx:
--------------------------------------------------------------------------------
1 | import React, {useCallback, useMemo} from 'react';
2 | import {useNavigation} from '@react-navigation/native';
3 |
4 | import {Container} from './styles';
5 | import {useTranslation} from 'react-i18next';
6 |
7 | import List, {ListItemProps} from 'components/List';
8 | import {Toolbar} from 'components';
9 |
10 | import {LANG_EN_US, LANG_PT_BR} from 'services/language';
11 | import {sortBy} from 'lodash';
12 |
13 | const LanguageScreen = (): JSX.Element => {
14 | const navigation = useNavigation();
15 |
16 | const {t, i18n} = useTranslation('settings');
17 |
18 | const changeLanguage = useCallback(
19 | (language: string) => {
20 | i18n.changeLanguage(language);
21 | navigation.goBack();
22 | },
23 | [i18n, navigation],
24 | );
25 |
26 | const items: ListItemProps[] = useMemo(() => {
27 | return [
28 | {
29 | id: 1,
30 | title: t(LANG_PT_BR),
31 | selected: LANG_PT_BR === i18n.language ? t('active') : undefined,
32 | icon: 'chevron-right',
33 | onPress: () => changeLanguage(LANG_PT_BR),
34 | },
35 | {
36 | id: 2,
37 | title: t(LANG_EN_US),
38 | selected: LANG_EN_US === i18n.language ? t('active') : undefined,
39 | icon: 'chevron-right',
40 | onPress: () => changeLanguage(LANG_EN_US),
41 | },
42 | ];
43 | }, [i18n.language, changeLanguage, t]);
44 |
45 | const menuItems = sortBy(items, 'selected');
46 |
47 | return (
48 |
49 |
50 |
51 |
52 | );
53 | };
54 |
55 | export default LanguageScreen;
56 |
--------------------------------------------------------------------------------
/src/screens/Language/index.tsx:
--------------------------------------------------------------------------------
1 | export {default} from './LanguageScreen';
2 |
--------------------------------------------------------------------------------
/src/screens/Language/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 |
3 | export const Container = styled.View`
4 | flex: 1;
5 | `;
6 |
7 | export const Title = styled.Text``;
8 |
--------------------------------------------------------------------------------
/src/screens/Reports/__tests__/ReportsScreen.test.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/unbound-method */
2 | /* eslint-disable @typescript-eslint/no-explicit-any */
3 | import React from 'react';
4 | import {render} from 'utils/test-utils';
5 | import ReportsScreen from '../ReportsScreen';
6 |
7 | jest.mock('react-i18next', () => ({
8 | useTranslation: () => ({
9 | t: (key: any) => key,
10 | i18n: {
11 | changeLanguage: () => new Promise(() => {}),
12 | },
13 | }),
14 | }));
15 |
16 | jest.mock('@react-navigation/native', () => {
17 | const actualNav = jest.requireActual('@react-navigation/native');
18 | return {
19 | ...actualNav,
20 | useRoute: () => jest.fn(),
21 | };
22 | });
23 |
24 | describe('ReportsScreen', () => {
25 | it('Test match snapshot ReportsScreen', () => {
26 | // given
27 | const props = {};
28 |
29 | // when
30 | const {toJSON} = render();
31 |
32 | // then
33 | expect(toJSON()).toMatchSnapshot();
34 | });
35 | });
36 |
--------------------------------------------------------------------------------
/src/screens/Reports/components/FilterPeriod/FilterPeriod.tsx:
--------------------------------------------------------------------------------
1 | import React, {useCallback, useState} from 'react';
2 |
3 | import Item from './Item';
4 |
5 | import {Container, FlatList} from './styles';
6 |
7 | import DATA, {FilterPeriodItem} from './data';
8 |
9 | interface Props {
10 | onChangeFilter: (filterPeriod: FilterPeriodItem) => void;
11 | }
12 |
13 | const FilterPeriod = ({onChangeFilter}: Props): JSX.Element => {
14 | const [first] = DATA;
15 |
16 | const [data, setData] = useState(DATA ?? []);
17 |
18 | const [, setFilterPeriodSelected] = useState(first);
19 |
20 | const listKeyExtractor = useCallback(item => item.id, []);
21 |
22 | const onPress = useCallback(
23 | (filterPeriod: FilterPeriodItem) => {
24 | setFilterPeriodSelected(filterPeriod);
25 |
26 | const merge = data.map(item =>
27 | item.id === filterPeriod.id
28 | ? {...filterPeriod, active: true}
29 | : {...item, active: false},
30 | );
31 |
32 | setData(merge);
33 | onChangeFilter(filterPeriod);
34 | },
35 | [data, onChangeFilter],
36 | );
37 |
38 | const renderItem = useCallback(
39 | ({item}) => ,
40 | [onPress],
41 | );
42 |
43 | return (
44 |
45 |
51 |
52 | );
53 | };
54 |
55 | export default FilterPeriod;
56 |
--------------------------------------------------------------------------------
/src/screens/Reports/components/FilterPeriod/Item.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {useTranslation} from 'react-i18next';
3 | import {FilterPeriodItem} from './data';
4 |
5 | import {Touchable, Label} from './styles';
6 |
7 | interface ItemProps {
8 | item: FilterPeriodItem;
9 | active?: boolean;
10 | onPress: (item: FilterPeriodItem) => void;
11 | }
12 |
13 | const Item = ({item, onPress}: ItemProps): JSX.Element => {
14 | const {t} = useTranslation('reports');
15 |
16 | return (
17 | onPress(item)}>
18 |
19 |
20 | );
21 | };
22 |
23 | export default Item;
24 |
--------------------------------------------------------------------------------
/src/screens/Reports/components/FilterPeriod/__tests__/FilterPeriod.test.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-explicit-any */
2 | /* eslint-disable @typescript-eslint/unbound-method */
3 | import React from 'react';
4 |
5 | import {render} from 'utils/test-utils';
6 | import FilterPeriod from '../FilterPeriod';
7 |
8 | jest.mock('react-i18next', () => ({
9 | useTranslation: () => ({t: (key: any) => key}),
10 | }));
11 |
12 | const onChangeFilter = jest.fn();
13 |
14 | describe('FilterPeriod', () => {
15 | it('Test match snapshot FilterPeriod', () => {
16 | // given
17 | const props = {
18 | onChangeFilter,
19 | };
20 |
21 | // when
22 | const {toJSON} = render();
23 |
24 | // then
25 | expect(toJSON()).toMatchSnapshot();
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/src/screens/Reports/components/FilterPeriod/data.ts:
--------------------------------------------------------------------------------
1 | import {
2 | addDays,
3 | endOfToday,
4 | endOfYesterday,
5 | startOfMonth,
6 | startOfQuarter,
7 | startOfWeek,
8 | startOfYear,
9 | } from 'date-fns';
10 |
11 | export interface FilterPeriodItem {
12 | id: string;
13 | active?: boolean;
14 | interval: {
15 | start: Date;
16 | end: Date;
17 | };
18 | }
19 |
20 | const yerterday = endOfYesterday();
21 | const today = endOfToday();
22 | const last15Days = addDays(today, -15);
23 | const month = startOfMonth(today);
24 | const week = startOfWeek(today);
25 | const quarter = startOfQuarter(today);
26 | const semester = addDays(today, -180);
27 | const year = startOfYear(today);
28 |
29 | const DATA: FilterPeriodItem[] = [
30 | {
31 | id: 'today',
32 | interval: {
33 | start: yerterday,
34 | end: today,
35 | },
36 | active: true,
37 | },
38 | {
39 | id: 'week',
40 | interval: {
41 | start: week,
42 | end: today,
43 | },
44 | },
45 | {
46 | id: '15-days',
47 | interval: {
48 | start: last15Days,
49 | end: today,
50 | },
51 | },
52 | {
53 | id: 'month',
54 | interval: {
55 | start: month,
56 | end: today,
57 | },
58 | },
59 | {
60 | id: 'quarter',
61 | interval: {
62 | start: quarter,
63 | end: today,
64 | },
65 | },
66 | {
67 | id: 'semester',
68 | interval: {
69 | start: semester,
70 | end: today,
71 | },
72 | },
73 | {
74 | id: 'year',
75 | interval: {
76 | start: year,
77 | end: today,
78 | },
79 | },
80 | ];
81 |
82 | export default DATA;
83 |
--------------------------------------------------------------------------------
/src/screens/Reports/components/FilterPeriod/index.ts:
--------------------------------------------------------------------------------
1 | export {default} from './FilterPeriod';
2 |
--------------------------------------------------------------------------------
/src/screens/Reports/components/FilterPeriod/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 | import {Text, TouchableOpacity} from 'styles/layout';
3 |
4 | export const Container = styled.View`
5 | flex-direction: row;
6 | `;
7 |
8 | export const FlatList = styled.FlatList`
9 | padding-top: ${({theme}) => theme.spacing.great}px;
10 | `;
11 |
12 | export const Touchable = styled(TouchableOpacity)`
13 | margin: 0px ${({theme}) => theme.spacing.great}px
14 | ${({theme}) => theme.spacing.tall}px;
15 | `;
16 |
17 | export const Label = styled(Text)<{active: boolean}>`
18 | font-family: ${({theme}) => theme.fontFamily.regular};
19 | font-size: ${({theme}) => theme.fontSizes.small};
20 | font-weight: ${({theme}) => theme.fontWeight.bold};
21 | color: ${({theme, active = false}) =>
22 | active ? theme.colors.white : theme.colors.gray};
23 | `;
24 |
--------------------------------------------------------------------------------
/src/screens/Reports/components/LegendList/LegendItem.tsx:
--------------------------------------------------------------------------------
1 | import {Currency} from 'components';
2 | import React from 'react';
3 |
4 | import {ReportItem} from 'services/reports';
5 |
6 | import {Container, Title, SubTitle, Circle} from './styles';
7 |
8 | const LegendItem = ({text, value, sumed, color}: ReportItem): JSX.Element => {
9 | return (
10 |
11 |
12 |
13 |
14 | {text} ({sumed})
15 |
16 |
17 | {currency}}
20 | />
21 |
22 | );
23 | };
24 |
25 | export default LegendItem;
26 |
--------------------------------------------------------------------------------
/src/screens/Reports/components/LegendList/LegendList.tsx:
--------------------------------------------------------------------------------
1 | import React, {useCallback} from 'react';
2 |
3 | import {FlatList} from './styles';
4 | import Item from './LegendItem';
5 | import {ReportItem} from 'services/reports';
6 |
7 | interface Props {
8 | items: ReportItem[];
9 | }
10 |
11 | const LegendList = ({items}: Props): JSX.Element => {
12 | const ListKeyExtractor = useCallback(item => item.id, []);
13 | const renderItem = useCallback(({item}) => , []);
14 |
15 | return (
16 |
23 | );
24 | };
25 |
26 | export default LegendList;
27 |
--------------------------------------------------------------------------------
/src/screens/Reports/components/LegendList/__tests__/LegendItem.test.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-explicit-any */
2 | /* eslint-disable @typescript-eslint/unbound-method */
3 | import React from 'react';
4 |
5 | import {render} from 'utils/test-utils';
6 | import LegendItem from '../LegendItem';
7 |
8 | const MOCK = {
9 | id: '123abc',
10 | color: 'red',
11 | text: 'Teste',
12 | sumed: 100,
13 | value: 10,
14 | };
15 |
16 | describe('LegendItem', () => {
17 | it('Test match snapshot LegendItem', () => {
18 | // given
19 | const props = {
20 | ...MOCK,
21 | };
22 |
23 | // when
24 | const {toJSON} = render();
25 |
26 | // then
27 | expect(toJSON()).toMatchSnapshot();
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/src/screens/Reports/components/LegendList/__tests__/LegendList.test.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-explicit-any */
2 | /* eslint-disable @typescript-eslint/unbound-method */
3 | import React from 'react';
4 |
5 | import {render} from 'utils/test-utils';
6 | import LegendList from '../LegendList';
7 |
8 | const MOCK = {
9 | id: '123abc',
10 | color: 'red',
11 | text: 'Teste',
12 | sumed: 100,
13 | value: 10,
14 | };
15 |
16 | describe('LegendList', () => {
17 | it('Test match snapshot LegendList', () => {
18 | // given
19 | const props = {
20 | items: [MOCK, MOCK],
21 | };
22 |
23 | // when
24 | const {toJSON} = render();
25 |
26 | // then
27 | expect(toJSON()).toMatchSnapshot();
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/src/screens/Reports/components/LegendList/__tests__/__snapshots__/LegendItem.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`LegendItem Test match snapshot LegendItem 1`] = `
4 |
19 |
33 |
46 | Teste
47 | (
48 | 100
49 | )
50 |
51 |
64 | R$ 0,10
65 |
66 |
67 | `;
68 |
--------------------------------------------------------------------------------
/src/screens/Reports/components/LegendList/index.ts:
--------------------------------------------------------------------------------
1 | export {default} from './LegendList';
2 |
3 | export * from './LegendList';
4 |
--------------------------------------------------------------------------------
/src/screens/Reports/components/LegendList/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 | import {Text} from 'styles/layout';
3 |
4 | const SIZE_CIRCLE = 16;
5 |
6 | export const FlatList = styled.FlatList`
7 | margin: 0px 20px;
8 | `;
9 |
10 | export const Container = styled.View`
11 | flex-direction: row;
12 | width: 50%;
13 | align-items: center;
14 | padding: 5px 2px;
15 | `;
16 |
17 | export const Content = styled.View``;
18 |
19 | export const Title = styled(Text)`
20 | color: ${({theme}) => theme.colors.paragraph};
21 | font-size: ${({theme}) => theme.fontSizes.tiny};
22 | font-weight: ${({theme}) => theme.fontWeight.bold};
23 | margin-right: 5px;
24 | `;
25 |
26 | export const SubTitle = styled(Title)`
27 | font-weight: ${({theme}) => theme.fontWeight.regular};
28 | `;
29 |
30 | export const Circle = styled.View<{color?: string}>`
31 | background: ${({theme, color = undefined}) => color ?? theme.colors.gray};
32 | width: ${SIZE_CIRCLE}px;
33 | height: ${SIZE_CIRCLE}px;
34 | margin-right: 5px;
35 | border-radius: ${SIZE_CIRCLE / 2}px;
36 | `;
37 |
--------------------------------------------------------------------------------
/src/screens/Reports/components/ListEmpty/LICENSE.md:
--------------------------------------------------------------------------------
1 | Yasmina Luria
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/screens/Reports/components/ListEmpty/ListEmpty.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {useTranslation} from 'react-i18next';
3 |
4 | import {Container, EmptyAnimation, Title, SubTitle} from './styles';
5 |
6 | import EMPTY from './reports.json';
7 |
8 | const ListEmpty = (): JSX.Element => {
9 | const {t} = useTranslation('home');
10 |
11 | return (
12 |
13 |
14 |
15 | {t('no-entries')}
16 | {t('message-add-new')}
17 |
18 | );
19 | };
20 |
21 | export default ListEmpty;
22 |
--------------------------------------------------------------------------------
/src/screens/Reports/components/ListEmpty/__tests__/ListEmpty.test.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-explicit-any */
2 | /* eslint-disable @typescript-eslint/unbound-method */
3 | import React from 'react';
4 |
5 | import {render} from 'utils/test-utils';
6 | import ListEmpty from '../ListEmpty';
7 |
8 | describe('ListEmpty', () => {
9 | it('Test match snapshot ListEmpty', () => {
10 | // given
11 | const props = {};
12 |
13 | // when
14 | const {toJSON} = render();
15 |
16 | // then
17 | expect(toJSON()).toMatchSnapshot();
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/src/screens/Reports/components/ListEmpty/index.ts:
--------------------------------------------------------------------------------
1 | export {default} from './ListEmpty';
2 |
--------------------------------------------------------------------------------
/src/screens/Reports/components/ListEmpty/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 | import LottieView from 'lottie-react-native';
3 | import {Text} from 'styles/layout';
4 |
5 | export const Container = styled.View`
6 | flex: 1;
7 | justify-content: center;
8 | align-items: center;
9 | background: ${({theme}) => theme.colors.tertiary};
10 | `;
11 |
12 | export const EmptyAnimation = styled(LottieView).attrs({
13 | autoPlay: true,
14 | loop: true,
15 | })`
16 | width: 366px;
17 | height: 366px;
18 | margin-top: ${({theme}) => theme.spacing.small}px;
19 | `;
20 |
21 | export const Title = styled(Text)`
22 | text-align: center;
23 | margin: ${({theme}) => theme.spacing.tall}px 0px;
24 | font-size: ${({theme}) => theme.fontSizes.large};
25 | font-weight: ${({theme}) => theme.fontWeight.bold};
26 | color: ${({theme}) => theme.colors.white};
27 | `;
28 | export const SubTitle = styled(Text)`
29 | text-align: center;
30 | color: ${({theme}) => theme.colors.white};
31 | font-size: ${({theme}) => theme.fontSizes.small};
32 | `;
33 |
--------------------------------------------------------------------------------
/src/screens/Reports/index.ts:
--------------------------------------------------------------------------------
1 | export {default} from './ReportsScreen';
2 |
--------------------------------------------------------------------------------
/src/screens/Reports/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 | import {getStatusBarHeight} from 'react-native-iphone-x-helper';
3 | import {Text, TouchableOpacity} from 'styles/layout';
4 |
5 | export const Container = styled.View`
6 | flex: 1;
7 | background: ${({theme}) => theme.colors.grayLight};
8 | `;
9 |
10 | export const Header = styled.View`
11 | width: 100%;
12 | height: 500px;
13 | align-items: center;
14 | padding: ${({theme}) => theme.spacing.tall + getStatusBarHeight()}px 0px;
15 | margin-bottom: ${({theme}) => theme.spacing.tall}px;
16 | background: ${({theme}) => theme.colors.accent};
17 | `;
18 |
19 | export const Title = styled(Text)`
20 | font-size: ${({theme}) => theme.fontSizes.large};
21 | color: ${({theme}) => theme.colors.white};
22 | `;
23 |
24 | export const Balance = styled(Title)`
25 | color: ${({theme}) => theme.colors.accent};
26 | `;
27 |
28 | export const ContainerButtons = styled.View`
29 | flex-direction: row;
30 | `;
31 |
32 | export const Touchable = styled(TouchableOpacity)`
33 | margin: 0px ${({theme}) => theme.spacing.great}px
34 | ${({theme}) => theme.spacing.tall}px;
35 | `;
36 |
37 | export const Label = styled(Text)<{active: boolean}>`
38 | font-family: ${({theme}) => theme.fontFamily.regular};
39 | font-size: ${({theme}) => theme.fontSizes.medium};
40 | font-weight: ${({theme}) => theme.fontWeight.bold};
41 | color: ${({theme, active = false}) =>
42 | active ? theme.colors.white : theme.colors.gray};
43 | `;
44 |
--------------------------------------------------------------------------------
/src/screens/Settings/index.ts:
--------------------------------------------------------------------------------
1 | export {default} from './SettingsScreen';
2 |
--------------------------------------------------------------------------------
/src/screens/Settings/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 |
3 | export const Container = styled.View`
4 | flex: 1;
5 | `;
6 |
--------------------------------------------------------------------------------
/src/services/currency.ts:
--------------------------------------------------------------------------------
1 | import storage from './storage';
2 |
3 | export const KEY_APP_CURRENCY = '@app-currency';
4 |
5 | export const DEFAULT_CURRENCY = 'BRL';
6 |
7 | export const saveCurrency = async (value: string): Promise => {
8 | return storage.setItem(KEY_APP_CURRENCY, value);
9 | };
10 |
11 | export const getCurrency = async (): Promise => {
12 | return storage.getItem(KEY_APP_CURRENCY);
13 | };
14 |
15 | export default {
16 | saveCurrency,
17 | getCurrency,
18 | };
19 |
--------------------------------------------------------------------------------
/src/services/database.ts:
--------------------------------------------------------------------------------
1 | import {Database} from '@nozbe/watermelondb';
2 | import SQLiteAdapter, {
3 | SQLiteAdapterOptions,
4 | } from '@nozbe/watermelondb/adapters/sqlite';
5 |
6 | import {schema, migrations, modelClasses, seeds} from 'database';
7 | import {DATABASE_NAME} from 'database/schema';
8 | import {isIos} from 'styles/mixins';
9 |
10 | let database: Database;
11 |
12 | export function getDatabase(): Database {
13 | if (!database) {
14 | const adapterConfig: SQLiteAdapterOptions = {
15 | dbName: DATABASE_NAME,
16 | schema,
17 | migrations,
18 | jsi: isIos,
19 | };
20 |
21 | const adapter = new SQLiteAdapter(adapterConfig);
22 |
23 | database = new Database({
24 | adapter,
25 | modelClasses,
26 | });
27 | }
28 | return database;
29 | }
30 |
31 | export function runSeeds(): void {
32 | seeds.run();
33 | }
34 |
35 | export default getDatabase;
36 |
--------------------------------------------------------------------------------
/src/services/index.ts:
--------------------------------------------------------------------------------
1 | export {default as database} from './database';
2 | export {default as storage} from './storage';
3 | export {default as currency} from './currency';
4 | export {default as reports} from './reports';
5 |
--------------------------------------------------------------------------------
/src/services/storage.ts:
--------------------------------------------------------------------------------
1 | import AsyncStorage from '@react-native-async-storage/async-storage';
2 |
3 | // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
4 | const setItem = async (key: string, value: any): Promise => {
5 | if (!value) {
6 | return undefined;
7 | }
8 |
9 | return AsyncStorage.setItem(key, value);
10 | };
11 |
12 | const getItem = async (key: string): Promise => {
13 | return AsyncStorage.getItem(key) ?? undefined;
14 | };
15 |
16 | const removeItem = (key: string): Promise => {
17 | return AsyncStorage.removeItem(key);
18 | };
19 |
20 | const reset = async (): Promise => {
21 | (await AsyncStorage.getAllKeys()).forEach(async key => {
22 | await AsyncStorage.removeItem(key);
23 | });
24 | };
25 |
26 | export default {
27 | setItem,
28 | getItem,
29 | removeItem,
30 | reset,
31 | };
32 |
--------------------------------------------------------------------------------
/src/styles/colors.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | primary: '#2B303A',
3 | seconday: '#37AF78',
4 | tertiary: '#6E7B96',
5 | accent: '#181D26',
6 |
7 | black: '#000000',
8 | white: '#ffffff',
9 |
10 | danger: '#E96245',
11 | success: '#50806A',
12 |
13 | gray: '#464E5F',
14 | grayLight: '#E1E3E5',
15 | paragraph: '#808080',
16 | };
17 |
--------------------------------------------------------------------------------
/src/styles/index.ts:
--------------------------------------------------------------------------------
1 | import * as Colors from './colors';
2 | import * as Typography from './typography';
3 | import * as Mixins from './mixins';
4 | import * as Spacing from './spacing';
5 | import * as Theme from './theme';
6 | import * as Shadow from './shadow';
7 |
8 | export {Colors, Typography, Mixins, Spacing, Theme, Shadow};
9 |
--------------------------------------------------------------------------------
/src/styles/layout.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components/native';
2 | import ThemeColors from './colors';
3 |
4 | export const TouchableOpacity = styled.TouchableOpacity.attrs({
5 | activeOpacity: 0.8,
6 | })``;
7 |
8 | export const Text = styled.Text`
9 | font-family: ${({theme}) => theme.fontFamily.regular};
10 | font-size: ${({theme}) => theme.fontSizes.medium};
11 | font-weight: ${({theme}) => theme.fontWeight.regular};
12 | color: ${({theme}) => theme.colors.black};
13 | `;
14 |
15 | export const Colors = ThemeColors;
16 |
--------------------------------------------------------------------------------
/src/styles/mixins.ts:
--------------------------------------------------------------------------------
1 | import {Platform, Dimensions, PixelRatio} from 'react-native';
2 |
3 | const isAndroid = Platform.OS === 'android';
4 | const isIos = Platform.OS === 'ios';
5 |
6 | const scaledSize = Dimensions.get('window');
7 |
8 | const {width, height} = scaledSize;
9 |
10 | const screenSize = Math.sqrt(width * height) / 100;
11 |
12 | //Guideline sizes are based on standard ~5" screen mobile device
13 | const guidelineBaseWidth = 350;
14 |
15 | const scale = width / guidelineBaseWidth;
16 |
17 | const scaleSize = (size: number): number => (width / guidelineBaseWidth) * size;
18 |
19 | const scaleFont = (size: number): number => size * PixelRatio.getFontScale();
20 |
21 | const normalize = (size: number): number => {
22 | const newSize = size * scale;
23 | return Math.round(PixelRatio.roundToNearestPixel(newSize)) - 2;
24 | };
25 |
26 | export {
27 | isAndroid,
28 | isIos,
29 | screenSize,
30 | width,
31 | height,
32 | scaleSize,
33 | scaleFont,
34 | normalize,
35 | };
36 |
--------------------------------------------------------------------------------
/src/styles/shadow.ts:
--------------------------------------------------------------------------------
1 | import {css} from 'styled-components/native';
2 |
3 | const shadow = css`
4 | shadow-color: ${({theme}) => theme.colors.black};
5 | shadow-offset: 1px 3px;
6 | shadow-opacity: 0.22;
7 | shadow-radius: 10px;
8 | elevation: 10;
9 | `;
10 |
11 | export default shadow;
12 |
--------------------------------------------------------------------------------
/src/styles/spacing.ts:
--------------------------------------------------------------------------------
1 | import {scaleSize} from './mixins';
2 |
3 | export const SMALL = scaleSize(5);
4 | export const GREAT = scaleSize(10);
5 | export const TALL = scaleSize(20);
6 | export const VENTI = scaleSize(30);
7 |
8 | export default {
9 | small: SMALL,
10 | great: GREAT,
11 | tall: TALL,
12 | venti: VENTI,
13 | };
14 |
--------------------------------------------------------------------------------
/src/styles/styled.d.ts:
--------------------------------------------------------------------------------
1 | import 'styled-components';
2 |
3 | declare module 'styled-components' {
4 | export interface DefaultTheme {
5 | colors: {
6 | primary: string;
7 | seconday: string;
8 | tertiary: string;
9 | accent: string;
10 | black: string;
11 | white: string;
12 | danger: string;
13 | success: string;
14 | gray: string;
15 | grayLight: string;
16 | paragraph: string;
17 | };
18 |
19 | fontFamily: {
20 | regular: string;
21 | medium: string;
22 | thin: string;
23 | light: string;
24 | bold: string;
25 | semiBold: string;
26 | };
27 |
28 | fontLineHeight: {
29 | large: number;
30 | medium: number;
31 | small: number;
32 | };
33 |
34 | fontWeight: {
35 | regular: string;
36 | bold: string;
37 | };
38 |
39 | fontSizes: {
40 | tiny: string;
41 | small: string;
42 | medium: string;
43 | large: string;
44 | big: string;
45 | extraBig: string;
46 | };
47 |
48 | spacing: {
49 | small: number;
50 | great: number;
51 | tall: number;
52 | venti: number;
53 | };
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/styles/theme.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import {ThemeProvider, DefaultTheme} from 'styled-components/native';
4 |
5 | import colors from './colors';
6 | import {fontFamily, fontLineHeight, fontWeight, fontSizes} from './typography';
7 | import spacing from './spacing';
8 |
9 | export const theme: DefaultTheme = {
10 | colors,
11 | fontSizes,
12 | spacing,
13 | fontFamily,
14 | fontLineHeight,
15 | fontWeight,
16 | };
17 |
18 | interface Props {
19 | children: React.ReactNode;
20 | }
21 |
22 | export const Theme = ({children}: Props): JSX.Element => (
23 | {children}
24 | );
25 |
26 | export default Theme;
27 |
--------------------------------------------------------------------------------
/src/styles/typography.ts:
--------------------------------------------------------------------------------
1 | import {scaleFont} from './mixins';
2 |
3 | // FONT FAMILY
4 | const FONT_FAMILY_REGULAR = 'Poppins-Regular';
5 | const FONT_FAMILY_MEDIUM = 'Poppins-Medium';
6 | const FONT_FAMILY_THIN = 'Poppins-Thin';
7 | const FONT_FAMILY_LIGHT = 'Poppins-Light';
8 | const FONT_FAMILY_BOLD = 'Poppins-Bold';
9 | const FONT_FAMILY_SEMI_BOLD = 'Poppins-SemiBold';
10 |
11 | export const fontFamily = {
12 | regular: FONT_FAMILY_REGULAR,
13 | medium: FONT_FAMILY_MEDIUM,
14 | thin: FONT_FAMILY_THIN,
15 | light: FONT_FAMILY_LIGHT,
16 | bold: FONT_FAMILY_BOLD,
17 | semiBold: FONT_FAMILY_SEMI_BOLD,
18 | };
19 |
20 | // FONT WEIGHT
21 | export const FONT_WEIGHT_REGULAR = '400';
22 | export const FONT_WEIGHT_BOLD = '700';
23 |
24 | export const fontWeight = {
25 | regular: FONT_WEIGHT_REGULAR,
26 | bold: FONT_WEIGHT_BOLD,
27 | };
28 |
29 | // FONT SIZE
30 | const FONT_SIZE_TINY = scaleFont(10);
31 | const FONT_SIZE_SMALL = scaleFont(14);
32 | const FONT_SIZE_MEDIUM = scaleFont(18);
33 | const FONT_SIZE_LARGE = scaleFont(22);
34 | const FONT_SIZE_BIG = scaleFont(30);
35 | const FONT_SIZE_EXTRA_BIG = scaleFont(40);
36 |
37 | export const fontSizes = {
38 | tiny: `${FONT_SIZE_TINY}px`,
39 | small: `${FONT_SIZE_SMALL}px`,
40 | medium: `${FONT_SIZE_MEDIUM}px`,
41 | large: `${FONT_SIZE_LARGE}px`,
42 | big: `${FONT_SIZE_BIG}px`,
43 | extraBig: `${FONT_SIZE_EXTRA_BIG}px`,
44 | };
45 |
46 | // LINE HEIGHT
47 | export const LINE_HEIGHT_LARGE = scaleFont(36);
48 | export const LINE_HEIGHT_MEDIUM = scaleFont(18);
49 | export const LINE_HEIGHT_SMALL = scaleFont(16);
50 |
51 | export const fontLineHeight = {
52 | large: LINE_HEIGHT_LARGE,
53 | medium: LINE_HEIGHT_MEDIUM,
54 | small: LINE_HEIGHT_SMALL,
55 | };
56 |
57 | export default {
58 | fontFamily,
59 | fontSizes,
60 | fontWeight,
61 | fontLineHeight,
62 | };
63 |
--------------------------------------------------------------------------------
/src/utils/dates.ts:
--------------------------------------------------------------------------------
1 | import {
2 | format as formatPattern,
3 | isValid,
4 | parseISO as parseISOFNS,
5 | } from 'date-fns';
6 |
7 | import locale from 'date-fns/locale/pt-BR';
8 |
9 | export const DATE_FORMAT_ISO = 'yyyy-MM-dd';
10 | export const DATE_FORMAT_BR = 'dd/MM/yyyy';
11 | export const DATE_FORMAT_FRIENDLY = " dd/MM 'às' HH:mm";
12 | export const DATE_FORMAT_FRIENDLY_TIME = "'às' HH:mm";
13 |
14 | export function parseStringToDate(value: string): Date {
15 | const parsedDate = parseISOFNS(value);
16 | if (!isValid(parsedDate)) {
17 | throw Error('canot parse date');
18 | }
19 | return parsedDate;
20 | }
21 | export function parseDateToString({
22 | value,
23 | format = DATE_FORMAT_BR,
24 | }: {
25 | value: Date;
26 | format?: string;
27 | }): string {
28 | return formatPattern(value, format, {locale});
29 | }
30 |
31 | export const getDate = (date: Date): string => {
32 | return formatPattern(date, DATE_FORMAT_ISO, {locale});
33 | };
34 |
35 | export const getDateToday = (): string => {
36 | const today = new Date();
37 | return formatPattern(today, DATE_FORMAT_ISO, {locale});
38 | };
39 |
40 | export const getDateAndHourFriendly = (value: Date): string => {
41 | return formatPattern(value, DATE_FORMAT_FRIENDLY, {locale});
42 | };
43 |
44 | export const getTimeFriendly = (value: Date): string => {
45 | return formatPattern(value, DATE_FORMAT_FRIENDLY_TIME, {locale});
46 | };
47 |
--------------------------------------------------------------------------------
/src/utils/index.ts:
--------------------------------------------------------------------------------
1 | export {default as strings} from './strings';
2 |
--------------------------------------------------------------------------------
/src/utils/strings.ts:
--------------------------------------------------------------------------------
1 | export const sanitizeString = (value: string): string => {
2 | return value
3 | .toLowerCase()
4 | .replace(/á/g, 'a')
5 | .replace(/é/g, 'e')
6 | .replace(/í/g, 'i')
7 | .replace(/ó/g, 'o')
8 | .replace(/ú/g, 'u');
9 | };
10 |
--------------------------------------------------------------------------------
/src/utils/test-utils.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-explicit-any */
2 | import React from 'react';
3 | import {render, RenderAPI, RenderOptions} from '@testing-library/react-native';
4 | import {Database} from '@nozbe/watermelondb';
5 | import LokiJSAdapter, {
6 | LokiAdapterOptions,
7 | } from '@nozbe/watermelondb/adapters/lokijs';
8 |
9 | import {theme} from 'styles/theme';
10 | import {ThemeProvider} from 'styled-components/native';
11 |
12 | import {NavigationContainer} from '@react-navigation/native';
13 | import DatabaseProvider from '@nozbe/watermelondb/DatabaseProvider';
14 |
15 | interface Props {
16 | children: JSX.Element;
17 | }
18 |
19 | import {schema, migrations, modelClasses} from 'database';
20 |
21 | const AllTheProviders = ({children}: Props) => {
22 | const adapterConfig: LokiAdapterOptions = {
23 | schema,
24 | migrations,
25 | useWebWorker: false,
26 | useIncrementalIndexedDB: true,
27 | extraLokiOptions: {
28 | autosave: false,
29 | },
30 | };
31 | const adapter = new LokiJSAdapter(adapterConfig);
32 | const database = new Database({
33 | adapter,
34 | modelClasses,
35 | });
36 |
37 | return (
38 |
39 |
40 | {children}
41 |
42 |
43 | );
44 | };
45 |
46 | const customRender = (
47 | ui: React.ReactElement,
48 | options?: RenderOptions,
49 | ): RenderAPI => render(ui, {wrapper: AllTheProviders, ...options});
50 |
51 | // re-export everything
52 | export * from '@testing-library/react-native';
53 |
54 | // override render method
55 | export {customRender as render};
56 |
--------------------------------------------------------------------------------