Compare commits

...

658 Commits

Author SHA1 Message Date
Joel Jeremy Marquez
8e71dcccd8 Release notes 2024-01-28 23:49:56 -08:00
Joel Jeremy Marquez
de9a1880a7 Migrate LoadBackup to ts 2024-01-28 23:49:10 -08:00
youngcw
43ebe9e0fd fix bad account sort order in demo (#2279)
* fix bad sort order in demo

* note

* add async back

* fix tests

* fix2

* fix3

* update vrt

* fix image
2024-01-26 09:12:54 -07:00
youngcw
515bdf5a74 update vrt instructions (#2287)
* update

* note

* revise
2024-01-26 09:06:12 -07:00
Ed
018714610a Updating link for GoCardless Bank Account Sync (#2276)
* Updating link for GoCardless Bank Account Sync

* Adding release notes

* Update URL to point to docs

* Fixing lint
2024-01-26 07:11:18 -08:00
Joel Jeremy Marquez
00ee165f8e Mobile Off Budget category label (#2284)
* Mobile Off Budget category label

* Release notes

* Fix error

* Fix release notes
2024-01-26 07:09:29 -08:00
Neil
68442ae9e6 Custom reports: hide "show ..." checkboxes in menu (#2174)
* Add Toggles

* budget table

* testing

* updates

* updates

* fixes

* updates

* fix Menu

* lint fixes

* fix keybindings

* revert budget menu changes

* notes

* remove default exports

* fixes

* disabled fix

* add style option

* lint fix

* remove css

* lint fixes

* color updates

* merge menu with togglemenu

* host

* menu fixes

* fix regression

* remove host

* adjustments

* onToggle

* vrt fix

* typecheck

* merge fixes

* colors

* lint fix
2024-01-24 21:49:12 +00:00
shall0pass
b937bfae04 [Bugfix] Goals: Database entry (#2281)
* fix database insertion

* dbMonth format

* release note
2024-01-24 14:51:38 -06:00
DJ Mountney
317e7f135e 🐛 Avoid passing a boolean to the import trans category title (#2278)
* Avoid passing a boolean to the import trans category title

- Fixes an error regading passing false to title
  when category is not available
2024-01-24 11:23:02 -08:00
DJ Mountney
5adb083575 🐛 Fix a missing ref param warning for forwardRef (#2277)
* Fix a missing ref param warning for forwardRef

- Drop unused usage of forwardRef on TableWithNavigator
2024-01-24 11:13:43 -08:00
Joel Jeremy Marquez
524bd4e9eb Update vite / swc / ts versions (#2268)
* Update vite / swc / ts versions

* Release notes

* Revert root tsconfig module changes

* yarn dedupe

* Dummy update to run pipeline

* Update webpack and playwright

* Update playwright docker images
2024-01-24 10:49:12 -08:00
Matiss Janis Aboltins
9dfd6ce34c 🐛 fix uncategorized transaction banner flashing on load (#2273) 2024-01-23 08:23:32 +00:00
Matiss Janis Aboltins
5d28bc0e3b 🏷️ making some files comply with strict TS (#2247) 2024-01-22 19:01:05 +00:00
Matiss Janis Aboltins
a4e97e0070 ♻️ refactored cash-flow report from victory to recharts (#2260) 2024-01-22 08:34:51 +00:00
Matiss Janis Aboltins
a6e38ad2ae allow un-reconciling (unlocking) transactions (#2252) 2024-01-22 08:34:40 +00:00
Matiss Janis Aboltins
bb0ae4ebc3 🔥 removing unused variables (batch 2) (#2256) 2024-01-21 14:55:24 +00:00
youngcw
dd29e02c5c fix color of schedule before/after weekend box (#2261)
* fix color of schedule after box

* note
2024-01-20 15:37:34 -07:00
Zach Whelchel
75186183eb SimpleFin (#2188)
* Some initial UI work for adding SimpleFin.

* SimpleFin proof of concept working.

* Adds linking & unlinking to existing accounts through the account menu UI.

* Added loading and lint fixes.

* Lint changes.

* Added release notes.

* Typecheck cleanup.

* Import, lint, typecheck cleanups.

* More typecheck cleanup.

* Refactored language for consistency.

* Added default institution name.

* Lint cleanup.

* Addressed change requests.

* Added a default to migration, made variables consistent, added feature flag.

* Added account_sync_source to server schema.

* Adds account_sync_source to test.

* Fix for typecheck.

* Attempt to make typecheck happy.

* Added strict ignore.

* Moved account_sync_source to the right model (face palm).

* Hotfix for institution format.

* Lint cleanup.

* Removed unnecessary promise.all.

* Lint cleanup.
2024-01-20 14:35:52 -08:00
Neil
83f13cbdc8 add compact to custom reports (#2258)
* add compact

* notes
2024-01-20 21:18:15 +00:00
DJ Mountney
0045d9212e Bundle loot-core types into the API (#2053)
* Bundle loot-core types into the API

So we can have loot-core be the source of truth
for some types that get passed through

- Improves downstream development with API by including types
- Use path aliases for dist vs dev tsconfigs
- Convert api index to typescript as example
- Permit ts-ignore for issues with our version of typescript

---------

Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
2024-01-20 10:30:23 -08:00
Matiss Janis Aboltins
dd254c6c23 🔧 add link to community discord for support (#2250) 2024-01-20 10:40:52 +00:00
Neil
c66d6e00f5 Custom Reports - add schema (#2246)
* Add schema work

* notes

* merge fixes

* add to handlers

* notes update

* Update packages/loot-core/src/server/reports/app.ts

Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>

* review changes

* type updates

---------

Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
2024-01-20 10:22:28 +00:00
Matiss Janis Aboltins
ae3be13aa8 🔥 removing unused variables (batch 1) (#2255) 2024-01-19 23:02:21 +00:00
Neil
cd9d1fb674 Clean-up some custom reports code (#2254)
* work

* missed

* notes

* change element name

* update

* update changes

* merge fixes
2024-01-19 22:39:09 +00:00
Matiss Janis Aboltins
edba670d3a ⬆️ (prettier) upgrade, fix issues and enable for jsx files (#2253) 2024-01-19 21:48:30 +00:00
Matiss Janis Aboltins
fbb1a9647d 🐛 fix 'delete budget' button always deleting cloud file (#2251)
Closes #2216
2024-01-19 20:04:00 +00:00
Neil
06cf65497f Custom Reports: fix broken table (#2249)
* fix broken table

* notes

* error fixes
2024-01-19 19:37:14 +00:00
Neil
62a0a0fedc Custom Reports compact additions (#2245)
* compact additions

* notes

* vrt fix

* revert AnchorLink

* Update upcoming-release-notes/2245.md

Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>

* merge fixes

---------

Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
2024-01-19 08:59:47 +00:00
Neil
106dce25dd Custom Reports update entites (#2244)
* work

* notes

* missing entity

* lint
2024-01-18 22:39:42 +00:00
Matiss Janis Aboltins
ff11399180 ♻️ (typescript) fixing strictNullChecks=true issues (#2228)
* ♻️ (typescript) fixing strictNullChecks=true issues

* Patch test-helpers

* Strict patch
2024-01-18 08:23:50 +00:00
Matiss Janis Aboltins
363c9e4afb 🐛 (gocardless) fix sync when additionalInformation field is null (#2238) 2024-01-18 08:23:29 +00:00
Neil
e745a4073b Custom Reports: fix table rendering (#2192)
* reorg

* notes

* Add render

* privacy Filter additions

* merge fixes

* notes

* merge fixes

* Apply patches for strict mode

* review fixes

---------

Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
2024-01-16 20:14:38 +00:00
DJ Mountney
7d1a895b48 Restore ability to use console.log in vite (#2233)
Restore ability to use console.log in vite

- Swap our swc plugin to remove-react-properties
- Configure remove-react-properties to preserve our testids
2024-01-16 09:40:44 -08:00
DJ Mountney
a8b42bcd50 Change the vite chunk filename hash usage (#2224)
* Change the vite chunk filename hash usage

- Change to more closely match the . syntax we use with webpack
- This should fix some issues with our size compare as well
* Account for chunks being base64
2024-01-15 09:47:09 -08:00
Matiss Janis Aboltins
f33bce41ea ♻️ (typescript) enable strict mode everywhere (#2230) 2024-01-15 08:24:33 +00:00
Ikko Eltociear Ashimine
cdefe6133f chore: Update actions.ts (#2227)
non-existant -> non-existent
2024-01-14 16:13:48 +00:00
Nik
44a5199a31 Add split distribution (#2151)
* Add split distribution feature

* Add upcoming release notes

* Fix tests

* Fix remaining test

* Disable distribute button when all transactions are filled

* Add canDistributeRemainder

---------

Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
2024-01-13 15:43:13 -08:00
Neil
dccad902d6 vite host regression fix (#2217)
add host to vite config
2024-01-12 17:45:20 -08:00
Neil
b477f7c2f1 Custom Reports: Enable Show Labels (#2124)
* work

* updates

* merge fixes

* syntax fix

* Add Label element

* updates

* notes

* normalize customLabel

* fix

* range adjustments

* margin update

* merge fixes

* review Updates

* labelFix

* Fix adjustTextSize
2024-01-12 21:46:22 +00:00
Stefan Hall
540c410957 fixed:issue->#1968 Import from nYNAB fails with unknown error (#2191)
* Allow case insensitive ynab5 import for special 'starting balance' payee

* set upcoming release number to related github issue

* extract string comparison into separate function

and reuse when checking starting balance/s on ynab4 import

* make all category group checks case insensitive

when importing from ynab5 to make the check strategy consistent when importing from ynab5

* extract findById into sreusable function

to 'simplify' usage

* Add null check

Co-authored-by: Joel Jeremy Marquez <joeljeremy.marquez@gmail.com>

---------

Co-authored-by: Joel Jeremy Marquez <joeljeremy.marquez@gmail.com>
2024-01-12 12:59:31 -08:00
Matiss Janis Aboltins
e205344867 ♻️ (typescript) fixing strictNullChecks=true issues (#2212) 2024-01-12 17:49:54 +00:00
HansiWursti
8b968579b1 Proposal - Disable Save Button in Mobile Transaction View if field is edited (#2214)
* Disable SaveButton if field is edited

* Add Release Notes and fix VRT
2024-01-12 08:57:22 -08:00
Matiss Janis Aboltins
6ce794ffcc 🔥 (ofx) removing old parser (#2215)
* 🔥 (ofx) removing old parser

* Release notes

* Patch unit test
2024-01-12 07:27:18 -08:00
DJ Mountney
d5359a96ca Proposal for switching desktop-client to vite (#2084)
* Proof of concept for switching desktop-client to vite

* Fix other packages ts tests issues

* Update jsx tests to use vitest instead of jest

* Inject our global shims properly

* Add comment regarding new plugin

* Cleanup unnessary change after rebase

* Fix inter fonts pathing

* Remove manual chunks sizes for now

Just set the limit higher

* Bring back size compare

* Suppress victory warnings

* Remove craco config now that it's not used

* Add vite basic ssl plugin

- This autogenerates self-signed certs in dev mode when HTTPS env is set
- Made to match the CRA behaviour

* Add release note

* Remove warning suppression for victory

- Updated to a rollup version that includes the fix
2024-01-11 10:18:49 -08:00
Matiss Janis Aboltins
3eee0b11d2 🐛 (autocomplete) fix multi-autocomplete causing crashes (#2207) 2024-01-10 17:15:34 +00:00
HansiWursti
e792afb1fd ReAdd Mobile Account Error Page (#2204)
* Add "error" Page for special accounts in Mobile
2024-01-09 15:06:24 -08:00
HansiWursti
4fb55d0d70 Mobile - Add cleared and uncleared Balances in Account Details (#2056)
* - Added Mobile Account Cleared and Uncleared
  Balanced

* Changed font size

* Add release notes

* Changed Visibility

* Centered the different Values

* Updated VRT

* Fix merge Conflict?!
2024-01-09 15:06:01 -06:00
DJ Mountney
b330991855 Add proper types to runHandler (#2136)
* Add proper types to runHandler

- Args and return values from runHandler should now work within loot core
- Updated some of the handler types to be more accurate
2024-01-09 13:05:15 -08:00
shall0pass
165ad45822 [Enhancement] Add crossorigin field to manifest line (#2206)
* add crossorigin

* release note

---------

Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
2024-01-09 15:04:22 -06:00
Joel Jeremy Marquez
295917036b ESLint to enforce Actual's useNavigate hook (#2208)
* ESLint to enforce Actual's useNavigate hook

* Release notes

---------

Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
2024-01-09 13:03:09 -08:00
Matiss Janis Aboltins
ef9a7cfe85 🔧 (ci) fork the electron build job - master and PR (#2209) 2024-01-09 20:06:50 +00:00
Matiss Janis Aboltins
4ece4a7ff6 ♻️ (gocardless) rename nordigen_* secrets to gocardless_* (#2181) 2024-01-09 19:21:16 +00:00
DJ Mountney
761b3c6a16 Tests: Add api tests for payees and transactions (#2168)
* Tests: Add api CRUD tests for payees and transactions
2024-01-09 11:20:06 -08:00
Matiss Janis Aboltins
7ace8c52dd 🔧 (electron) release electron app to app store (osx) (#2182) 2024-01-09 18:54:57 +00:00
Subhaditya Nath
f6dd0ecdb9 Fix site.webmanifest to enable Actual to be installed as Chromium PWA (#2202) 2024-01-09 10:21:34 -06:00
rjwonder
97a4296d7c Update sync.ts with additionalInformation as fallback (#2176) 2024-01-09 08:53:15 +00:00
Matiss Janis Aboltins
89698480a5 🐛 (rules) add 'no rules' message & always show rule table (#2199) 2024-01-09 08:32:12 +00:00
Matiss Janis Aboltins
1a6db82cfb Revert "👷 (ci) adding helper comments for failed CI jobs (#2074)" (#2183)
This reverts commit e11b65719e.
2024-01-08 18:38:46 +00:00
Matiss Janis Aboltins
6ce502ea24 ♻️ refactor(electron): moving back from websockets to IPC (#2190) 2024-01-08 18:34:19 +00:00
youngcw
fd962a97b0 fix missing borders in single category drop down in report budget (#2195)
* fix missing borders in single category drop down in report budget

* note
2024-01-08 11:20:52 -07:00
youngcw
300ed824f0 change the pie chart background color in the report status (#2196)
* change the pie chart background color in the report status

* note
2024-01-08 11:20:29 -07:00
Neil
caca2497ea Add ability to import categories from CSV (#2163)
* update transaction table

* notes

* adjust parser
2024-01-08 17:51:51 +00:00
shall0pass
33e778fe9f [Maintenence] Refactor Goals Schedule file (#2102)
* refactor pass 1

* refactor pass 2

* refactor pass 3

* commented out startDate

* remove console logging

* release note

* non-repeating error

* add daily

* move else

* Fix compounding to_budget

* lint

* reapply 2125
2024-01-07 16:19:19 -06:00
Neil
8c43c78fc7 Custom reports reorganize table graph files (#2153)
* reorg

* notes

* Update upcoming-release-notes/2153.md

Co-authored-by: DJ Mountney <david.mountney@twkie.net>

* merge fixes

* fix

* another

* f

---------

Co-authored-by: DJ Mountney <david.mountney@twkie.net>
2024-01-07 21:55:21 +00:00
DJ Mountney
e0d82fd4f9 Revert "Add "error" Page for special accounts in Mobile" (#2186)
* Revert "Add "error" Page for special accounts in Mobile (#2114)"

This reverts commit b6462347a9.
2024-01-06 15:20:19 -08:00
HansiWursti
b6462347a9 Add "error" Page for special accounts in Mobile (#2114)
* Add "error" Page for special accounts in Mobile
2024-01-06 14:33:59 -08:00
Joel Jeremy Marquez
8bf0f8e5bf [TS migration] MobileBudget tsx (#2081)
* MobileBudget tsx

* Release notes

* Fix type error

* Update upcoming-release-notes/2081.md

Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>

* SyncRefresh.tsx

* Remove loadCategories

---------

Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
2024-01-06 14:32:16 -08:00
Joel Jeremy Marquez
bc07235017 ESLint no-default-export 13 - final (#2185)
* ESLint no-default-export 13 - final

* Release notes

* Fix lint error

* Add api/migrations to override

* Fix lint error
2024-01-06 14:00:13 -08:00
Joel Jeremy Marquez
794476ac51 ESLint no-default-exports 12 - all loot-core folders except server (#2184)
* ESLint no-default-exports 12 - All loot-core folders except server

* Release notes

* Fix imports
2024-01-06 13:49:02 -08:00
Joel Jeremy Marquez
30bc216142 ESLint no-default-exports 11 - icons (last one for desktop-client package) (#2173)
* ESLint no-default-exports 10 - icons

* Fix icon imports + lint errors

* Release notes

* Fix typecheck error

* Fix icon import

* Fix lint error
2024-01-06 13:38:54 -08:00
Joel Jeremy Marquez
6f8d2574ab ESLint no-default-exports 10 - all desktop-client src folders except icons (#2172)
* ESLint no-default-exports 10 - all desktop-client src folders except icons

* Release notes
2024-01-06 12:57:45 -08:00
Joel Jeremy Marquez
9262b46428 ESLint no-default-exports 9 - all components folder (#2171)
* ESLint no default exports 9 - components folder

* ESLint no default exports 9 - components folder 2

* Release notes
2024-01-06 12:35:50 -08:00
Joel Jeremy Marquez
8e1c11e18e ESLint no-default-exports 8 (#2170)
* Fix default imports

* Fix manager Modals import

* ESLint no default exports 8

* Release notes

* Schedules
2024-01-06 12:14:52 -08:00
Joel Jeremy Marquez
6b5ee3f774 ESLint no-default-exports 7 (#2169)
* Fix default imports

* Fix manager Modals import

* ESLint no default exports 7

* Release notes
2024-01-06 11:54:52 -08:00
Joel Jeremy Marquez
02d3f96c20 ESLint no-default-exports 6 - View.tsx (#2120)
* Fix default imports

* Fix manager Modals import

* ESLint no default export part 6 - View.tsx

* Fix default imports

* Fix imports

* Release notes
2024-01-06 11:40:09 -08:00
Joel Jeremy Marquez
829c83afb2 ESLint no-default-exports 5 - Text.tsx (#2119)
* Fix default imports

* Fix manager Modals import

* ESLint no-default-exports part 5 - Text.tsx

* Fix default import

* Release notes
2024-01-06 11:21:25 -08:00
Joel Jeremy Marquez
5d29585fb7 ESLint no default exports 4 (#2118)
* ESLint no-default-exports part 4

* Fix default imports

* Fix default imports

* Fix manager Modals import

* Release notes
2024-01-06 11:06:39 -08:00
Joel Jeremy Marquez
882fd9f5cd ESLint no-default-exports 3 (#2117)
* ESLint no default exports - part 3

* Fix default imports

* Release notes

* Fix Menu
2024-01-06 10:57:19 -08:00
Jason
d203def230 Fix when pressing Enter adds an extra split transaction when no split remains (#2144)
* Fix pressing enter adds split transaction when no split remains

* Added release notes

* Refactor to use find indstead of findIndex
2024-01-06 09:43:06 -08:00
Joel Jeremy Marquez
4d7cfab8bc ESLint no-default-exports 2 - Button.tsx (#2116)
* ESLint no default imports - Button

* Fix

* Release notes

* Fix Button imports
2024-01-06 09:32:06 -08:00
Joel Jeremy Marquez
84a9269ae4 ESLint no-default-exports 1 (#2115)
* ESLint no-default-imports part 1

* Release notes

* Remove Notes.tsx default export

* Fix Notes imports
2024-01-06 09:06:26 -08:00
Jakub Kuczys
83d2472a55 Ask for confirmation when editing date of a locked transaction (#2134)
* Ask for confirmation when editing date of a locked transaction

* Add release note
2024-01-06 08:14:00 -07:00
Khanh Nguyen
458d556e51 added cleared column in csv export (#2138)
* added cleared column in csv export

* added release note
2024-01-06 08:09:48 -07:00
Jason
d9aeb8db1e Change net worth graph to show more detail in compact card view (#2132)
* Change net worth graph to show more detail in compact card view

* Added release notes

* Update release notes

* Update VRT images
2024-01-06 08:06:24 -07:00
David Kus
85c0352abb Adding filter for reconciled transactions (#2108)
* Adding filter for reconciled transactions

* Adding release note
2024-01-06 08:05:49 -07:00
Matiss Janis Aboltins
7a40a645e6 ♻️ (TypeScript) fix strictFunctionTypes violations (pt 5) (#2072) 2024-01-06 12:58:08 +00:00
Matiss Janis Aboltins
7e9921e9e5 ♻️ (TypeScript) fix strictFunctionTypes violations (pt 4) (#2142) 2024-01-06 12:48:45 +00:00
Matiss Janis Aboltins
02aff1acbe 🔖 (24.1.0) Mobile split transactions (#2177) 2024-01-06 09:56:10 +00:00
Matiss Janis Aboltins
e0d66d3083 🐛 (rules) fix filtering in the rules page (#2141) 2024-01-05 19:06:32 +00:00
Joel Jeremy Marquez
d999e466b7 Fix mobile transaction page amount input (#2166)
* Request active edit on focus

* Release notes

* Track previous focus state

* use inputRef to focus

* Focus total amount effect

* Run active edit action synchronously

* Revert unneeded changes

* Run cleanup synchronously

* Cleanup

* runAction

* Space
2024-01-05 07:05:07 -08:00
Martin French
e589163bb7 [Goals] Negate schedule amount to budget if income (#2125)
* Negate schedule amount to budget if income

* Release notes

* Determine sign at initial calc

To ensure no unintended impact to remainder calcs etc.

* Lint fixes
2024-01-03 08:02:07 -06:00
Matiss Janis Aboltins
d2b86d100c 🐛 (import) fix reconciled transactions getting overriden on import (#2140) 2024-01-01 12:44:57 +00:00
Matiss Janis Aboltins
d8996405c4 revert: ♻️ (TypeScript) fix strictFunctionTypes violations (pt 4) (#2128) 2023-12-29 18:32:10 +00:00
Martin French
e548125907 [Bugfix] Fix update transaction API bug (#2127) 2023-12-27 17:20:58 +00:00
Neil
edbcaba89b Custom reports header fix (#2085)
* tablechanges

* notes

* trigger rerun

* fixes

* lint fixes
2023-12-26 17:55:41 +00:00
Joel Jeremy Marquez
b39d106c04 Mobile category modal menu (#1964)
* Mobile category modal menu

* Release notes

* Apply category and group modals

* Category modal updates + notes

* Reduce notes modal view height

* Update CategoryGroupMenu modal view height

* Update CategoryMenu modal view height

* Fix lint error

* No notes text

* Add budget-table testid on edit mode

* Fix lint error

* Fix lint error

* Remove edit mode tooltip

* Update modals

* Updates + Simplify code by removing edit mode

* Optional closeModal arg

* Fix mobile budget table category divider

* Cleanup + close modal on click outside

* collapseModals action

* Fix lint errors

* Category modal hidden menus + Lift edit state to MobileBudget component

* Hide category / group actions when editing title

* Add margins between buttons

* Fix lint errors
2023-12-22 14:35:46 -08:00
Joel Jeremy Marquez
4894118809 Mobile split transactions (#2068)
* Mobile split transactions

* Release notes

* Fix colors

* Update test ids

* AmountInput component on split transactions

* Add notes to mobile split transaction

* Ease of use improvements + functional components

* Ease of use updates

* VRT updates

* Fix AmountInput blur

* Remove negative state in AmountInput + fix vrt
2023-12-22 13:53:39 -08:00
Matiss Janis Aboltins
c6723da780 ♻️ (TypeScript) fix strictFunctionTypes violations (pt 4) (#2071) 2023-12-22 21:20:29 +00:00
Neil
7a8c320560 Custom Reports Donut Graph refactor (#2098)
* donut refactor

* notes

* fixes
2023-12-22 20:31:36 +00:00
Joel Jeremy Marquez
6fbf0fdc10 [ESLint] Enable prefer-const project-wide (#2113)
* Enable prefer-const project-wide

* Release notes

* Fix remaining lint errors
2023-12-22 11:42:40 -08:00
Matiss Janis Aboltins
efaefcfaa9 ♻️ (typescript) moving DeleteFile modal to TS (#2112) 2023-12-22 19:13:49 +00:00
Matiss Janis Aboltins
73d281b6ee ♻️ (eslint) disallow unnecessary curly braces (#2111) 2023-12-22 09:30:05 +00:00
Matiss Janis Aboltins
03e943f383 ♻️ (TypeScript) fix strictFunctionTypes violations (pt 3) (#2070) 2023-12-22 09:29:48 +00:00
Matiss Janis Aboltins
5854dffd50 🐛 fix category spending report (#2096) 2023-12-19 18:40:08 +00:00
Matiss Janis Aboltins
c727e3e980 ♻️ (TypeScript) fix strictFunctionTypes violations (pt 2) (#2066) 2023-12-19 18:01:06 +00:00
youngcw
b385c715ef [Goals]: don't run templates in hidden groups (#2100)
* don't run templates in hidden groups

* note

* lint
2023-12-18 08:00:30 -07:00
youngcw
77e550f028 [Goals]: fully skip budgeted categories when only applying (#2099)
* remove budgeted categories from list when only applying

* cleanup

* speed up the category removal

* note
2023-12-18 07:58:49 -07:00
DJ Mountney
9ea8e86bb3 (ESLint) Enforce filename extensions for jsx (#2101) 2023-12-18 11:00:47 +00:00
Neil
273fa8cd59 Custom reports: make views global (#2094)
* make views global

* notes and lint fixes

* fixes

* lint fix

* fix

* fix
2023-12-17 22:18:57 +00:00
IzStriker
d4c8b5fa16 [TS migration] Reafactor tooltips.js to use typescript (#2073) 2023-12-17 20:55:16 +00:00
Neil
e62e8ca24e Custom Reports Enable Legend (#2078)
* enable Legend

* notes

* adding type

* overhaul

* calculateLegend
2023-12-16 21:38:45 +00:00
Neil
4497fbcb10 Custom Reports date paused (#2080)
* work

* fixes

* updates

* update table layout

* revert changes

* notes

* updae names and improve flow
2023-12-15 15:05:06 -08:00
Matiss Janis Aboltins
8a15caf42d ♻️ (TypeScript) fix strictFunctionTypes violations (pt 1) (#2065) 2023-12-15 18:09:36 +00:00
iOSLife
af9efa09f0 API – Return Hidden for Category and CategoryGroup (#2062) 2023-12-15 18:09:14 +00:00
HansiWursti
a5557ca032 Fix issue 1878 (#2093)
* Fix issue 1878

* Add release notes
2023-12-15 10:38:47 -07:00
HansiWursti
0bf587bcf5 Added BG Color to view to fix Issue 2089 (#2092)
* Added BG Color to view to fix Issue 2289

* add release notes
2023-12-15 08:46:52 -07:00
Matiss Janis Aboltins
e11b65719e 👷 (ci) adding helper comments for failed CI jobs (#2074) 2023-12-14 19:02:05 +00:00
Julian Dominguez-Schatz
c09a85f340 Add schedule end date/count field (#1899)
* Add "end" field with date/count options

* Use "end" field to generate schedule

* Show "end" field in recurring description

* Disable weekend before/after picker when not enabled

* Add release notes

* Fix failing typechecks

* Add some description tests

* PR feedback

* 'Features', not 'Feature'

* Fix goal templates infinite loop

* Empty commit to bump ci

* Fix bug where schedule templates in the past would apply incorrectly

For example, if you had a schedule which started in November 2023 for
1.00, and you applied the schedule in October 2023, then you would end
up with a value of 0.50 applied in October.

* Fix handling of schedules with an end date

This commit also includes a refactor of the skip-weekend logic: rather
than referring only to dates with skipped weekends (which requires
checking whether the "next date" request worked correctly), we track a
"base date" which is the previous value of the schedule according to the
rrule, excluding any weekend-skipping. This lets us use `addDays(baseDate, 1)`
to get the next occurrence, regardless of the weekend behaviour.

Doing things this way ensures that the loop will always make progress.

* Only compute skipped weekend if weekend skips were requested

* Fix typo in iterate-schedule-occurrences code

We should be using `nextBaseDate` to derive the next base date, not
`nextDate`; this is because we want the base date to be guaranteed to
make progress in each loop iteration, so we can finish in at most 30
iterations without duplicate base dates.

* Use const

* Revert const -> let for one mutable variable
2023-12-14 05:43:16 -06:00
Joel Jeremy Marquez
f90fe04b2b Add left and right margins to modals (#2082)
* Add left and right margins to modals

* Release notes
2023-12-13 19:47:47 -08:00
spezzino
ca55d9c85e add support for auto theme (#1906)
* add support for system theme

* add release notes

* Rename auto theme

* add theme selector

* update visual snapshots

* update snapshots

* update snapshots
2023-12-13 15:10:14 -07:00
Neil
4ada92071e Custom Reports typescript updates and small functional changes (#2046)
* work

* notes

* error fixes

* updates

* card fix

* fix filters

* fixes
2023-12-12 11:34:58 -08:00
DJ Mountney
e848946898 Remove older version of react-router (#2064) 2023-12-12 17:58:22 +00:00
Neil
9da05543c3 Dark Theme color fixes (#2048)
* subdued fix

* notes

* fixes

* upcoming

* autocomplete menu

* make notes consistenet on budget page

* VRT updates
2023-12-12 06:40:52 -08:00
Neil
8a721bf2e0 Renaming variables for reports files (#2069)
* work

* notes

* error fixes

* updates

* card fix

* fix filters

* splitting PR work

* notes

* fixes

* lint fix

* Update upcoming-release-notes/2069.md

Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>

---------

Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
2023-12-12 05:06:05 -08:00
Lucas
36914abcd4 Cashflow.js -> Cashflow.tsx (#2005) 2023-12-12 10:21:48 +00:00
Neil
cd2d186599 Add new types for reports files (#2067)
* work

* notes

* error fixes

* updates

* card fix

* fix filters

* splitting PR work

* notes
2023-12-11 16:31:25 -08:00
Joel Jeremy Marquez
b3bcff094d [Maintenance] ESLint - prefer-const on loot-core server (#2032)
* Another round of prefer-const rule updates

* Release notes

* Fix test error

* Fix sync.ts
2023-12-09 13:22:16 -08:00
Matiss Janis Aboltins
7955fe7aed ♻️ (typescript) refactor FixedSizeList to TS (#2022) 2023-12-07 19:30:29 +00:00
Neil
2741422c0a Custom Reports optimization (#1988)
* Range fix and payee fix

* bug fixes and UI tweaks

* range options, hover UI

* Select - UnSelect All Buttons

* fix hidden group bug

* YAxis PrivacyFilter

* notes

* more privacyFilter graphs

* overflowY fix

* Loading Indicator

* Fix Filter button hover

* data revamp

* review fixes

* LoadingIndicator fixes

* remove a loop

* filterbuttontype

* lint fixes

* review fixes

* filtersbutton

* updates

* Split out functions to separate files

* uncategorized optimization

* rename ambiguous variables

* notes

* remove indexStack

* renaming variables

* Improve scrolling of tableGraph

* revert renaming variables

* code fixes

* lint fixes

* review fixes

* fix

* review fixes

* variable name changes

* const eslint fixes

* remove indexStack
2023-12-06 09:33:19 -08:00
Tuhin Ghose
cde4551eb7 [Maintenance] Migrating util.js and chartTheme.js to typescript (#2009) 2023-12-06 08:26:17 +00:00
Matiss Janis Aboltins
7174fccfe4 ♻️ (typescript) adding strict typings to utils.ts (#2023) 2023-12-06 08:22:33 +00:00
Joel Jeremy Marquez
0303292a28 [Maintenance] ESLint: prefer-const components folder part 2 (#2034)
* ESLint prefer-const components folder part 2

* Release notes
2023-12-05 13:36:29 -08:00
Joel Jeremy Marquez
f95df06800 [Maintenance] ESLint: prefer-const components folder part 1 (#2033)
* ESLint prefer-const components folder part 1

* Release notes
2023-12-05 13:14:37 -08:00
DJ Mountney
3680d45814 Add API category tests (#2036) 2023-12-05 20:08:48 +00:00
Joel Jeremy Marquez
dc872647a9 [Maintenance] Use Page component for mobile pages (#1993)
* Use Page component for mobile pages

* Release notes

* Use Button instead of Link in MobileBackButton

* Update mobile budget table to use Page component

* Settings page cleanup

* Fix lint error

* Updates + small font size increase in  page headings

* Fix rebase error

* Button height

* Revert payees navtab
2023-12-05 11:18:35 -08:00
Matiss Janis Aboltins
7a45d467b7 ♻️ (eslint) enable 'react/no-children-prop' rule and fix issues (#2029) 2023-12-05 18:58:22 +00:00
Joel Jeremy Marquez
79aa07ff1a Fix bulk edit field modal in desktop (#2031)
* Fix bulk edit field modal in desktop

* Release notes
2023-12-05 09:44:30 -08:00
Kyle Mckay
c4ff099f26 Fix failure to create category with deleted name (#2002)
* Fix failure to create category with deleted name

I'm not 100% familiar with the design of the data model so may have
misinterpreted what's going on here, but how I read this:

- The tombstone flag is used to soft delete categories.
- When creating a new category, duplicate names within a group are
  prevented.
- When checking for duplicate names, the tombstone flag was unchecked
  which meant deleted categories were falsely blocking creation.

I had a look at category deleteion logic to verify that simply including
the tombstone flag in this check is sane and see that there's a category
mapping being maintained to redirect one category to another on
deletion. So from what I can tell the correct behaviour here is to allow
a new category with the previously deleted name, rather than to revive
the old category (to preserve that old mapping lineage).

* Add release note

* Add regression test
2023-12-05 08:48:16 -07:00
Vishnu Kaushik
ad2b577515 Fix #1977: Filter Amount formatting issue (#2008)
* Fix #1977: Filter Amount formatting issue

* Fix linting and add release notes
2023-12-05 08:42:54 -07:00
Michael Clark
5f52801869 [Maintenance] Adding Aria labels for accessibility (#2025) 2023-12-04 19:31:38 +00:00
Ameek Singh
6bfd9586e0 [Maintenance] Converting dateRange and useReport to tsx (#2007) 2023-12-02 22:43:56 +00:00
Michael Clark
f36713e0be [Maintenance] BudgetTotals, GoCardlessLink, Import, WelcomeScreen components to Typescript. (#2004) 2023-12-02 22:43:29 +00:00
DJ Mountney
8b20169b59 Add some initial api tests for budgets and accounts (#1991) 2023-12-02 20:42:53 +00:00
Matiss Janis Aboltins
da03acc394 🔖 (23.12.0) darkmode, transaction locking, mobile updates and more (#2003) 2023-12-02 19:26:02 +00:00
Joel Jeremy Marquez
5d1f2d48ae Larger mobile autocomplete fonts and paddings (#1900)
* Larger mobile autocomplete fonts and paddings

* Release notes

* VRT + update tests

* Update tests

* Update data-highlighted and tests

* Use styles text

* Fix tests

* Fix tests

* Fix tests

* Fix tests

* Fix tests

* Fix tests

* Adjust Add Transaction padding + VRT updates

* Larger autocomplete text and divider

* Fix rebase

* Fix rebase

* Fix icons

* Adjust fonts

* Fix lint errors

* PR feedback

* VRT

* Update embedded autocomplete highlight hover color

* Refactor create payee button

* Embedded create payee button color

* Dummy change to re-run CI
2023-12-01 13:10:01 -08:00
youngcw
c96782d7c1 [Goals]: Sort the priorities properly (#2000)
* sort properly

* note
2023-12-01 08:40:35 -07:00
Neil
5d61dfce68 Update rules API (#1987)
* Update rules.ts

* notes

* Update packages/loot-core/src/server/accounts/rules.ts

Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>

* Entity update

* fix

---------

Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
2023-11-30 10:27:51 -08:00
youngcw
5a81a25b7e fix broken schedule amount sign in firefox (#1992)
* fix broken schedule amount sign in firefox

* note

* delete line
2023-11-30 09:50:31 -07:00
Stefan Hall
29f323e721 Validates minimum node version to 18.12.0 for @actual-app/api npm package (#1980) 2023-11-28 19:19:18 +00:00
shall0pass
c462604833 [Bugfix] Goals: Schedule overbudget condition (#1984)
* fix overbudget in schedules template

* release note
2023-11-27 18:18:26 -06:00
Neil
e48f36cd20 Custom Reports Bug Fixes (#1930)
* Range fix and payee fix

* bug fixes and UI tweaks

* range options, hover UI

* Select - UnSelect All Buttons

* fix hidden group bug

* YAxis PrivacyFilter

* notes

* more privacyFilter graphs

* overflowY fix

* Loading Indicator

* Fix Filter button hover

* review fixes

* LoadingIndicator fixes

* remove a loop

* filterbuttontype

* review fixes

* filtersbutton

* updates
2023-11-26 12:19:58 -08:00
Michael Clark
1ed84059d7 [Maintenance] Update ConfirmCategoryDelete, GoCardlessExternalMsg, ManageRulesModal to tsx (#1972) 2023-11-26 19:49:26 +00:00
William Kurniawan
5d8b96a749 Fix cancel add category group (#1975) 2023-11-26 16:38:05 +00:00
Julian Dominguez-Schatz
0920bdc3fa Fix bug in rule transaction apply preventing amount overrides (#1976) 2023-11-26 16:35:09 +00:00
Mohamed Muhsin
c3a9b4dda3 [refactor] Migrate Discover schedules to tsx (#1946) 2023-11-26 15:51:18 +00:00
Matiss Janis Aboltins
eab0068428 🎨 (fix) gocardless success button color (#1970) 2023-11-26 15:50:28 +00:00
Joel Jeremy Marquez
0d1b962b2f Fix budget amount not saved when clicking on another budget cell (#1965)
* Fix budget amount not saved when clicking on another budget cell

* Release notes
2023-11-25 16:52:38 -08:00
Joel Jeremy Marquez
67b9143dfc Transfer and Off Budget category in mobile (#1955)
* Transfer and Off Budget category in mobile

* Release notes

* Fix typo

* Update lookupName callers
2023-11-25 16:52:05 -08:00
Zach Whelchel
e7f298e32a [Bugfix] Fixed modal error and transactions now unlock when moved across accounts. (#1962) 2023-11-25 14:52:01 +00:00
Matiss Janis Aboltins
516f0c259f 🔧 (vrt) make screenshot comparisons more strict (#1960) 2023-11-25 14:34:24 +00:00
Neil
2c87c60328 Dark Theme Live (#1925)
* Remove Featureflag

* notes

* VRT updates

* Dark Theme fixes

* VRT updates

* VRT updates

* Revert "VRT updates"

This reverts commit 7d669f2df2.

* Revert VRT

Reverting VRT

* VRT updates

* pillbackground

* VRT updates

* Theme Selector

* VRT Updates

* notes

* VRT updates
2023-11-24 13:20:03 -08:00
DJ Mountney
b4fd6e86ed Create category rules for ynab imports (#1944) 2023-11-23 08:36:15 +00:00
Matiss Janis Aboltins
a3e71a8b49 🔧 (eslint) enable object-shorthand:properties rule (#1959) 2023-11-22 22:52:53 +00:00
Joel Jeremy Marquez
aa9c992f2e Part 2 of eslint prefer-const (#1958)
* Part 2 of eslint prefer-const

* Release notes

* Update
2023-11-22 14:28:09 -08:00
Zach Whelchel
ca498b19cc Lock transactions after reconcilliation (#1789) 2023-11-22 22:14:34 +00:00
Matiss Janis Aboltins
8be2389fd9 ♻️ (typescript) ported gocardless auth file to TS (#1941) 2023-11-22 21:54:43 +00:00
Matiss Janis Aboltins
46988800ef ♻️ (typescript) refactor global-events to TS (#1942) 2023-11-22 19:35:30 +00:00
Joel Jeremy Marquez
881999bcfe ESLint prefer-const (#1956) 2023-11-22 11:12:22 -08:00
Joel Jeremy Marquez
9c124e8e44 Consistent button size in budget title bar + use zondicon eye icon (#1951)
* Consistent button size in budget title bar + use zondicon eye icon

* Release notes

* VRT
2023-11-22 11:02:02 -08:00
Kyle Mckay
3306963c54 [Maintenance] Refactor payee table to typescript (#1948) 2023-11-22 17:58:07 +00:00
Kyle Mckay
baa474843a [Maintenance] Split up large payee management components file (#1950) 2023-11-21 19:27:20 +00:00
Matiss Janis Aboltins
6ed1b58321 🔧 (eslint) disallow using 'var': no-var rule (#1949) 2023-11-21 19:18:13 +00:00
Paul Sukow
28266ed9a2 Fix switching budget from rollover budget to report budget and immediately back does not work (#1933)
* Fix switching budget from rollover budget to report budget and immediately back does not work

* Pr Changes
2023-11-21 08:15:18 -08:00
Julian Dominguez-Schatz
cd6bd9ece8 Fix crash when undoing a rule application (#1885)
* Undo shouldn't open modal if it wasn't open before

* Add release notes
2023-11-21 00:19:31 -08:00
Michael Huynh
fb2f712c16 Fix flickering scroll bar on mobile transaction input (#1782) (#1929) 2023-11-20 12:46:47 -08:00
Joel Jeremy Marquez
f58fae8d16 Support multiple months in experimental ofx parser (#1921)
* Support multiple months in experimental ofx parser

* Release notes

* Fix lint error
2023-11-20 12:44:28 -08:00
Joel Jeremy Marquez
a10b10a87b [regression] Remove extra padding in mobile accounts page (#1916)
* [regression] Remove extra padding in mobile accounts page

* Release notes

* VRT
2023-11-20 12:44:12 -08:00
DJ Mountney
47007232b8 Switch from en-ZA to en-SE for space-comma NumberFormat (#1938) 2023-11-20 20:28:55 +00:00
Stefan Hall
4761e9ce2f Specify minimum node version for @actual-app/api nom package (#1934) 2023-11-20 18:37:38 +00:00
Kyle Mckay
849262c95e [Maintenance] Refactor amount input to typescript (#1936) 2023-11-20 18:29:19 +00:00
Shazib Hussain
e6e184c412 Fix Electron Build (#1926) 2023-11-20 18:16:25 +00:00
Shazib Hussain
9c6d9ecf0a Don't allow duplicate category names (per group) (#1833)
* Remove author from electron package.json

* Don't allow dupes in cat names (per group)

* Release Notes

* Handle reorders and moves

* Fix linter warnings

* Fix typecheck

* Show the name of the duplicate category

* missed func call

* Upper case before compare

* lint fixes

---------

Co-authored-by: Shazib Hussain <contact@shazib.com>
2023-11-20 09:41:13 -07:00
Joel Jeremy Marquez
65273127f5 Mobile report budget (#1880)
* Mobile report budget

* Release notes

* Update bindings

* Cleanup

* Mobile report budget summary + reuse desktop components
2023-11-18 20:22:05 -08:00
Joel Jeremy Marquez
b5530085bb Mobile create account (#1853)
* Mobile create account

* Release notes

* Update add button style

* Add back desktop page header padding

* Empty accounts handling

* decimal keyboard on CreateLocalAccount balance input

* VRT updates
2023-11-18 20:21:41 -08:00
Michael Clark
9ec82d52c9 [Maintenance] Update CloseAccount, AccountAutocomplete, SavedFilterAutocomplete, PayeeAutocomplete components to Typescript. (#1923) 2023-11-18 13:21:21 +00:00
shall0pass
93922567fc [Bugfix] Goals: Fix Schedule Infinite loop (#1917)
* logic

* release note
2023-11-17 11:09:13 -06:00
Neil
7c0b7a2f17 Duplicate color removal (#1911)
* duplicate color removal

* notes
2023-11-16 02:17:51 -08:00
Neil
e8055bbc35 Mobile pages standardized (Colors/UI) (#1874)
* Mobile Color Consistency

* VRT updates

* color updates

* notes

* Sync Text

* Adjust header font smaller

* color and format adjustments

* tidying header buttons

* color and button adjustments

* Header Text color

* VRT updates

* back button changes

* VRT changes

* adjust buttons

* lint fix

* darkTheme header background

* VRT updates

* VRT updates
2023-11-16 00:08:46 -08:00
Neil
7fdd9767ba sideBar Dark Color changes (#1887)
* sideBar Dark Color changes

* notes

* VRT updates

* test fixes

* color fixes

* Revert "test fixes"

This reverts commit 3c6131b2c4.

* Revert "VRT updates"

This reverts commit b84d7659a4.

* color update

* VRT updates

* calendar changes

* VRT update

* notes update

* color change

* Revert "VRT update"

This reverts commit 4c5aba4247.

* Revert "VRT updates"

This reverts commit ba142b0653.

* VRT updates
2023-11-16 00:05:58 -08:00
Neil
1f105999af Customizeable Reports (#1791)
* Reorganize and add graphs

* Create Customizable Chart

* Notes

* Hide Menu update Donut

* lint fixes

* Organize Menus

* Change Title

* UI changes

* UI updates

* Add Data Table

* Functionality additions and Privacy Filters

* Date filters working and formatting changes

* Fix default spreadsheet and add tableGraph

* Integrate Summary Data and Split Legend

* started adding functionality on charts

* list fix

* Enabling more graphs, fixing errors

* Legend, interactions, Empty Rows Filter

* fixes for EmptyRows/interactions/legends

* formatting UI and filtering data

* format date

* fix errors

* Fix Legend Order

* lint fixes

* Add tooltips

* Feature Flag

* fix overview card, fix offbudget checkbox

* Revamped dataType, added scrollBars

* data display adjustments

* data spreadsheet updates/groups added to matrix

* Add Category Selector

* Add Labels Button

* formatting fixes

* Add Averages to dataTable

* data bug fix

* Added all type back in with exceptions

* formatting

* split assets/debts, add Uncategorized

* bug fixes and UI updates

* add scrollbars to table

* formatting dataTable

* tooltips, navigation and graph labels

* Code clean-up and re-org

* revert color change

* Change labels name

* organize files

* code cleanup

* Tooltip Colors

* Descoping legend for future PR

* descope legend & rename split

* rename type variable to be more descriptive

* adjustments for sankey and eslint merges

* notes update

* code review fixes

* code fixes

* fix date selections
2023-11-16 00:04:29 -08:00
Stefan Hall
f970263c72 Add typings condition operators for Action class (#1909) 2023-11-15 17:18:06 +00:00
Neil
fecc9178b3 Color Consolidations (#1868)
* Color normalization

* color updates

* VRT updates

* update colors table

* VRT updates

* more VRT updates

* pillborder

* VRT updates

* Revert "VRT updates"

This reverts commit ae58317b00.

* Revert "more VRT updates"

This reverts commit a316745efd.

* Revert "VRT updates"

This reverts commit 03797af362.

* Revert "VRT updates"

This reverts commit 454adac251.

* VRT refresh

* border color

* Checkbox Border

* VRT updates
2023-11-15 01:18:46 -08:00
Matiss Janis Aboltins
08c80b6f58 ⬆️ (yarn) upgrade to v4 and better-sqlite3 to v9.1.1 (#1902) 2023-11-14 08:34:12 +00:00
Joel Jeremy Marquez
a3995582c4 Swipe up mobile navbar (#1758)
* Swipe up mobile navbar to reveal more menus

* Release notes

* Revert navbar button height to 70

* Reduce debounce value

* More tabs coming soon

* VRT update

* VRT update

* Fix: e2e tests

* Smoother mobile navtabs

---------

Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
2023-11-13 14:14:16 -08:00
Shaan Khosla
5008eb022f Sankey with Recharts (#1739)
* added sankey plot

* fix sankey

* formatting

* formatting

* lint

* reduce margin

* experimental flag

* update location of recharts install

* truncate tooltip

* add titles to node and sizing

* better filtering

* comments, types, and check if data exists

* don't log

* clean up extra views

* responsive container, and fix label in graph

* change back

* fix bug and font

* static tooltip

* update overview to fit both sankey and category spending

* overview sankey

* increase iterations, fix right text, and release notes

* remove is out

* lint

* fix tooltip space

* fix margins

* format and efficient sql payees query

* restructure category sum value

* add tooltip names...again

* conditionally use container

* use useCategories
2023-11-12 16:01:12 -05:00
Joel Jeremy Marquez
353e7be31f Support ofx INVSTMTMSGSRSV1 (#1876)
* Support ofx INVSTMTMSGSRSV1

* Release notes
2023-11-11 13:28:48 -08:00
Michael Clark
30c7024663 [Maintenance] Convert ExpenseGroup, ExpenseCategory, IncomeCategory components to Typescript. (#1897) 2023-11-11 21:05:45 +00:00
youngcw
22efe74ec8 add budget tables to the aql schema (#1895)
* add budget tables to the schema

* note

* note fix

* lint
2023-11-11 13:08:14 -07:00
Matiss Janis Aboltins
5792b15cb5 🔧 (eslint) enabling 'react/no-unstable-nested-components' rule (#1893) 2023-11-11 18:46:28 +00:00
shall0pass
168b79a178 [Maintenance] Goals break out goal target calculations to individual files (#1888)
* move goal calculations to separate files by type

* bug squashing

* release note
2023-11-11 10:48:33 -06:00
Michael Clark
7062f2a7d9 Maintenance: BudgetSummaries, MonthPicker, SidebarCategory components to tsx. (#1879) 2023-11-10 21:38:18 +00:00
Matiss Janis Aboltins
af666458d3 (VRT) darkmode visual regression tests (#1860) 2023-11-10 21:17:07 +00:00
Neil
184b302273 Remove Static Colors (#1875)
* Color Changes

* notes
2023-11-09 15:51:58 -08:00
Neil
3004f336da Final Colors Consolidation (#1871)
* Color Updates

* color change

* notes

* fix preview background
2023-11-09 15:45:59 -08:00
James Costian
45bb7696c3 Support full month names when importing CSV transactions (#1862)
Support dates like "Dec 24, 2020" and "december 24, 2020"
2023-11-09 10:16:23 -07:00
James Costian
199a9f838d Make Select All respect filters and splits (#1864) 2023-11-09 07:11:51 -08:00
youngcw
df5aa3186c Goals: Save full goal value and add an indicator of goal status (#1780)
* first pass at progress bar

* db migration / enter goal in db

* add getGoal function

* stabilize

* whoops

* TS

* reset goal in db if no template found

* reconfirm

* release note

* typo

* rename migration

* to ms

* move priority logic, consistent variable names,

* fixup

* clear goal if template removed

* Visual goals (#40)

* 🔥 removing privacyMode feature flag (#1688)

* 🎨  fix multiline label in schedules modal (#1687)

* Update Visual Regression README File (#1689)

* Fix typo in GoCardlessLink.js (#1684)

happend -> happened

* queried cleared balance for tooltip (#1678)

* Dark Theme Reports/Settings (#1512)

* 🐛 Mobile account transaction list: Fix sticky date section headers (#1698)

* 👷  do not cancel github ci jobs on master branch (#1692)

* Sidebar Account Fix (#1703)

* Dark Theme Final (#1513)

* Category autocomplete should only search selectable categories  (#1681)

* set colors based on a goal value

* extra comment

---------

Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
Co-authored-by: Crazypkr1099 <nicholas.lacasse430@gmail.com>
Co-authored-by: Ikko Eltociear Ashimine <eltociear@gmail.com>
Co-authored-by: Shaan Khosla <35707672+shaankhosla@users.noreply.github.com>
Co-authored-by: Neil <55785687+carkom@users.noreply.github.com>
Co-authored-by: Trevor Farlow <trevdor@users.noreply.github.com>

* update release note

* lint

* use null as cleared state

* show goal status via colors (#41)

* cleanup

* I think its working

* lint

* fix report budget, by adding in the goal coloring

* fix the error by adding colors to the report side (#42)

* [refactor] Migrate Schedules Table to typescript (#1691)

* 🔧  removing unnecessary manual module resolution (#1707)

* 🐛 (mobile) scrolling in lists with pull-to-refresh (#1706)

* 💄 (mobile) updating apple home-screen icon (#1705)

* Enhance Y-Axis Scaling on Net Worth Graph (#1709)

* fix report budget, by adding in the goal coloring

---------

Co-authored-by: Mohamed Muhsin <62111075+muhsinkamil@users.noreply.github.com>
Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
Co-authored-by: Crazypkr1099 <nicholas.lacasse430@gmail.com>

* report budget database updates

* Goal progress bar (#1734)

* first pass at progress bar

* db migration / enter goal in db

* add getGoal function

* stabilize

* whoops

* TS

* reset goal in db if no template found

* reconfirm

* release note

* typo

* rename migration

* to ms

* move priority logic, consistent variable names,

* fixup

* clear goal if template removed

* Visual goals (#40)

* 🔥 removing privacyMode feature flag (#1688)

* 🎨  fix multiline label in schedules modal (#1687)

* Update Visual Regression README File (#1689)

* Fix typo in GoCardlessLink.js (#1684)

happend -> happened

* queried cleared balance for tooltip (#1678)

* Dark Theme Reports/Settings (#1512)

* 🐛 Mobile account transaction list: Fix sticky date section headers (#1698)

* 👷  do not cancel github ci jobs on master branch (#1692)

* Sidebar Account Fix (#1703)

* Dark Theme Final (#1513)

* Category autocomplete should only search selectable categories  (#1681)

* set colors based on a goal value

* extra comment

---------

Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
Co-authored-by: Crazypkr1099 <nicholas.lacasse430@gmail.com>
Co-authored-by: Ikko Eltociear Ashimine <eltociear@gmail.com>
Co-authored-by: Shaan Khosla <35707672+shaankhosla@users.noreply.github.com>
Co-authored-by: Neil <55785687+carkom@users.noreply.github.com>
Co-authored-by: Trevor Farlow <trevdor@users.noreply.github.com>

* update release note

* lint

* use null as cleared state

* show goal status via colors (#41)

* cleanup

* I think its working

* lint

* fix the error by adding colors to the report side (#42)

* [refactor] Migrate Schedules Table to typescript (#1691)

* 🔧  removing unnecessary manual module resolution (#1707)

* 🐛 (mobile) scrolling in lists with pull-to-refresh (#1706)

* 💄 (mobile) updating apple home-screen icon (#1705)

* Enhance Y-Axis Scaling on Net Worth Graph (#1709)

* fix report budget, by adding in the goal coloring

---------

Co-authored-by: Mohamed Muhsin <62111075+muhsinkamil@users.noreply.github.com>
Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
Co-authored-by: Crazypkr1099 <nicholas.lacasse430@gmail.com>

* report budget database updates

* Fix schedule searchbar (#1729)

---------

Co-authored-by: youngcw <calebyoung94@gmail.com>
Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
Co-authored-by: Crazypkr1099 <nicholas.lacasse430@gmail.com>
Co-authored-by: Ikko Eltociear Ashimine <eltociear@gmail.com>
Co-authored-by: Shaan Khosla <35707672+shaankhosla@users.noreply.github.com>
Co-authored-by: Neil <55785687+carkom@users.noreply.github.com>
Co-authored-by: Trevor Farlow <trevdor@users.noreply.github.com>
Co-authored-by: Mohamed Muhsin <62111075+muhsinkamil@users.noreply.github.com>

* working dynamic colors.  Need to figure out what changes are actually needed

* cleanup

* more cleanup

* lint

* reset the goal when applying a single template

* make getCategory function

* remove some unneeded changes

* actually remove the changes, not just comment

* cleanup some unneeded code that was causing some bugs. Works for me, but should be vetted more

* lint

* add json definitions to database

* use template feature flag to enable colors

* some fixes

* don't set goals for remainders, remove unneeded change

* lint

* release note

* lint again

* fix mobile crash

* undo changes in CellValue.tsx

* lint

* use getStyle

* move status calc to helper

* lint

* recommendations

* suggestion

Co-authored-by: Joel Jeremy Marquez <joeljeremy.marquez@gmail.com>

---------

Co-authored-by: shall0pass <20625555+shall0pass@users.noreply.github.com>
Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
Co-authored-by: Crazypkr1099 <nicholas.lacasse430@gmail.com>
Co-authored-by: Ikko Eltociear Ashimine <eltociear@gmail.com>
Co-authored-by: Shaan Khosla <35707672+shaankhosla@users.noreply.github.com>
Co-authored-by: Neil <55785687+carkom@users.noreply.github.com>
Co-authored-by: Trevor Farlow <trevdor@users.noreply.github.com>
Co-authored-by: Mohamed Muhsin <62111075+muhsinkamil@users.noreply.github.com>
Co-authored-by: Joel Jeremy Marquez <joeljeremy.marquez@gmail.com>
2023-11-08 12:44:45 -07:00
Sreetam Das
1c68c3f964 [Bugfix]: Account filter for budgeted and offbudget accounts (#1867)
* Update AQL filter for budgeted and offbudget accounts

* Add release note
2023-11-07 11:52:32 -08:00
Neil
a15f80e771 Warning Color Consolidations (#1798) 2023-11-05 17:29:55 +00:00
Jesse Visser
be18891957 Enhancement: Add option to select in/out field during import (#1788) 2023-11-05 14:49:00 +00:00
Stefan Hall
a6dae398da fixed:issue->#1835 Support NYNAB import of transactions that contain subtransactions tha… (#1836) 2023-11-05 14:42:23 +00:00
Joel Jeremy Marquez
d8c885bf4e Mobile budget pull down to sync (#1858)
* Mobile budget pull down to sync

* Release notes
2023-11-04 16:45:16 -07:00
Evan Trujillo
f94d8aff9f [Bugfix]: Dark mode - darker tint for pageTextLink (#1765) 2023-11-04 21:47:02 +00:00
Seth Gillett
a549cdf0e7 fixed:issue->#1762 Schedule creation modal notify user of weekend skip option (#1840) 2023-11-04 15:58:26 +00:00
凯 - kǎi
b7a2f16246 fix styling of transaction page on mobile (#1820) 2023-11-04 15:31:35 +00:00
Michael Clark
0ec88ecc24 Maintenance: Moving some components to Typescript (#1834) 2023-11-04 15:23:09 +00:00
Matiss Janis Aboltins
39fa9f2097 🔖 (23.11.0) (#1859) 2023-11-04 09:27:13 +00:00
Matiss Janis Aboltins
9040cab600 🐛 fix: bring back rollover arrow in budget page (#1856)
Closes #1845
2023-11-03 18:44:05 +00:00
Matiss Janis Aboltins
e56cb4bc85 🐛 fix: add missing top border for budget menu icon (#1855)
* 🐛  fix: add missing top border for budget menu icon

* Release notes
2023-11-03 18:33:12 +00:00
Joel Jeremy Marquez
fc08baf7ae Fix mobile budget click handlers (#1844)
* Fix mobile budget click handlers

* Release notes
2023-10-31 07:48:32 -07:00
Joel Jeremy Marquez
7325153da1 Mobile balance cover/transfer/rollover overspending (#1802)
* Mobile balance cover/transfer

* Release notes

* Fix errors

* Cleanup

* Fix hit boxes and add line clamp

* Fix styling

* Prevent simultaneous field edits

* Use onPointerDown

* Remove balanceTooltip close effect
2023-10-29 20:26:12 -07:00
Joel Jeremy Marquez
e12793e7eb Inline mobile category and group edit (#1781)
* Inline mobile category and group edit

* Release notes

* Allow one edit at any time

* Mobile: long press to edit category/group

* Mobile: set userSelect: 'none'

* Move getLongPressEvents

* Add role to prevent tap-to-search

* Add role to text

* Prevent default on longpress

* Add tabIndex to disable tap-to-search

* Add back userSelect

* Role updates

* Remove userSelect

* Revert long press category name

* Remove unused import

* Fix lint error

* Edit mode

* Do not allow to start an edit when another  field is currenty being edited

* Fix blur event not firing

* Grey out zero spent or balance values

* VRT update

* Fix budget cell being zero on click

* Delete useLongPress
2023-10-28 09:42:47 -07:00
Kit PANG
5fe146ee0a Allow categorise transfer to off budget accounts on mobile (#1824)
* Allow categorise transfer to off budget accounts

* Add release notes
2023-10-27 09:48:01 -07:00
Joel Jeremy Marquez
0af2987cef Hide mobile nav tabs on scroll (#1745)
* Hide mobile nav tabs on scroll

* Release notes

* Reduced navbar bottom padding

* Make hide transition a bit faster

* Update scroll defaults

* VRT snapshots

* Fix: safari scroll bounce effect disrupting the show/hide of bottom nav

* Update release notes

* No tap highlight on MobileNavTabs

---------

Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
2023-10-22 19:12:39 -07:00
Joel Jeremy Marquez
e266093a4a Custom useNavigate which tracks previous location (#1808)
* Custom useNavigate which tracks previous location

* Release notes

* Fix lint errors
2023-10-22 11:41:07 -07:00
Michael Clark
19f0efb654 Maintenance: Converting Utils and Sort to typescript (#1823) 2023-10-22 16:18:59 +01:00
Neil
e0b2eab475 Error Color Consolidation (#1756)
* Error Color Consolidation

* notes

* color changes
2023-10-21 14:33:30 -07:00
Neil
25f4e2a8b5 Page color updates (#1799)
* page color updates

* notes
2023-10-21 14:23:02 -07:00
Shaan Khosla
d338a73794 add styling back to cash flow graph (#1819)
* add styling back

* release notes

* vrt
2023-10-21 10:21:16 -07:00
Shaan Khosla
75f2bf8b1b 2 new vrt tests for reports (#1814) 2023-10-19 17:33:31 +01:00
Michael Clark
eb54487d8e Maintenance: Convert FixEncryptionKey, Loading, AnimatedLoading components to TypeScript (#1784) 2023-10-19 08:14:50 +01:00
Michael Clark
5cc6bad34b Maintenance: Updating icons generator to output typescript (#1785) 2023-10-19 08:13:04 +01:00
Neil
acf4456077 darkMode Fixes (#1800)
* darkMode Fixes

* notes

* AutoComplete Colors

* Sync button fix

* dark color changes
2023-10-16 07:18:33 -07:00
youngcw
b45615e6fc Darkmode: fix theming on budget table (#1795)
* fix darkmode for budget table

* note

* lint

* fix totals line, fix group colot
2023-10-16 07:13:32 -07:00
Michael Huynh
38609ee25a Add user-friendly theme option labels (#1796) (#1797) 2023-10-16 07:05:01 -07:00
Joel Jeremy Marquez
cae2b9cd5a Allow linked child transactions (#1759)
* Allow linked child transactions

* Release notes
2023-10-13 21:19:42 -07:00
Joel Jeremy Marquez
4cacc845c0 Fix mobile budget header sync button (#1783)
* Fix mobile budget header sync button

* Release notes
2023-10-11 00:21:29 -07:00
Shaan Khosla
3dfbd23f96 POC Recharts charting library (#1740) 2023-10-11 08:02:01 +01:00
youngcw
21effa654d Goal speedup2 (#1720)
* only use needed priorities

* cleanup

* cleanup

* cleanup

* maybe better?

* note

* note fix

* suggestion

Co-authored-by: Joel Jeremy Marquez <joeljeremy.marquez@gmail.com>

---------

Co-authored-by: Joel Jeremy Marquez <joeljeremy.marquez@gmail.com>
2023-10-10 14:39:37 -07:00
Joel Jeremy Marquez
c33dc6fbad Mobile category and group functionalities (#1737)
* More mobile functionalities

* Cleanup

* Remove close button on mobile budget summary modal

* Release notes

* Close mobile inputs on enter

* Fix mobile budget row header color

* Fix income group hidden

* Add validation + close button on category tooltip

* Add mobile budget visual cues

* More mobile visual cues

* Error message fix

* Update blank category name behavior

* Cleanup

* Cleanup

* Fix mobile group deletion and category tooltip behavior

* Zero sign for AmountInput

* VRT snapshot updates

* Handle null values in must-category-transfer
2023-10-10 13:02:33 -07:00
Michael Clark
057caf127a Maintenance: DateSelect component to tsx & server-handlers get-categories type (#1776) 2023-10-10 20:38:15 +01:00
Matiss Janis Aboltins
767bc8ecb6 Update FUNDING.yml - add github sponsors (#1772) 2023-10-10 20:19:53 +01:00
Compositr
bdf5c45cda Sync on ctrl+s (#1770) 2023-10-10 20:19:23 +01:00
凯 - kǎi
3dfe633428 fix: csv parser delimiter (#1774) 2023-10-09 20:49:54 +01:00
Michael Clark
efba86a72d Maintenance: BudgetSummary & CreateLocalAccount to tsx (#1768) 2023-10-09 18:46:29 +01:00
shall0pass
316da144e1 [Bug] End of month cleanup error (#1750)
* check for null carryover

* release note

* simplify logic
2023-10-06 17:12:13 -05:00
shall0pass
d3ab8f9812 [Bug] Goals: Fix schedules 'in between' calculation (#1753)
* fix in between calculation

* release note
2023-10-06 17:11:52 -05:00
Joel Jeremy Marquez
0ea7f1852b Prevent parent transaction from being added to transfer account (#1751)
* Prevent parent transaction from being added to transfer account

* Release notes
2023-10-06 15:06:55 -07:00
Josh Krebs
3c341fc583 Budget tsx refactor (#1743) 2023-10-06 21:51:29 +01:00
Matiss Janis Aboltins
de90504a6d 🐛 (electron) reconnect to sockets if connection lost (#1694) 2023-10-05 08:40:41 +01:00
Michael Clark
f6e2d3b1f3 Maintenance: CreateAccount and CreateEncryptionKey to tsx (#1755) 2023-10-04 22:33:05 +01:00
Joel Jeremy Marquez
f1973d55c0 Editable mobile budget (#1662)
* Editable mobile budget

* Fix error on empty amount

* Fix typo

* Decimal keyboard + select input on click

* Remove scrollIntoView

* Fix focus

* Focus input on negative/positive

* Use CellValue

* Functional mobile components

* Blur fix c/o dmlazaro

* Use pointer event

* Remove useFireChangeOnUnmount

* Release notes

* Prevent default on pointerdown

* Add dmlazaro to release notes

* Disable amount input button when not focused

* Remove input refocus
2023-10-04 14:19:12 -07:00
Neil
476070b0a7 Consolidate Notice Colors (#1724) 2023-10-04 20:47:14 +01:00
Matiss Janis Aboltins
510f635bbc 🔖 (23.10.0) (#1757)
* 🔖 (23.10.0)

* Remove used release notes

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-10-04 17:33:51 +01:00
Neil
d1e57340b8 Regression fixes (#1752)
* regression fixes

* notes

* VRT updates
2023-10-02 11:46:28 -05:00
Matiss Janis Aboltins
9ca36f3eeb ♻️ (typescript) hardening data entity types (#1680) 2023-09-27 20:56:53 +01:00
shall0pass
05f0df4917 [Bugfix]: Goals - Fixed an overbudgeting condition (#1738)
* fix

* release note
2023-09-26 09:50:47 -05:00
Michael Clark
5f347bbe40 Maintenance: Consolidate useMergedRef and convert to typescript (#1733) 2023-09-24 17:40:44 +01:00
Shaan Khosla
3c4f62bd51 keep name after schedule completion or recreation (#1728) 2023-09-24 17:37:49 +01:00
Shaan Khosla
abd2d424a6 Fix schedule searchbar (#1729) 2023-09-24 17:34:49 +01:00
Jainam Desai
89c5f15c1f Add link type to Button Component (#1725) 2023-09-23 10:38:50 +01:00
Syukron Rifa'il Muttaqi
2a597fb3b8 fix: filter empty note (#1708) 2023-09-22 20:11:21 +01:00
Shaan Khosla
2f97c3a1e2 changed default months to 5 in cash flow report (#1723) 2023-09-22 20:03:10 +01:00
Matiss Janis Aboltins
ba5174ddfa 🐛 (mobile) redirect to budget page for all 404 (#1721) 2023-09-22 08:37:16 +01:00
Matiss Janis Aboltins
9a900f9ff1 ♻️ moving rule server actions to separate file (#1677) 2023-09-22 08:36:56 +01:00
Joshua Krebs
2081e25cf5 refactor BudgetMonthCountContext to tsx (#1722)
* refactor BudgetMonthCountContext to tsx

* add release notes
2023-09-21 16:25:40 -07:00
youngcw
49ab358cf2 Goals: speed up and a bugfix (#1718)
* speed up and a bug fix

* cleanup

* note

* lint
2023-09-21 09:08:26 -05:00
Jainam Desai
efb72af1b8 Add common component for AnchorLink and ButtonLink (#1701) 2023-09-20 17:19:09 +01:00
Crazypkr1099
16334f67a5 Enhance Y-Axis Scaling on Net Worth Graph (#1709) 2023-09-19 18:40:33 +01:00
Matiss Janis Aboltins
ddb78af57d 💄 (mobile) updating apple home-screen icon (#1705) 2023-09-18 18:43:34 +01:00
Matiss Janis Aboltins
9e2226e384 🐛 (mobile) scrolling in lists with pull-to-refresh (#1706) 2023-09-18 18:43:13 +01:00
Matiss Janis Aboltins
a1dd6cf3dc 🔧 removing unnecessary manual module resolution (#1707) 2023-09-18 18:42:57 +01:00
Mohamed Muhsin
2ee023ac22 [refactor] Migrate Schedules Table to typescript (#1691) 2023-09-17 19:05:32 +01:00
Shaan Khosla
3496ac28f3 Category autocomplete should only search selectable categories (#1681) 2023-09-16 09:58:59 +01:00
Neil
f6f496f656 Dark Theme Final (#1513) 2023-09-16 09:56:19 +01:00
Neil
68147d654f Sidebar Account Fix (#1703) 2023-09-15 21:39:58 +01:00
Matiss Janis Aboltins
3d29cfbed5 👷 do not cancel github ci jobs on master branch (#1692) 2023-09-15 17:10:31 +01:00
Trevor Farlow
fbd1097a0c 🐛 Mobile account transaction list: Fix sticky date section headers (#1698) 2023-09-15 07:25:19 -06:00
Neil
0cc34798fb Dark Theme Reports/Settings (#1512) 2023-09-13 18:40:16 +01:00
Shaan Khosla
b6100cfecd queried cleared balance for tooltip (#1678) 2023-09-13 17:58:48 +01:00
Ikko Eltociear Ashimine
d835b113f8 Fix typo in GoCardlessLink.js (#1684)
happend -> happened
2023-09-12 17:25:18 +01:00
Crazypkr1099
4e4d20ad31 Update Visual Regression README File (#1689) 2023-09-12 17:15:04 +01:00
Matiss Janis Aboltins
8167ea8a83 🎨 fix multiline label in schedules modal (#1687) 2023-09-12 17:11:00 +01:00
Matiss Janis Aboltins
42e1b5ca7e 🔥 removing privacyMode feature flag (#1688) 2023-09-12 17:10:46 +01:00
Neil
55285f4c5f Dark Theme Manager and Modals (#1503) 2023-09-11 20:11:58 +01:00
Crazypkr1099
5565116c34 Add spent column to mobile view (#1651) 2023-09-11 18:06:19 +01:00
Matiss Janis Aboltins
addd8e0f5f (e2e) adding sample schedules to test budget (#1672) 2023-09-11 08:42:22 +01:00
Matiss Janis Aboltins
0e66ebfeef ⬆️ (electron) upgrading electron-based dependencies (#1674) 2023-09-10 16:15:27 +01:00
youngcw
05f2e2af1a Mobile: Show proper name of income group (#1679)
* Show right name for income group on mobile

* release note
2023-09-09 17:59:05 -04:00
Neil
8ff89a5ab4 DarkTheme Schedules/Payees (#1487) 2023-09-09 18:21:17 +01:00
Matiss Janis Aboltins
faa4a7c0e2 🔧 (eslint) convert rules from ERROR to WARN to improve devX (#1599) 2023-09-09 17:54:06 +01:00
Joshua Krebs
ba4885cb85 refactor budget/IncomeHeader to tsx (#1670) 2023-09-08 19:39:48 +01:00
Matiss Janis Aboltins
a8a0f777e2 🐛 (mobile) fix schedule status positioning (#1669)
* 🐛 (mobile) fix schedule status positioning

* Release notes
2023-09-08 07:56:00 +01:00
Matiss Janis Aboltins
852b5373bc 🐛 (signup) fix setting up for the first time with missing protocol (#1657) 2023-09-07 17:40:58 +01:00
Joel Jeremy Marquez
bca09023e2 Left over glamor style cleanup (#1668)
* #1666 - left over glamor style cleanup

* vrt

* Release notes
2023-09-06 23:04:47 -07:00
Matiss Janis Aboltins
4d8efccc73 🔧 (electron) improved OS detection (#1658)
* 🔧 (electron) improved OS detection

* Release notes
2023-09-07 06:56:15 +01:00
Matiss Janis Aboltins
0e539d91fe (mobile) pull down to trigger bank-sync (#1663)
*  (mobile) pull down to trigger bank-sync

* Release notes

* Remove canSync checks
2023-09-07 06:56:02 +01:00
Matiss Janis Aboltins
2ff1e67e8a 🐛 (mobile) fix sync button design (#1665)
* 🐛 (mobile) fix sync button design

* Release notes
2023-09-07 06:55:47 +01:00
youngcw
35c3d54688 Mobile: Dont show hidden groups (#1654)
* fix percent goals in the same priority level not compounding but overwritting

* release note

* don't show hidden groups on mobile view

* cleanup

* cleanup2

* cleanup3

* release note
2023-09-06 21:33:44 -07:00
Joel Jeremy Marquez
50f60b18e7 Enable swc sourcemap (#1659)
* Enable swc sourcemap

* Release notes
2023-09-06 18:48:37 -07:00
youngcw
bc5c2ce059 Mobile: don't show hidden income categories (#1656) 2023-09-06 19:42:38 +01:00
Shyam Guthikonda
21d5f117ee TypeScript migration (partial). (#1660) 2023-09-06 17:27:16 +01:00
Joel Jeremy Marquez
319d196e93 Webpack + SWC Loader (#1650)
* desktopc-client swc-loader

* More swc

* Jest swc + upgrades

* Revert @swc/jest usage for now

* SWC minify

* Remove setupFilesAfterEnv in package.json as per warning message in CI

* Release notes

* Minify on CI

* swc helpers in loot-core

* @swc/jest

* Upgrade webpack

* Add @swc/core to crdt

* Use yarn cache in github actions

* Cleanup

* Fix electron

* Revert "Fix electron"

This reverts commit 787af1980648fa30788a1d1678dcda534716f31d.

* Revert action.yml cache changes

---------

Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
2023-09-05 13:42:44 -07:00
Joel Jeremy Marquez
96863b3196 Change Accounts page BUDGET label to FOR BUDGET in mobile (#1639)
* Change BUDGET to FOR BUDGET in mobile accounts page

* Release notes

* Updated screenshot
2023-09-05 10:48:29 -07:00
Joel Jeremy Marquez
9fde36dca1 Fix ofx/qfx import options (#1649)
* Fix ofx/qfx import options

* Release notes
2023-09-05 09:54:04 -07:00
Syukron Rifa'il Muttaqi
108daedff5 Fix submit form on enter key pressed (#1634) 2023-09-05 08:34:34 +01:00
Joel Jeremy Marquez
bf786e8872 Break apart budget/misc.js (#1584)
* Break apart budget/misc.js

* Release notes

* Rename release notes

* UI bolder "No server" text
2023-09-05 00:27:52 -07:00
Joel Jeremy Marquez
3eb09b66ec Fix blur performance issue in Safari (#1646)
* Fix blur performance issue in Safari

* Release notes
2023-09-04 23:17:16 -07:00
Joel Jeremy Marquez
cb00826f51 Fix mobile page "back" behaviors (#1648)
* Fix mobile page "back" behaviors

* Release notes
2023-09-04 23:17:03 -07:00
Joel Jeremy Marquez
15ab80a475 Remove glamor css (#1637)
* Remove glamor css syntax

* Release notes

* Remove fallback value

Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>

* Remove fallback value

Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>

* Remove fallback value

Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>

---------

Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
2023-09-04 23:16:41 -07:00
Joel Jeremy Marquez
3e32db74cc Fix onNavigateToSchedule prop (#1645)
* Fix onNavigateToSchedule prop

* Release notes
2023-09-04 11:51:59 -07:00
Mohamed Muhsin
db07d7a73d refactor: make Schedules component to tsx (#1644) 2023-09-04 17:41:02 +01:00
Oleh
65899b0ef0 Add support for markdown in notes (#1587) 2023-09-04 08:28:07 +01:00
Joel Jeremy Marquez
63c3d07cb9 Replace usage of glamor CSSProperties with React CSSProperties (#1542)
* Remove usages of glamor CSSProperties

* Cleanup

* Re-add CellButton className prop

* More removal of glamor CSSProperties

* Release notes

* Fix vrt test failures

* Use React CSSProperties in View

* Custom CSSProperties type

* Settings UI regression fix

* Fix type check errors

* Address PR comments

* CategorySpendingGraph style

* Fix rebase mistake
2023-09-03 16:15:44 -07:00
Matiss Janis Aboltins
246e0d76c1 ⬆️ (better-sqlite3) upgrade to 8.6.0 to fix electron export crash (#1643) 2023-09-03 21:57:12 +01:00
Matiss Janis Aboltins
1a15d2c039 ♻️ use useCategories everywhere (#1597) 2023-09-03 21:56:58 +01:00
Matiss Janis Aboltins
90e32df3fb 🐛 (vrt) set static version (#1641) 2023-09-03 21:44:24 +01:00
Joel Jeremy Marquez
8ef2c4013a Experimental OFX parser (#1600)
* Experimental OFX parser

* Release notes

* Enable enableExperimentalOfxParser in tests

* Move experimental ofx parser to ofx2json

* Enable experimental ofx parser by default

* Address PR comments
2023-09-03 10:33:06 -07:00
Matiss Janis Aboltins
a460bc25d6 ♻️ convert budget component from class to hook component (#1566) 2023-09-03 17:43:10 +01:00
Joel Jeremy Marquez
59de6b0035 Replace format with useFormat (#1630)
* Replace format with useFormat

* [chore] Release notes
2023-09-03 09:34:24 -07:00
Joel Jeremy Marquez
3931625133 Rename CategorySelect file to CategoryAutocomplete (#1614)
* Rename CategorySelect file to CategoryAutocomplete

* Release notes
2023-09-03 09:32:53 -07:00
Oleh
6a0b7d6b7d Make reports more responsive (#1592) 2023-09-03 17:28:13 +01:00
Matiss Janis Aboltins
6817c45ddc ⬆️ upgrade absurd-sql and remove patch-package (#1632) 2023-09-03 17:26:01 +01:00
Matiss Janis Aboltins
ad4c383adf 🔖 (23.9.0) mobile transaction entry, privacy mode (#1635) 2023-09-03 15:54:28 +01:00
Matiss Janis Aboltins
8864e79db1 Add github sponsors button to the repository (#1636) 2023-09-02 21:56:36 +01:00
Matiss Janis Aboltins
dd7a7fa796 🐛 (imports) ability to toggle on/off OFX import fallback payee (#1631) 2023-09-02 18:03:38 +01:00
Trevor Farlow
d4422f89aa 🐛 Mobile: Account ledger jumps on first selection (#1625) 2023-09-01 10:58:26 -06:00
Trevor Farlow
cdff98b109 🐛 Mobile: fix Accounts page theme color (#1604)
* Make all mobile view themes respect Actual color themes
2023-09-01 10:58:01 -06:00
Matiss Janis Aboltins
835e16e54d releasing privacy mode feature (#1623) 2023-09-01 08:18:45 +01:00
Matiss Janis Aboltins
d46afab6dd 🐛 fix filtering in transaction table (#1622) 2023-09-01 08:04:44 +01:00
Joel Jeremy Marquez
05e582793d Close modals on route change (#1613)
* Close modals on route change

* Release notes

* Fix import error
2023-08-31 07:55:43 -07:00
shall0pass
fc62d85c23 [Bug] Mobile: Account picker text color (#1607)
* commit

* release note
2023-08-30 13:13:26 -05:00
Matiss Janis Aboltins
940af6d367 (vrt) increased strictness, added datepicker test (#1605) 2023-08-30 18:13:29 +01:00
Joshua Krebs
c19717e84c Refactor budget/MobileTable to tsx (#1602) 2023-08-29 21:28:56 +01:00
Matiss Janis Aboltins
080951fb34 🔧 mark PRs as stale and auto-close (#1591) 2023-08-29 21:07:29 +01:00
Joel Jeremy Marquez
245c59e942 Open transaction date picker when clicked while it's focused (#1583)
* Open transaction date picker when clicked while it's focused

* Release notes
2023-08-29 07:13:34 -07:00
youngcw
99dc87d715 Goals: fix percent goals in the same priority level not compounding (#1579)
* fix percent goals in the same priority level not compounding but overwritting

* release note
2023-08-28 12:26:19 -05:00
Joel Jeremy Marquez
e48924d987 Schedule privacy (#1580)
* Schedule privacy

* Release notes

* Fix schedule amount shifting
2023-08-28 08:15:00 -07:00
Joel Jeremy Marquez
2b06a42ac5 Create transfer using child transaction instead of parent (#1581)
Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
2023-08-28 07:06:36 -07:00
Matiss Janis Aboltins
d8c99221ff 🐛 (vrt) fix vrt - use static date (#1590) 2023-08-28 06:52:11 +01:00
adamkelly86
7c48e53329 Update screenshot (#1477) 2023-08-25 20:41:20 +01:00
Matiss Janis Aboltins
a0290609f9 👷 visual regression tests (#1553) 2023-08-25 17:34:10 +01:00
Trevor Farlow
821fa724e8 🐛 Schedule editor: Linked transactions table collapses (#1571) 2023-08-23 18:03:59 -06:00
Johannes Löthberg
5adab1885b Show all payees by default for child transactions (#1573)
Previously we would default to only show transfer payees in the payee
selection dropdown for child transactions.  This is confusing and there
doesn't seem to be any obvious reason for this, so this commit removes
that behavior.

Signed-off-by: Johannes Löthberg <johannes@kyriasis.com>
2023-08-23 08:50:01 -07:00
Khanh Nguyen
240dc46a23 Fix bug - Manually entered split transactions are not cleared on import #200 (#1465)
* Added clear transactions on import option

* Added release note

* Added cleared column to csv export

* fixed Manually entered split transactions are not cleared on import

* Revert "Added cleared column to csv export"

This reverts commit 2952bc3e7d.

* added release note

* Copied same code to Gocardless

* Updated var name

* Updated to only query  changed transactions instead of all
2023-08-23 08:26:33 -05:00
Jarek Samic
3d621c68cb Sync on visibility change (#1549) 2023-08-23 07:53:26 +01:00
Matiss Janis Aboltins
dd47b6c6ad 🐛 reset reconciliation status when switching accounts (#1547)
Closes #1327
2023-08-22 20:34:51 +01:00
Johannes Löthberg
37cec4c46f Don't update transaction date when syncing from GoCardless. (#1559)
We should not override the date in case the user has manually corrected
it.

Signed-off-by: Johannes Löthberg <johannes@kyriasis.com>
2023-08-22 19:12:13 +01:00
Joshua Krebs
108c0a6176 Month count selector ts refactor (#1565) 2023-08-22 19:10:52 +01:00
Trevor Farlow
ec24d0eaae 🐛 Link Schedules modal list of schedules grows too long (#1563) 2023-08-22 09:52:53 -06:00
Johannes Löthberg
6912c082b1 Fetch GoCardless transactions from the last 90 days or since first transaction (#1484)
Most banks allow up to 90 days of transactions, and so we try to fetch
up to 90 days of transactions or transactions since the first
transaction in the Actual account, whichever is shortest.  This lets
users get a clean start based on their selected starting balances date.

Signed-off-by: Johannes Löthberg <johannes@kyriasis.com>
2023-08-21 20:53:35 +01:00
Matiss Janis Aboltins
8a6c54c4d5 🎨 improving category spending side-nav style (#1548) 2023-08-21 18:53:32 +01:00
Matiss Janis Aboltins
85f21550cb 🐛 (mobile) hide sync button if sync is not active (#1546) 2023-08-21 07:18:38 +01:00
Matiss Janis Aboltins
fe8ed4e346 🐛 (darkmode) fix more issues - transaction table and csv imports (#1541) 2023-08-21 07:18:25 +01:00
Trevor Farlow
2d0464c097 🐛 Mobile entry will enter positive value instead of negative (#1551)
Ensure our FocusableAmountInput used in MobileTransaction conditionally
applies negation logic the same way onChange as it does onBlur.
2023-08-20 21:15:59 -06:00
Matiss Janis Aboltins
6dfc43abf1 (mobile) allow creating transactions via the footer (#1545) 2023-08-20 20:19:15 +01:00
Ziga Macele
c52900e713 [Bug]: Mobile Header (#1550) 2023-08-20 17:23:55 +01:00
Ziga Macele
4378489d80 [Bug]: darkmode regression - fatal error message is ugly (#1552) 2023-08-20 17:17:26 +01:00
Trevor Farlow
ca5977db75 🐛 Schedule table in Link Schedule modal is collapsed (#1501)
Co-authored-by: kyrias <johannes@kyriasis.com>
2023-08-19 21:45:47 -06:00
Pol Eyschen
639720b6fd Allow schedules to skip weekends (#1505)
* allow schedules to skip weekends

* wording

* release note

* skip weekend in upcoming dates as well

* Clean UI

* Move switch to the date selection modal
2023-08-19 12:55:47 -07:00
Joel Jeremy Marquez
79f4d02350 Port App and FatalError components to TS and functional components (#1535)
* Port app to functional component and tsx

* FatalError functional component + tsx

* Release notes

* Exports cleanup

* App cleanup

* Address PR comments
2023-08-19 12:53:34 -07:00
Matiss Janis Aboltins
f8afb396ed 🐛 (darkmode) fix colors used for schedules in transaction table (#1533) 2023-08-19 19:42:31 +01:00
Matiss Janis Aboltins
884ab8c783 (e2e) adding mobile e2e tests (#1521) 2023-08-19 19:42:04 +01:00
shall0pass
ac055dc2e0 Mobile: To Budget inconsistency (#1540)
* rearrangement

* release note
2023-08-18 15:39:22 -05:00
shall0pass
32cc86ec99 Mobile: Don't show hidden categories (#1539)
* don't show hidden categories on mobile

* release note
2023-08-18 09:25:29 -05:00
Joel Jeremy Marquez
6fae79560e Some typescript migration (#1532)
* Typescript migration

* Release notes

* Update error boundary

* Breakup sidebar components

* Account and Sidebar props

* Remove button in Item component + exports cleanup

* Put accountNameStyle to Account

* Revert component ports (separated to another PR)

* Export cleanup

* Remove ErrorBoundary (separated to another PR)

* Sidebar budgetName as ReactNode
2023-08-17 13:21:29 -07:00
Matiss Janis Aboltins
f8ce38f11e ♻️ extract rules components to individual files + TS (#1517) 2023-08-17 20:29:03 +01:00
Matiss Janis Aboltins
b114a8f8fc (export) adding error handling & improving dev-exp for electron (#1468) 2023-08-17 08:28:43 +01:00
Matiss Janis Aboltins
0228072c6f 🐛 (darkmode) fix transaction list hover effects (#1531) 2023-08-16 20:02:43 +01:00
Matiss Janis Aboltins
efec507bf8 🐛 (darkmode) fixing lightmode regressions in transaction table (#1530) 2023-08-16 17:45:21 +01:00
Matiss Janis Aboltins
1d65184241 🐛 (importer) fix nYNAB importer when decimal budgets used (#1529) 2023-08-16 17:45:06 +01:00
Johannes Löthberg
d3f9a9c3a0 Rely on date determined by server for GoCardless transactions (#1499)
Because different banks use the date fields in vastly different ways we
now let the server's bank integrations decide which date we should use.

Signed-off-by: Johannes Löthberg <johannes@kyriasis.com>
2023-08-15 08:33:10 +01:00
Joel Jeremy Marquez
91217b6d5e #1454 - Show projected balance when adding new transaction (#1455)
* 1454 - Show projected balance when adding new transaction

* Release note

* Rename 1455.md to 1454.md

* Update release notes category
2023-08-14 13:30:59 -07:00
Johannes Löthberg
af875ab035 Correctly fix schedule transaction payee (#1526) 2023-08-14 21:03:07 +01:00
Matiss Janis Aboltins
8ada28775e 🔧 (eslint) add 'plugin:react/recommended' (#1492)
* 🔧 (eslint) add 'plugin:react/recommended'

* Release notes
2023-08-13 15:23:14 +01:00
Martin French
6ebcbc8738 🐛 (reports) fix incorrect cashflow balance (#1518)
* 🐛 (reports) fix incorrect cashflow balance

Fixing a small typo here where multiple transfers across different payees on the same day fail to be summed up resulting in significant balance errors.

* release notes

* Update 1518.md
2023-08-12 22:21:17 -07:00
biohzrddd
42af73cdff Bugfix row render (#1402)
* Fix re-rendering all rows on hover

* release note

* Removing isHover logic in place of css :hover

---------

Co-authored-by: biohzrddd <10577752+biohzrddd@users.noreply.github.com>
2023-08-12 22:16:02 -07:00
Johannes Löthberg
71f885b899 Import category notes from YNAB4 exports (#1515)
Signed-off-by: Johannes Löthberg <johannes@kyriasis.com>
2023-08-12 13:32:20 +01:00
Matiss Janis Aboltins
b325bd9b18 ♻️ (typescript) migrating hooks to TypeScript (#1479) 2023-08-12 10:02:02 +01:00
Matiss Janis Aboltins
a0ecd65e70 (reports) add loading indicators (#1491)
*  (reports) add loading indicators

* Release notes
2023-08-12 09:10:45 +01:00
Matiss Janis Aboltins
2ef0fc9415 🐛 show all available transaction icons (#1508) 2023-08-11 21:13:02 +01:00
Johannes Löthberg
ba6eb26e6e Show correct payee for scheduled transactions on budgeted accounts page (#1379)
Signed-off-by: Johannes Löthberg <johannes@kyriasis.com>
2023-08-10 19:52:12 +01:00
Neil
b208294185 DarkTheme Accounts files (#1480) 2023-08-09 19:16:46 +01:00
Johannes Löthberg
448c4546f2 Stop setting endDate when fetching GoCardless transactions (#1493)
This breaks fetching transactions where the valueDate is in the future.
While the GoCardless documentation says this should never happen, it's
what happens with at least Bank Norwegian due to them using the interest
date as valueDate.  (C.f. #1392)

Signed-off-by: Johannes Löthberg <johannes@kyriasis.com>
2023-08-09 08:57:15 +01:00
Jarek Samic
b2738db441 Fix transactions button background color (#1494) 2023-08-08 20:55:11 -06:00
Jed Fox
c667118f10 Remove page-based modals in favor of existing state-based modal logic (#1270)
Co-authored-by: Trevor Farlow <trevdor@users.noreply.github.com>
2023-08-08 20:35:22 -06:00
shall0pass
218a4a761a Goals: Ignore hidden categories when running goal templates (#1481)
* ignore hidden categories

* release note
2023-08-08 05:14:51 -05:00
Neil
e1dc58d456 add dev theme to list (#1469) 2023-08-08 07:39:52 +01:00
Jarek Samic
e9137fccc7 Initial port of react native edit transaction view (#1340)
* Set `role="button"` on downshift autocomplete items
This avoids content observation behavior in WebKit on touch devices that delays the onClick event (and therefore reaction to user input).
* Disable split transaction editing for now
2023-08-07 21:40:01 -06:00
Jarek Samic
facc3acf31 Fix mobile account view (#1486) 2023-08-07 21:37:57 -06:00
Neil
c2d5d475b9 eslint rule for capturing colors/themes (#1482)
This is to prevent any further color changes added to PRs. This will
help prevent the darkTheme getting any addition work loaded onto it.
2023-08-07 21:13:02 +01:00
Johannes Löthberg
e17d90ce5f Add category spending report (#1382) 2023-08-07 19:04:56 +01:00
Neil
9fed15f88b Dark mode filters/rules/transactions (#1436) 2023-08-07 19:04:33 +01:00
Matiss Janis Aboltins
4a9b30d4d5 🔖 (23.8.1) fix nYNAB import, mobile colors and other smaller fixes (#1475)
- web: https://github.com/actualbudget/actual/pull/1475
- server: https://github.com/actualbudget/actual-server/pull/240
- docs: https://github.com/actualbudget/docs/pull/260
2023-08-07 18:53:22 +01:00
shall0pass
40d94141c4 [Bug] Goals: Monthly schedule can cause error (#1478)
The 'interval' value from the schedules is not set if the schedule is
monthly. This change will check for a valid number for the interval
value and if it does not exist will default to 1 (monthly).
2023-08-07 05:26:52 -05:00
shall0pass
023badc39c [Bug]Goals: Applying templates would zero non-templated categories (#1464)
Using the Apply Template or Overwrite option would result in
non-templated categories being overwritten. First reported :
https://discord.com/channels/937901803608096828/1137329898999001140/1137329898999001140
2023-08-06 13:41:32 -05:00
Matiss Janis Aboltins
e78430db62 🐛 (schedules) fix showing balance of approximate transactions (#1473)
Closes #1328

Closes #1471
2023-08-06 19:30:16 +01:00
Matiss Janis Aboltins
8ee4768f58 ♻️ (crdt) adding more strict typings (#1461)
Making the `crdt` package fully TypeScript-strict.
2023-08-05 21:02:14 +01:00
shall0pass
c581a8016c Goals: Schedule multi month forecasting (#1452)
References discord discussion starting here:
https://discord.com/channels/937901803608096828/940290142579605514/1133523705063030824

Currently the schedule keyword won't fill any future budget cells if the
category balance already satisfies the schedule. This PR is an attempt
to improve the behavior by allowing budget fills regardless of the
category balance.

This is a drastic rewrite of the schedule keyword. Though I've tried not
to have any regressions, it is possible because of how different the
logic is. I've tested compounding using a simple template, so a small
change in the 'by' keyword was also made.
2023-08-05 14:26:27 -05:00
Neil
293692d5c5 Fix titlebar transparent background (#1460)
Not much to this. Turns title bar from transparent to coincide with new
theme coloring.
2023-08-05 20:17:15 +01:00
Matiss Janis Aboltins
7b7e6e4db0 🐛 (import) fix YNAB (and other) importers (#1462) 2023-08-05 13:42:43 +01:00
Sean Tsai
cd54a2093e Fix the mobile footer color (#1456)
fix #1445
2023-08-05 12:40:48 +01:00
Matiss Janis Aboltins
92099dc763 🐛 (schedules) fix creating schedules with the same name (#1463)
Closes #1375
2023-08-05 12:40:17 +01:00
Jed Fox
9d27379b25 Move big input component into Input.js, port some of the manager app to TS (#1431) 2023-08-04 19:03:16 +01:00
Sean Tsai
b84546826f Bugfix: remove select component inner scrollbar (#1458)
In some situations the text in the Select component will be too big,
which will make the inner vertical scrollbar appear. This PR is to hide
the vertical scrollbar.

![截圖 2023-08-04 下午3 06
38](https://github.com/actualbudget/actual/assets/67775387/be6c066d-de44-4944-b02e-5d265c84bc61)
2023-08-04 07:49:20 -06:00
Matiss Janis Aboltins
421aa65e6d ⬆️ (crdt) upgrade murmurhash (#1438)
Upgrading murmurhash. The new version has TS types.

Apart from that, it looks pretty much the same.

Diff: https://www.diffchecker.com/CEpBedX1
2023-08-04 08:06:44 +01:00
Matiss Janis Aboltins
fac3af6360 ♻️ (crdt) adding more strict typings (#1437)
This PR has no functional changes. Only types are changing and getting
improved (more stricter).

A follow-up PR will come next that does a few functional changes to make
this package fully strict.
2023-08-04 08:06:32 +01:00
Khanh Nguyen
a9d34dfcc8 Add options on Import transactions screen to mark transactions as cleared/uncleared (#1451) 2023-08-04 08:05:27 +01:00
Joel Jeremy Marquez
9bfbf229db Reports privacy filter (#1447)
<!-- Thank you for submitting a pull request! Make sure to follow the
instructions to write release notes for your PR — it should only take a
minute or two:
https://github.com/actualbudget/docs#writing-good-release-notes -->

Addresses
https://github.com/actualbudget/actual/issues/1360#issuecomment-1646600087
2023-08-03 14:14:23 -07:00
Joel Jeremy Marquez
2d11c0f61e Sentence case in menus (#1446)
<!-- Thank you for submitting a pull request! Make sure to follow the
instructions to write release notes for your PR — it should only take a
minute or two:
https://github.com/actualbudget/docs#writing-good-release-notes -->

Updating other menus to sentence case so that it is consistent with the
other menus throughout the app
2023-08-03 14:14:08 -07:00
shall0pass
fe033d68cf Goals: Allow up to to consider spent values (#1448)
Resolves https://github.com/actualbudget/actual/issues/1443
2023-08-03 10:13:14 -05:00
shall0pass
64ad07b9db Release note README link update (#1449)
The current link in the README directs to a github page that has a link
to the documentation page on how to write release notes. I've updated
the link here to just go straight to the documenation page on how to
write good release notes.
2023-08-03 05:53:14 -05:00
Neil
e7f8288244 fix eye coloring (#1450)
Eye.js is currently set to a static color (black). This PR allows you to
change the color of the icon with the style element - mimicing all other
icons in Actual.
2023-08-03 08:13:08 +01:00
Matiss Janis Aboltins
5f92920195 🔖 (23.8.0) stability improvements and new experimental features (#1444)
- web: https://github.com/actualbudget/actual/pull/1444
- server: https://github.com/actualbudget/actual-server/pull/238
- docs: https://github.com/actualbudget/docs/pull/257
2023-08-02 20:33:20 +01:00
Jed Fox
907571bd83 Update to the latest version of the size compare action (#1435)
Includes a few typographical improvements I PR’d.

(since the workflow is a `pull_request_target` one, the changes won’t
show up in the comments till this PR is merged)
2023-07-31 18:02:03 +01:00
Matiss Janis Aboltins
5bc37379fc 🔥 remove unused budgetMonth (#1432)
<!-- Thank you for submitting a pull request! Make sure to follow the
instructions to write release notes for your PR — it should only take a
minute or two:
https://github.com/actualbudget/docs#writing-good-release-notes -->
2023-07-31 17:59:20 +01:00
Matiss Janis Aboltins
0d943516a3 🐛 fix incorrect state slice usage (#1433)
<!-- Thank you for submitting a pull request! Make sure to follow the
instructions to write release notes for your PR — it should only take a
minute or two:
https://github.com/actualbudget/docs#writing-good-release-notes -->
2023-07-31 17:59:12 +01:00
Matiss Janis Aboltins
5f76067190 🐛 (crdt) export Clock as a type to fix warning (#1434) 2023-07-31 17:58:57 +01:00
Jed Fox
81446fb4ef Remove no-op applyFilter call (#1430)
`applyFilter` exits early if the filter is unchanged, so this call
doesn’t do anything.
2023-07-31 08:08:52 +01:00
Johannes Löthberg
edf2e32c98 Include the schedule name when filtering schedules (#1429) 2023-07-30 16:31:27 -07:00
Neil
46af4556a9 Search Bar Changes (#1408)
Co-authored-by: Jed Fox <git@jedfox.com>
2023-07-30 14:38:29 -07:00
Matiss Janis Aboltins
d349354c9d 🐛 fix number formatter not listening to state changes (#1423)
Closes #1415

Fix number formatter not listening to state changes. This is not a fully
comprehensive solution. We will need to run a migration campaign to port
over from direct usage of `format` util to `useFormat`, but this is a
first step that solves the most glaring issue.
2023-07-30 20:57:05 +01:00
Matiss Janis Aboltins
a3c59f1ec3 🐛 fix side-nav collapse/pin icon color (#1421)
Closes #1416

---------

Co-authored-by: Jed Fox <git@jedfox.com>
2023-07-30 18:03:10 +01:00
Matiss Janis Aboltins
73289148df ♻️ (common) migrating to specific common component import paths pt4 (#1422)
Part 4 of the migration. Final moves.

Previous PR: https://github.com/actualbudget/actual/pull/1420
2023-07-30 16:28:33 +01:00
Matiss Janis Aboltins
abd7cf090a ♻️ (common) migrating to specific common component import paths pt3 (#1420)
Part 3 of the migration.

Part 2 here: https://github.com/actualbudget/actual/pull/1419
2023-07-30 16:13:17 +01:00
Matiss Janis Aboltins
30d035f8c6 ♻️ (common) migrating to specific common component import paths pt2 (#1419)
Part 2 of the path migration. No functional changes.

Part 1: https://github.com/actualbudget/actual/pull/1418

---------

Co-authored-by: Jed Fox <git@jedfox.com>
2023-07-30 15:37:03 +01:00
Jed Fox
e8b3419933 Port the settings components to TS (#1405) 2023-07-30 07:24:55 -07:00
Jed Fox
fd5ace58b4 Move YNAB4/5 import code into loot-core (#1208) 2023-07-30 07:21:57 -07:00
Matiss Janis Aboltins
60e5f1ae85 ♻️ (common) migrating to specific common component import paths (#1418)
Just moving away from the barrel `common` export to a bit more specific
imports.

Part 1 of the migration (other imports to follow in other PRs).
2023-07-30 13:22:13 +01:00
Matiss Janis Aboltins
61d707482a 🐛 (electron) always show titlebar (#1417) 2023-07-29 21:19:10 +01:00
Tom French
26d0bda8b2 chore: add more concrete types to loot-core (#1186) 2023-07-29 13:07:49 -07:00
Jed Fox
a99e88b46c Update node-fetch in the API to fix connection drop issue (#1397) 2023-07-29 05:24:51 -07:00
Matiss Janis Aboltins
9fd4e6c8f7 ♻️ moving P, useStableCallback, AnchorLink common components (#1413)
Moving the code to separate files. Functionally should be no
differences.
2023-07-29 13:09:01 +01:00
Sid Vishnoi
410dbbc8b1 Add Indian numbering format (#1412) 2023-07-29 04:54:58 -07:00
youngcw
9273a0abcf Goals: add flag to percent goals to use previous month income instead of this months (#1403)
This is a more elegant way of implementing a month ahead version of the
percent goals. To use it add the `previous` flag to the percent goal, ex
`#template 10% of previous Paycheck`.
2023-07-28 19:11:19 -05:00
youngcw
f68cb4ae13 Goals: fix remaining funds calculation (#1410)
Fixes #1409. This makes the template processing not include previously
budgeted funds in the return value.
2023-07-28 19:10:44 -05:00
Jed Fox
e7d8fdf590 Fix typo in handle-feature-requests.js (#1411) 2023-07-28 17:00:27 -07:00
Jed Fox
9ef5fd12e0 More typing improvements for Redux-related code (#1404) 2023-07-28 13:49:34 -07:00
Neil
2c69af2149 Fix Sorting/Balance interactions (#1406) 2023-07-28 09:48:31 -07:00
youngcw
5dd59c0053 Goals: fix leftover 1 cent in remainder goal (#1400) 2023-07-27 18:35:00 -07:00
shall0pass
ebc943bd70 Add per-category button to fill budget cells, including Goal template support (#1350) 2023-07-27 14:01:50 -07:00
Jed Fox
ee3d995117 Refactor buttons a bit, enable dark mode (#1395) 2023-07-26 15:53:39 -04:00
Jed Fox
9c527e3fce Improve messaging when the API fails to download a file (#1396) 2023-07-24 15:48:30 -04:00
Jed Fox
b8c90aa8d5 Adapt the top level components to dark mode (#1391)
Co-authored-by: biohzrddd <10577752+biohzrddd@users.noreply.github.com>
2023-07-24 13:02:42 -04:00
Jed Fox
f6c5769d47 Exclude “isbetween” op from the filter UI (#1389) 2023-07-23 16:18:26 -04:00
Jed Fox
c06b1badc4 Consistency improvements for <AnimatedLoading> (#1390) 2023-07-23 16:15:53 -04:00
Sean Tsai
94a6b53f4a Delete table navigator from rules page (#1342) 2023-07-23 17:36:42 +01:00
Sean Tsai
1d60635e3b Remove double scrollbar as the screen is big enough (#1385) 2023-07-23 17:12:22 +01:00
Jed Fox
a84f009ad9 Correct the width of StatusCell (#1380) 2023-07-22 06:31:51 -04:00
Jed Fox
c6e480e89c Move saveScrollWidth call to layout effect (#1366) 2023-07-21 13:14:59 -04:00
Jed Fox
af53f06eac Add support for importing the first row of a CSV file without a header row (#1373) 2023-07-21 13:14:30 -04:00
Jed Fox
93a6df362c Update coloring of newly added icons (#1378) 2023-07-21 13:04:00 -04:00
Jack
9a80a006ce Storing start month in user prefs (#1237) 2023-07-20 15:32:13 -04:00
Neil
a3e9971379 Add "not" checkbox for string filters (#1287) 2023-07-20 12:36:37 -04:00
Jed Fox
ac0d17e57e Begin integrating support for themes (#1367) 2023-07-20 12:34:17 -04:00
Jed Fox
b9f0b0d1d7 Update the text of the comment posted when closing feature requests (#1374)
The change isn’t so new anymore so I’ve updated the comment to recognize
that.
2023-07-20 17:12:33 +01:00
Joel Jeremy Marquez
7340b48b70 Include scheduled transactions for the month in Account's running balance (#1354) 2023-07-20 08:21:12 -04:00
Jed Fox
6146d659cc Add support for parsing TSV files using the CSV logic (#1372) 2023-07-20 06:19:18 -04:00
Jed Fox
d757ffd641 Improve clarity of informational message in “merge unused payees” modal (#1371) 2023-07-20 06:19:05 -04:00
Jed Fox
5cd6b6b7c5 Update to the latest SVGR version & re-generate all icons (#1368)
Co-authored-by: biohzrddd <10577752+biohzrddd@users.noreply.github.com>
2023-07-19 18:57:50 -04:00
Joel Jeremy Marquez
ec42dad728 Fix onExpose is not a function error (#1362) 2023-07-19 17:54:27 -04:00
Jed Fox
a97b36ebdd Further reduce bundle size by code splitting out pages that are only used on desktop/mobile (#1240) 2023-07-19 17:47:50 -04:00
shall0pass
717de22f58 Goals: Implement goals in Report Budget (#1329) 2023-07-19 09:12:41 -04:00
Michael Gillett
d91d062c96 spelling error in FixSplit.js (#1363) 2023-07-19 08:42:14 +01:00
Matiss Janis Aboltins
63d48032c5 ♻️ (Nordigen) rename to GoCardless (#1361)
Renaming all occurrences of Nordigen to GoCardless
2023-07-18 22:27:45 +01:00
Jed Fox
f329fe21af Add clear typings to the modals (#1359) 2023-07-18 15:55:14 -04:00
Joel Jeremy Marquez
ba2de7ece5 Privacy mode (#1272) 2023-07-18 20:40:14 +01:00
Neil
77ef86413d Sort Transactions on Accounts Page (#1232) 2023-07-18 20:21:44 +01:00
Jed Fox
2860837741 Add typings to most of the Redux logic (#1269) 2023-07-18 15:02:40 -04:00
Sean Tsai
e6acf52638 Fix clicking enter will create empty transaction (#1316)
fix #1301

After the change, upper components will not detect the press enter event
before the datepicker is closed.
2023-07-18 19:23:10 +01:00
Shazib Hussain
4c9dbcb96d Remove author from electron package.json (#1281)
I _think_ this no longer applies?
2023-07-18 19:20:48 +01:00
Jed Fox
617892eb93 Remove bottom border radius from the payee table (#1334) 2023-07-18 09:15:10 -04:00
Jed Fox
986dc6c1c0 Rename <CustomSelect> to <Select> (#1348) 2023-07-17 18:24:51 -04:00
shall0pass
a7e7ff61ef Goals: Fix calculated fill when using multiple 'up to' statements in different priority levels (#1312) 2023-07-17 17:36:15 -04:00
Jed Fox
1031bbbce7 Add a few more ESLint rules (#1355) 2023-07-17 17:32:22 -04:00
Jed Fox
9d31120f1a Hide the “Show unused payees” button when not needed (#1335) 2023-07-17 17:30:05 -04:00
Jed Fox
4bceaf8a25 Stop the demo build bar from shrinking on mobile/small sizes (#1353) 2023-07-17 17:21:21 -04:00
Sean Tsai
185daf470d Sort saved filters by alphabetical order (#1298)
fix #1284
2023-07-17 21:45:53 +01:00
Sean Tsai
723cbcf99c Fix a bug in confirm category delete (#1351) 2023-07-16 10:55:48 -04:00
Jarek Samic
c2ebfc72ef Fix importing transactions in Safari (#1349) 2023-07-16 10:52:16 -04:00
Sean Tsai
92ddaa568a Remove legacy Select and NativeCategorySelect Component (#1343) 2023-07-15 19:53:36 -04:00
shall0pass
1845ec8469 Use setZero function within goal templates for speed improvement (#1344) 2023-07-15 18:14:12 -04:00
Jack
9bccacea47 Month picker rework (#1235)
Co-authored-by: Jed Fox <git@jedfox.com>
2023-07-11 16:19:24 -04:00
Sean Tsai
b06d87d2ee Fix the CashFlow report crash because of the new CustomSelect (#1325) 2023-07-11 15:44:14 -04:00
Neil
fc6eb4be33 Add transfers to CashFlow Report (#1311) 2023-07-11 15:19:50 -04:00
shall0pass
a3e3c78c5c Goals: Improve remainder calculation when combined with other keywords (#1288) 2023-07-11 15:17:05 -04:00
Sean Tsai
927c859456 Remove some usages of the Select component (#1277) 2023-07-09 15:19:21 -04:00
Matiss Janis Aboltins
954309d034 🔖 (23.7.2) more patches (not so critical, but still annoying) (#1322)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-07-09 16:41:11 +01:00
Matiss Janis Aboltins
91474f1f0c 🐛 fix sync: add back 'accounts.type' column (#1317)
Closes #1296

After this is merged I'll do an API release which will also solve
https://github.com/actualbudget/actual/issues/1303
2023-07-09 16:13:30 +01:00
Jed Fox
d6cb8674f7 Fix ID for transaction_filters migration (#1309)
Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
2023-07-08 13:27:08 -04:00
Sean Tsai
2a627ef988 Fix Custom Select unnecessary disabled scrollbar (#1314) 2023-07-08 17:32:02 +01:00
Jed Fox
c76f39c0fa Fix migration check script (#1307) 2023-07-07 14:56:32 -04:00
Matiss Janis Aboltins
cc497b29ab 🐛 changing pages while adding a transaction (#1305) 2023-07-07 19:17:45 +01:00
Matiss Janis Aboltins
8d112d2e93 Revert "Fix drag and drop on touch devices (#1194)" (#1306)
Reverts actualbudget/actual#1204

Closes https://github.com/actualbudget/actual/issues/1293

This is way more complicated than originally anticipated. Reverting the
change to fix the prod build. Someone can work on re-introducing this in
a later build, but we need to be sure it doesn't break once more.
2023-07-07 19:15:25 +01:00
Matiss Janis Aboltins
e9f6d6ba4d 🐛 fix zero budget values (#1308)
Closes #1299

<img width="324" alt="Screenshot 2023-07-07 at 18 56 38"
src="https://github.com/actualbudget/actual/assets/886567/1a4dbd65-52af-44d9-8205-94a4c6c745e5">
2023-07-07 19:14:50 +01:00
Sean Tsai
88452ea519 Fix page not found error in README (#1297)
The "community documentation" link below Code Structure headline in
`README` will direct user to page not found error
2023-07-07 18:27:47 +01:00
Matiss Janis Aboltins
ff7be0d637 🔊 improving 'invalid-schema' error logs (#1302)
The logs for `invalid-schema` were not logging the used query or the
metadata. So fixing this.

It would make debugging issues such as
https://github.com/actualbudget/actual/issues/1296 easier.
2023-07-07 18:06:25 +01:00
Matiss Janis Aboltins
02b1e03611 🔖 (23.7.1) critical sync fixes (#1295) 2023-07-05 21:38:05 +01:00
Matiss Janis Aboltins
6e0c84ccad 🐛 fix new budget files not syncing (#1291)
Fix a small regression introduced in
https://github.com/actualbudget/actual/pull/1076

Tested locally and this seems to have solved the problem
2023-07-05 21:13:46 +01:00
Matiss Janis Aboltins
bd125d2915 🐛 custom select width and scrollbars (#1294)
Closes #1286, closes #1283

Scrollbar fix by @aleetsaiya
2023-07-05 21:13:37 +01:00
Matiss Janis Aboltins
2cc40cbff9 🐛 fix nordigen sync bug (#1289) 2023-07-05 20:14:09 +01:00
Matiss Janis Aboltins
20825d66fe 🔖 (23.7.0) Nordigen release, stability improvements (#1280)
- web: https://github.com/actualbudget/actual/pull/1280
 - server: https://github.com/actualbudget/actual-server/pull/222
 - docs: https://github.com/actualbudget/docs/pull/223
2023-07-04 21:47:36 +01:00
Matiss Janis Aboltins
28db6fb32e 🔖 (crdt) v2.0.0 (#1279) 2023-07-04 21:47:19 +01:00
Shazib Hussain
583eb10b83 Fallback to memo in ofx if payee is not found (#1268) 2023-07-04 15:29:15 -04:00
youngcw
c1b99958f4 Goals: Add option to percent goal to use available funds (#1254) 2023-07-04 09:35:23 -04:00
Neil
050f48ac2a Reorganize Accounts Folder (#1258)
There's no new code here, just a reorg. Had to change some import calls
in some files to make it all work properly.

Having just done a lot of work in the accounts directory I figured it
could do with some organization. I've broken out all the Header
functions into a different file in order to cut down on the size of the
account.js file.

I also moved the transactions files into a new directory since they are
used by other pages. Also makes them easier to find with this structure.
2023-07-04 08:40:58 -04:00
Tom French
e4ec5b3eb1 chore: add types to crdt package (#1076)
This PR adds types to the `crdt` directory to make the structure of the
merkle tree (really a merkle radix trie) clearer.

---------

Co-authored-by: Jed Fox <git@jedfox.com>
Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
2023-07-04 08:38:52 -04:00
Jed Fox
953ff45085 Fix layout of the management app with the demo bar in place (#1267)
The banner in #1229 pushes the bottom bar items (including version &
“change server URL” button) out of view on Netlify builds.
2023-07-03 22:06:22 -04:00
Matiss Janis Aboltins
2ebea847c1 ♻️ (select) removing 2x usages of the Select component (#1259) 2023-07-03 18:52:04 +01:00
Jed Fox
4e89a95e35 Fix transaction list scrolling behavior (#1260) 2023-07-03 12:04:44 -04:00
Matiss Janis Aboltins
ede51872e2 🐛 fix link-schedule option in transaction table (#1250)
Closes #1236

---------

Co-authored-by: Jed Fox <git@jedfox.com>
2023-07-03 16:24:59 +01:00
Neil
d36569d258 cleared/uncleared background update (#1265)
Adding background update to cleared/uncleared boxes on accounts page.
This is needed due to Accounts page background change in previous PR
(#1122)
2023-07-03 16:23:19 +01:00
Jed Fox
70f00b6bb4 Fix calculation of how many table rows to render (#1262) 2023-07-03 08:05:33 -04:00
Matiss Janis Aboltins
f5617aca1c ♻️ moving more components out of common.tsx (#1257)
Moving some more components out of `common.tsx` into their own files.

There are no functional changes. This is a direct copy&paste into new
files.
2023-07-02 17:41:02 +01:00
Matiss Janis Aboltins
bdaa78b919 ♻️ moving some components from common.tsx to separate files (#1248)
This is direct copy & paste with no changes. Just moving things a bit to
reduce the size of the massive common.tsx file
2023-07-02 15:51:54 +01:00
Matiss Janis Aboltins
32ecd52f2b 🐛 fix toggling of balances in all-accounts view (#1252)
Closes #1249

Also removing some unused code
2023-07-02 15:51:18 +01:00
Matiss Janis Aboltins
f1c21be4a0 🐛 (mobile) reduce the size of account cards (#1247)
Closes #1163
2023-07-02 15:51:10 +01:00
Shazib Hussain
7a5bf2ffc4 Fix electron export issue (#1242) 2023-07-02 08:32:55 -04:00
Neil
f5ea9d0fda Saved Filters Page (#1122) 2023-07-02 07:00:23 -04:00
Matiss Janis Aboltins
168b38fc4b 🔧 cancel previous CI runs if a new push is made (#1251)
Cancel previous CI runs if a new push is made. Thus saving a bit of CI
resources.
2023-07-01 21:26:02 +01:00
Shazib Hussain
de41301a44 .gitattributes Check line endings for tsx files (#1246) 2023-07-01 19:00:05 +01:00
Jed Fox
7c0c440df2 Fix importing transfers from YNAB4/5 (#1224) 2023-07-01 06:28:27 -04:00
Jed Fox
0d636aa04c Auto-close the local/nordigen picker modal after creating an account (#1219) 2023-07-01 06:28:10 -04:00
Jed Fox
f4940dceb1 Run “Handle completed feature requests” in pull_request_target (#1243) 2023-07-01 06:21:21 -04:00
Shazib Hussain
447f7d6459 Add electron options to bug-report.yml (#1239) 2023-06-30 19:20:31 -04:00
Joel Jeremy Marquez
d325e6b060 Add onClick handlers to the schedule and transaction icons in the transaction list (#1228)
For #547 

In addition to the arrow icon, I have also updated the
recurring/calendar icon to open the edit schedule page.
2023-06-30 17:32:48 -04:00
Jed Fox
0286fa4ed0 Remove usage of Formik (#1212) 2023-06-30 17:01:43 -04:00
shall0pass
df74e6ddd1 Goals: Add recurring schedule support (#1193)
Add recurring schedule support.

To test: use the schedule keyword with a daily or weekly recurring
schedule.
2023-06-30 17:00:51 -04:00
Jed Fox
3b3770d6b9 Restore undo-url-saving behavior (#1234) 2023-06-30 16:57:57 -04:00
Jed Fox
e6d931729c Extract the reports page into a separate JS file (#1210) 2023-06-30 12:24:28 -04:00
Shazib Hussain
7165a2159d Lint tweaks (#1223) 2023-06-30 10:20:18 -04:00
Matiss Janis Aboltins
9e03a5f7bd 🔧 improved dev-server - enable asset cache (#1230)
Bringing back asset caching. This means we won't need to do a full
re-build for every change.

I had previously disabled caching because the backend changes (kcab)
were not picked up properly. But now that should be fixed too.
2023-06-30 14:37:42 +01:00
Matiss Janis Aboltins
1bc988d9d1 🔧 fix the build - ignore missing moment (#1231) 2023-06-30 07:52:16 -04:00
Matiss Janis Aboltins
dafbfb4198 add netlify development banner (#1229) 2023-06-29 22:20:51 +01:00
Matiss Janis Aboltins
e07ff45ae6 🔥 remove pikaday monkeypatch (#1203)
AFAIK with the build-script upgrades we have done - this monkeypatch is
no longer necessary. The build size still seems to be the same.
2023-06-29 19:50:10 +01:00
Matiss Janis Aboltins
f41763b0b9 🔥 remove unused scripts; simplify version script (#1227)
1. Removed some more unused scripts
2. Simplified the way we build `version` in to the source
2023-06-29 19:25:54 +01:00
Shazib Hussain
59a1c38d34 Move netlify-wait-for-build into github/actions folder (#1222)
This script is only used for the github e2e tests so doesn't need to be
inside /bin
2023-06-28 17:10:10 -04:00
Shazib Hussain
09d624c24b Tidy up electron (#1221)
Bumping package version to match actual/web, and deleting two scripts
i'm pretty sure are legacy/unused.
2023-06-28 17:09:28 -04:00
Joel Jeremy Marquez
81afe28901 Fix drag and drop on touch devices (#1204)
For #1194
2023-06-28 15:53:34 -04:00
Jed Fox
c88038e95e Clean up the public/ folder (#1187)
- Remove unused CSS files
- Set up the `.wasm` files to be automatically copied in
- `sql-wasm-debug.wasm` is removed entirely since we never use the
debug/dev build of sql.js. This reduces the app download size by 1MB on
the server side. It may be worth enabling the debug build automatically
in the future?
2023-06-28 15:41:26 -04:00
Matiss Janis Aboltins
962ebc9ef0 Update .eslintignore (#1218)
The real migration files exist in `loot-core`. These are just copies.
2023-06-28 17:27:32 +01:00
Jed Fox
1733179bfb Remove redundant UUID wrapper module, update uuid package to 9.x (#1216)
I noticed that:

- The web and electron versions of our `uuid` module both looked exactly
the same…
- …and deferred to the `uuid` package…
- …and the async version just called the sync API.

So now we will just use the `uuid` package directly everywhere.
2023-06-28 12:24:24 -04:00
Jed Fox
2108712f2d Fix size comparison workflow on fork PRs (#1214)
This PR moves the size comparison action back to a separate workflow
which now uses the `pull_request_target` event. This event is triggered
at all the same times as the `pull_request` action, except that the
workflow file content comes from the target branch of the PR, and it is
run in the context of the repo owning the target branch. Practically,
this means that it will still have access to post a comment even if the
PR comes from a fork.

We don’t want the build actions to be run in a `pull_request_target`
workflow because they would get access to the secrets and be able to
perform arbitrary actions on the repository, even from fork PRs.

See the current version failing here:
https://github.com/actualbudget/actual/actions/runs/5395184895/jobs/9797388016?pr=1122
2023-06-28 12:04:15 -04:00
Jed Fox
982d57c9ae Move the rest of the syncing protobuf code to the CRDT package (#1217) 2023-06-28 07:03:52 -04:00
Jed Fox
38b8000d2a Bundle only the variable font version of Inter (#1213) 2023-06-28 07:02:59 -04:00
Jed Fox
d5387c5d46 Improve some of the typings in loot-core (#1180)
<!-- Thank you for submitting a pull request! Make sure to follow the
instructions to write release notes for your PR — it should only take a
minute or two:
https://github.com/actualbudget/docs#writing-good-release-notes -->
2023-06-27 13:02:58 -04:00
Jed Fox
081c9d068f Display bundle size changes in comments on opened PRs (#1205) 2023-06-27 12:46:27 -04:00
Jed Fox
aa503d6a74 Consistently use ExternalLink (#1184)
This cleans up all of the usage of `<a>` in the codebase. Now all
external links get the appropriate attributes added.
2023-06-27 08:46:38 -04:00
Jed Fox
ed50e2b392 Run ESLint at the top level once (#1202)
This significantly speeds up `yarn lint` for me. It also ensures we’re
listing all source files in the project, including the `.eslintrc` files
and any other files that may be present.
2023-06-26 15:32:56 -04:00
Jed Fox
ea4a68e06c Add public/data to .eslintignore in desktop-client (#1199)
This fixes an error that you’ll see about an unused export if you build
before linting.
2023-06-26 15:19:38 -04:00
Jed Fox
45247e6d59 Remove unused deps from desktop-client (#1200)
This will reduce install/lockfile size but won’t affect the built bundle
size. Still nice, hopefully!
2023-06-26 15:15:47 -04:00
Jed Fox
6fd2ad21ec Add release note 2023-06-26 14:59:19 -04:00
Matiss Janis Aboltins
b7d5602cce ⬆️ upgrade hotkeys-js from v3.8.2 to v3.10.3, remove monkeypatch (#1195) 2023-06-26 17:19:11 +01:00
Jed Fox
53df1a03a3 Fix navigating to the per-category per-month page (#1182)
I checked through the other references to `navigate` and none of them
appeared to be affected.
2023-06-26 09:13:20 -04:00
Matiss Janis Aboltins
0f81d877ef ⬆️ upgrade react-dnd and remove monkeypatch (#1192)
Another monkeypatch bites the dust.

AFAIK there is no regression in functionality. But please test
yourselves too to double check.
2023-06-26 07:03:04 +01:00
Matiss Janis Aboltins
a3ca5a26ae ⬆️ upgrade @reach/listbox and remove monkeypatch (#1190)
It me it seems the monkey patch is no longer necessary.. At least I'm
not able to reproduce any `escape` key related issues. But LMK what you
think.

The `@reach/listbox` component is used for the custom select component
(for example: for filters).
2023-06-25 22:09:31 +01:00
Matiss Janis Aboltins
dacaef6fa4 🔥 remove beta code and some unused scripts (#1189)
Just removing some unused/unnecessary scripts and `IS_BETA` env variable
+ the associated code (we don't use this in the OS version of Actual).
2023-06-25 21:47:23 +01:00
shall0pass
78e7468715 Goals: Use shared 'months' functions for time (#1082)
Based on Tom's comment
(https://github.com/actualbudget/actual/pull/1058#issuecomment-1568030881)
I've eliminated all direct use of date-fns and only use the functions in
months.ts.

---------

Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
2023-06-25 16:45:49 -04:00
Jed Fox
d48add55b6 Fix completed feature request handling (#1183) 2023-06-25 12:55:27 -04:00
Jed Fox
bb3ed4cea4 Fix “delete file” modal layout (#1170) 2023-06-25 12:55:08 -04:00
Tom French
fddcdec81f chore: enforce proper types in sync/index (#1077)
I've updated the return values in `sync/index` to sensible values based
on how they're being called.

I've updated the type of `msg.value` to be `string | number | null` to
match the values expected by `serializeValue`
2023-06-25 17:33:57 +01:00
Jed Fox
0e7de456f6 Fix transaction list page being blank on mobile (#1171)
Also remove the need to unmount & remount when switching between
accounts. Fixes #1169
2023-06-24 17:39:55 -04:00
Joel Jeremy Marquez
64408495ee Automatic category selection for new category transactions (#1176)
For #1152
2023-06-24 17:01:06 -04:00
Shazib Hussain
228cff3cfd New linter rule import/no-unused-modules & fixing import on typescript (#1173)
- New linter rules are now added
- Perhaps the `createPayee` method i've tagged with a disable should be
deleted.
- I have ignored unused methods in Plaid/Mobile as this stuff is still
WIP/experimental?
2023-06-24 11:58:40 -04:00
Trevor Farlow
f3f2c8485a React Router 6 fixes (#1178)
* Provide `match` prop to class components that still rely on it.
* Fixes #1169
* Fixes an unrelated crash on Payees
2023-06-24 07:40:17 -06:00
Shazib Hussain
83459b4c78 Remove remaining tutorial code (#1174)
Looks like this got missed in the last PR!
2023-06-23 19:52:20 -04:00
Trevor Farlow
9d041aaa7a react-router 6 upgrade (#1066)
Co-authored-by: Jed Fox <git@jedfox.com>
2023-06-23 14:40:59 -04:00
Shazib Hussain
0c0f9e6ccf Deleting all unused files, deleting unused functions from loot-core (#1158)
Last one before I add the actual linter rules!
2023-06-23 14:38:57 -04:00
Jed Fox
d6ed860bc3 Log more details when migrations are out of sync (#1161) 2023-06-23 06:40:25 -04:00
Jed Fox
c6443f24b2 Remove upgrade notifications code (#1156)
None of this code is relevant to people who have used the open source
app. I initially wanted to leave it in so it could be used for major new
features in the future, but as it falls further and further out of date
I think it’s best to just delete it in a clean PR that can be referenced
later if desired.
2023-06-21 15:16:37 -04:00
Matiss Janis Aboltins
2b64a49359 🔥 removing needs-triage github label (#1157) 2023-06-21 20:08:42 +01:00
Shazib Hussain
287fb9b9d6 Tidy up exports in loot-core (#1147)
Following on from #1146 this PR removes all unused exports from
`loot-core/`
2023-06-20 20:57:19 +01:00
Matiss Janis Aboltins
c286f1c5f3 🐛 remove 'we have been notified' copy (#1155)
Closes #1069

I've not actually tested this change. Which is why I'm not changing it
to direct links (as they might not work).

Instead I'm just applying a very quick patch so the message would not be
misleading anymore.
2023-06-20 20:56:23 +01:00
Jed Fox
977e0c9008 Updates to the server button at the top right (#1141)
It now always shows a menu, so the user doesn’t unexpectedly perform an
action.
2023-06-20 14:57:23 -04:00
Joel Jeremy Marquez
497a3104f0 Expand / collapse all categories (#1143)
This PR is for the first item listed in #559: `Expand All / Collapse All
Categories`

For the expand / collapse all categories functionality, I was choosing
between having a single `Expand / collapse all categories` button or one
for each: `Expand all categories` and `Collapse all categories` buttons.

For the initial implementation, I have opted with the latter. Please let
me know which one is the right way to go or if there are other
suggestions and I'll just accordingly.


![image](https://github.com/actualbudget/actual/assets/20313680/64d0e498-1139-4dd0-9b7f-4d478ab947aa)
2023-06-20 14:35:01 -04:00
Matiss Janis Aboltins
adc5e324a7 (nordigen) release the feature (#1135) 2023-06-20 19:21:30 +01:00
Jed Fox
2c6cca6bf6 Improve error logging in the API (#1121) 2023-06-19 06:41:49 -04:00
Matiss Janis Aboltins
610c42a1ae ♻️ (crdt) moved re-used utils in actual-server to separate package (#1150)
actual-server does not need to import the full actual-app/api package.
It can import only the CRDT stuff.. so I'm extracting it into a new
package to reduce the size of actual-server and make the link between
things more transparent.
2023-06-18 20:16:50 +01:00
Shazib Hussain
fcb1bba7fa Removing Tutorial code (#1146) 2023-06-17 19:49:48 -04:00
Shazib Hussain
764a20a36b Removing unused functions (#1145)
Continuing on from #1139 this PR deletes all function that are not used
anywhere.

The next PR will include all the entire files that are unused & deleted.
2023-06-16 21:37:30 -04:00
Jed Fox
b8dbec46bb Revert “Make number parsing agnostic to decimal and thousands separators” (#1144)
This reverts #1029. As raised in #1097, the formatting chosen doesn’t
work well when doing math. There may be a way to balance compatibility
with multiple format styles with handling non-currency amounts
correctly, but it will require some more careful consideration. Re-opens
#894.
2023-06-16 15:30:27 -04:00
Jed Fox
a86fd9cf06 Strip a trailing slash off of server URLs (#1140)
URLs with the trailing slash don’t work well — requests end up being
made to `https://example.com//sync/sync` and such which can 404

---------

Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
2023-06-16 13:54:13 -04:00
Jed Fox
8f16e0167c Update CONTRIBUTING.md to point to the website (#1138) 2023-06-16 13:38:28 -04:00
Jed Fox
60a8f72be8 Updates to the account creation/linking flow (#1137) 2023-06-16 13:38:16 -04:00
Shazib Hussain
61a5b1a337 Remove 'export' keyword where not needed. (#1139)
As discussed in #1126 creating separate PRs.

The linter rule isn't actually part of this first PR and will likely be
in the final PR as otherwise it will just fail because all the issues
aren't resolved.

The code is 'ready' though so as soon as this is merged I will open next
one(s)
2023-06-15 18:11:45 -04:00
Matiss Janis Aboltins
f8dfa5a6e0 🐛 disable 'all payees' checkbox while schedules are loading (#1136)
Closes #1125

Disables (hides) the "all payees" checkbox when schedules are loading.

Reproduction:
1. open `/schedule/discover`
2. while the data is still loading - click on the checkbox in the table
header
3. after loading finishes - the page crashes with an error
2023-06-15 20:45:04 +01:00
Jed Fox
9aea091f53 Update links to point to new domain (#1129)
Depends on https://github.com/actualbudget/docs/pull/202.
2023-06-15 06:46:04 +01:00
Rich Howell
4bee4584dc Contributing link update (#1134)
Replaced contributing link

<!-- Thank you for submitting a pull request! Make sure to follow the
instructions to write release notes for your PR — it should only take a
minute or two:
https://github.com/actualbudget/docs#writing-good-release-notes -->
2023-06-14 20:08:25 +01:00
Matiss Janis Aboltins
05754d3e42 🐛 (nordigen) expired bank-links (#1133)
Further Nordigen fixes for expired bank-links. With the recent changes
to the router, we had broken this functionality.

<img width="488" alt="Screenshot 2023-06-14 at 09 00 21"
src="https://github.com/actualbudget/actual/assets/886567/8e7516e0-121a-46e0-9e52-24748b161173">
<img width="437" alt="Screenshot 2023-06-14 at 09 00 30"
src="https://github.com/actualbudget/actual/assets/886567/88b36549-1073-4269-b42f-1553c164316f">
2023-06-14 18:13:57 +01:00
Matiss Janis Aboltins
4f5fd6c463 🐛 (nordigen) linking an account via account page (#1128)
Fix linking via the account page. Previously it was not fully working if
the user had not set up the credentials before.



https://github.com/actualbudget/actual/assets/886567/588dc437-b53d-4616-8612-a2ef07445dc8

---------

Co-authored-by: Jed Fox <git@jedfox.com>
2023-06-14 07:38:46 +01:00
Matiss Janis Aboltins
06b2a8757e 🐛 (nordigen) perform status check only if server is online (#1127)
Small bugfix for nordigen.

Do not perform the server status check if the user is actually offline.
2023-06-13 20:47:59 +01:00
Jed Fox
0e2b317eb8 Fix sync-related errors that show up in the console when not using a server (#984)
Previously, the frontend would attempt to make real requests to
`https://not-configured/`, which of course failed. I’ve changed the
internal structure to have the lack of a server expressed as a `null`
server. A new `did-bootstrap` browser-level setting has been added to
track if the user clicked “Don’t use a server.”

Finally, I updated the auth logic for methods that call out to the
server to make them consistent and use the early return style which
reduces indentation.
2023-06-11 13:57:32 -04:00
Jed Fox
15bc3c45a0 Partition GitHub Actions cache based on Node version (#1118)
This should fix the test failures on `master` — currently tests are
failing because the cache was created with an old version of Node.
2023-06-11 13:37:20 -04:00
youngcw
ded6ee8a65 Goals: Check template action (#1108)
This adds an option to the month drop down to check all the template
lines. If there are errors the offending line is shown with its
category.

I also modified the wording on the regular template return to be more
accurate. Fixes #1100
2023-06-11 10:14:46 -04:00
Jed Fox
c1af40ff5c Update recommended version to Node.js 18 (#1117)
After #1115, new Node.js versions will be able to be used for
development of the frontend. This PR changes the recommended Node.js
version to 18 (the current LTS version). I have also tested with 16 and
20 and it works.

I also took the opportunity to:

- move the build script that was at the project root to the `bin/`
folder
- update the `browserslist` to target Electron 24 (which is the version
we currently build against). This results in a slightly smaller bundle
due to no longer having to transpile optional chaining.
2023-06-10 13:12:42 -04:00
Jed Fox
f06edd723d Update loot-core to webpack 5 (#1115)
~based on #1114~

This brings its build process up to date with the rest of the project.
We can now use Node 20 to build successfully.

Closes #918
2023-06-10 12:59:51 -04:00
Jed Fox
02f1fe48c6 Stop mixing platform-specific code (#1114)
Bundle sizes are unchanged but now we no longer pull in the
Electron-specific FS code on the web version
2023-06-09 16:06:18 -04:00
Davis Silverman
87d269ba5c Remove 'new' OFX parser as it is too buggy (#1111)
Draft because it is untested, maybe tonight I will test it! Just wanted
to get some code out cause i had a spare 5 minutes.

The new parser isn't immediately good enough to replace the old parser,
and I sadly lost the time to contribute more! Sorry! If someone else
wants to take maintenance burden of this code, we can not merge this.
Otherwise, it should go the way of the Dodo it seems.

Thanks!

Closes #1044
2023-06-08 14:52:34 -04:00
Jed Fox
6e6d765699 Fix migration ID for “remove account type” migration (#1109)
Good catch @Jackenmen in
https://github.com/actualbudget/actual/pull/948#issuecomment-1580501909
— I’ve also added a CI check to ensure bad migrations aren’t introduced
in the future.

I think if you have a budget that has managed to have this migration
applied successfully, you’ll need to manually patch
`getAppliedMigrations` in
`packages/loot-core/src/server/migrate/migrations.ts` and inject a query
to remove the old migration ID and insert the new one.
2023-06-08 14:52:13 -04:00
Jed Fox
a25327d370 Remove account types (#948)
Closes #944, closes #532. ~WIP because something is causing the test
budget to fail to create because it’s using INSERT instead of UPDATE sql
queries. (Or not? I have no idea)~
2023-06-06 16:41:46 -04:00
youngcw
ed285e9ac5 Goals: Remainder option (#1101)
Added the option to add a remainder goal template. This will use the
remaining available funds and dump them into the respective category.
There is optional weighting. The remainder templates will be forced to
the lowest priority as to run after all other templates.

Usage: `#template remainder <weight>` Add the template line to any
categories you want to catch any remaining funds such as savings. The
amount added to the category will equal
`remaining_budget/total_of_weights*weight`. The default weight is 1.
2023-06-06 16:41:09 -04:00
SudoCerb
c42d17897c BUGFIX Unused Payees: Corrected an issue where the filter control was not being referenced (#1107) 2023-06-05 08:34:48 +01:00
Shazib Hussain
be81091698 More Electron fixes (#1099)
- Fix socket connection issues when reloading
- Fix external url clicking & middle clicking internal links
- Remove broken menu option. Easier for now than refactoring the
settings panel it now lives in. We can add it back later if needed?
2023-06-04 21:21:15 +01:00
Matiss Janis Aboltins
3cba838412 🔧 (cross-env) add missing dep and upgrade all to same version (#1106)
1. Upgrade all `cross-env` versions to be the same
2. Added `cross-env` to `desktop-electron` - it was used there, but was
missing as a dep
2023-06-04 21:11:09 +01:00
Jed Fox
38357f7efa Fix error in console when hideFraction pref is missing (#1105) 2023-06-04 14:35:08 -04:00
Jed Fox
2c7b814d37 Disable “Reset sync” button when sync is disabled (#1104)
Extracted from #984
2023-06-04 14:22:32 -04:00
Jed Fox
e1f7262f2a Add clear error to API when no budget is open (#1073)
Previously, this would fail with a confusing “cannot read property of
`undefined`” error.
2023-06-04 13:57:29 -04:00
shall0pass
400078dce5 Goals: Cleanup script error (#1095)
There was one more location where the cleanup script could generate an
error. This should fully resolve the error, along with
https://github.com/actualbudget/actual/pull/1084.
2023-06-03 12:02:39 -04:00
Jed Fox
6de6ad661d Sync more often when using the API (#1075) 2023-06-02 15:34:42 -04:00
Matiss Janis Aboltins
a0dfb8afbd 🔖 (23.6.0) category hiding and filters for reports (#1087)
Web: https://github.com/actualbudget/actual/pull/1087
Server: https://github.com/actualbudget/actual-server/pull/207
Docs: https://github.com/actualbudget/docs/pull/179

---------

Co-authored-by: Jed Fox <git@jedfox.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-06-01 20:58:42 +01:00
shall0pass
1d301ac78d Bug: Cleanup script (#1084) 2023-06-01 10:25:00 -04:00
youngcw
8875f6d487 fix parser bug where by goals couldn't repeat on months/years >9 (#1083) 2023-06-01 08:26:28 -04:00
Tom French
66bfef28c0 chore: migrate some JS to TS (#1078) 2023-06-01 08:25:19 -04:00
Tom French
f03b8a3a14 chore: remove loot-design reference from readme.md (#1079) 2023-06-01 08:24:26 -04:00
Matiss Janis Aboltins
62ebd0627d 🐛 (budget) link from budget to transactions not working (#1067) 2023-06-01 07:11:28 +01:00
shall0pass
bb1a4747f5 Goals: Schedule include spent value in calculation (#1049)
This adds back the functionality, which was inadvertantly removed, that
includes the already spent column in the calculation when the template
is run.

Some transactions may be posted to the account prior to running the
templates and would result incorrect budgeted amounts.

---------

Co-authored-by: Jed Fox <git@jedfox.com>
2023-05-30 15:24:21 -04:00
shall0pass
d640859940 End of month cleanup script (#1016)
~This is really just a proof of concept. I have no delusions that this
might get included. I'm sure others might have a much cleaner
implementation.~
I'm now delusional.

Resolves https://github.com/actualbudget/actual/issues/508

Taking @youngcw 's advice, I changed the keyword to #cleanup for the end
of month script to keep it separated.

This screen video shows two categories that are sources of funds. At the
end of the month, any excess in these funds can be redistributed to your
highest priorities. Three categories are set as sinks, or recipients, of
excess funds.

#cleanup source   -> Move 'extra' funds to To Budget
#cleanup sink -> Fund category with To Budget funds, default weight = 1
#cleanup sink 2       -> Fund category with To Budget funds, weight = 2

Steps of the script:
1. Return funds from any category marked 'source'
2. Fund overspent categories fully if negative carryover is not allowed.
3. Fund each 'sink' category by the desired weight.

I run through the script twice. Once to show that if there is a debt
category that has a rolling negative balance, it will skip funding that
category first and once to show how if a rolling negative balance isn't
allowed, it will fund it before applying the weighted remainder. The
example shown uses weights of 60, 20, and 20; therefore, the Debt
category will receive 60% of the To Budget funds while General and Bills
receive 20% each. The weights could have been changed to 6, 2, and 2 or
3 for the Debt category with no additional value for General and Bills
to achieve the same result.


![cleanup_button](https://github.com/actualbudget/actual/assets/20625555/56ae2b29-9be6-4e85-b532-1b05cff7c4c7)
2023-05-30 15:24:03 -04:00
Jed Fox
e660e1e727 More import-related ESLint rules (#1070)
- Enforce that imports from the same package are merged into a single
import
- In `loot-core`, require that imports of other `loot-core` files use a
relative import (like the vast majority of such imports) rather than
specifiers starting with `loot-core/` (probably a result of moving files
out of other packages into `loot-core`)
2023-05-29 13:31:01 -04:00
Jed Fox
ad89aea45c Integrate useMemo into useLiveQuery (#1064) 2023-05-28 07:38:37 -04:00
Jack
c73416bdb8 [Feature] Hide category (#1060) 2023-05-27 15:15:09 +01:00
Jed Fox
6253aaa015 Use the useLiveQuery hook in a couple more places (#1061) 2023-05-25 16:50:55 -04:00
youngcw
0d2d861896 Goals: fix broken parser (#1059) 2023-05-25 07:06:35 -04:00
shall0pass
0baf4a094a Goals: Add timezone offset to date calculations (#1058) 2023-05-24 16:17:59 -04:00
Matiss Janis Aboltins
353474dacd 🔖 (6.0.1) api (#1057)
<!-- Thank you for submitting a pull request! Make sure to follow the
instructions to write release notes for your PR — it should only take a
minute or two:
https://github.com/actualbudget/docs#writing-good-release-notes -->
2023-05-24 18:44:14 +01:00
youngcw
5f38b579fe Template: Apply schedule template only on needed month (#1052)
Add option to schedule templates to budget the full amount only in the
needed month. Default behavior stays the same of spreading the expense
out over the available range.

To use the option, use a template like `#template schedule full
SCHEDULE_NAME`


Also some minor cleanup.
2023-05-24 10:57:25 -04:00
Eike Siewertsen
1e2bc29a60 change copy-migrations shebang to bash (#1056) 2023-05-24 10:26:03 -04:00
Jed Fox
fafd162db0 Export api/methods.js at the top level again (#1054)
This allows using the API as documented. In #877, I think this was
unintentionally converted to be a named export.
2023-05-22 18:20:09 -04:00
Jack
ec5e98b934 Fixed a bug where it was possible to make a transfer to the same account as the one making the transfer. (#1038)
The payee autocomplete was always using cached accounts. Added a check
to see if accounts was already passed in as a parameter - only using
cached if it wasnt.
2023-05-21 18:42:05 +01:00
Trevor Farlow
18e3a16299 Update to React Router v5.1 conventions (#1045) 2023-05-20 16:01:10 -06:00
Jed Fox
efe4194f9c A few fixes for Electron (#1048) 2023-05-20 09:46:10 -04:00
chylex
4249a0beb1 Make number parsing agnostic to decimal and thousands separators (#894) (#1029) 2023-05-20 08:27:17 -04:00
Shazib Hussain
461132b95e Fix electron app (#1003)
Updates to the latest version of electron and moves the backend-frontend
communication from node-ipc to websockets. This resolves the previous
roadblock regarding `nodeIntegration` .

Done

- Remove node-ipc in favour of websockets. 
- Move file copying out of `preload.js` to avoid importing module `fs`
there
- Bump all electron pacakge versions to the latest
- Added new package for finding open ports as node-ipc is gone
- Tweaked webpack config for above changes


Partially fixes #468

Questions/ Pending:
- Literally every single test fails for me, presumably some issue with
my setup/environment.
- The websocket communication is not using TLS. I'm not sure how to
enable this, or if we even need to as its all local.
- Still need to create the CI for building/deploying but I'm not sure
where start in this regard as i have no exp with it. Presumably we will
need to point the electron auto-updater to the github releases url's. If
people are happy with this PR I will look at adding the CI before its
merged.
- In dev mode only, I have disabled TLS security becuase my docker
container's cert is not signed. I _assume_ this will be true for other
people who spin up the server on thier own hardware. Perhaps I just need
to change my cert to one from letsencrypt or something...

Notes.
I have not touched javascript in eons so my apologies if the commit
trail is a bit fragmented. I tried to keep them fairly contained and
then there is a slightly gnarly final commit fixing all the linter
issues... Please let me know if you want me to squash some commits etc.

I initially tried to move this to web workers the same way the web app
does it but this was unsuccessful. I have found no way to spin up a
worker in one place (frontend/backend) and then pass this worker to the
other. The electron ipc channels don't allow you to directly pass
objects such as workers, everything is cloned/serialised. Passing a port
number so the other end can spin up its own socket works fine.

---------

Co-authored-by: Shazib Hussain <contact@shazib.com>
Co-authored-by: Jed Fox <git@jedfox.com>
2023-05-18 19:56:48 -04:00
Jed Fox
19af0b36a2 Upgrade react-spring, remove wobble (#1043)
Now we just have one spring animation library
2023-05-17 20:04:07 -04:00
Jed Fox
6fc4fc294a Update Yarn (#1042) 2023-05-17 18:54:06 -04:00
Alberto Gasparin
a6b6295426 Convert client to TS part 2 (#1037)
Another batch of components in `desktop-client` converted to TS
2023-05-18 07:42:53 +10:00
Trevor Farlow
029dfd4688 Responsive context (#964)
Introduces a **ResponsiveProvider** as the sole location that tracks
window size and makes that info available to the entire app. This can be
used for media queries and size-based component switching.
---------

Co-authored-by: Jed Fox <git@jedfox.com>
2023-05-16 21:02:07 -06:00
Jed Fox
2587350d1e Remove messaging around where to find schedules in the future (#1033) 2023-05-16 18:37:51 -04:00
Jed Fox
d400ebfda0 Fix the API (again) (#1002) 2023-05-16 14:56:24 -04:00
Jed Fox
34c8a73ee5 Remove @reactions/component dependency (#1036)
We can use hooks now!
2023-05-16 14:41:36 -04:00
Jack
4ecb58cd5c Updated account ordering in the account autocomplete popup (#1034) 2023-05-15 18:55:17 -04:00
SudoCerb
1305335f0a Add “Show unused payees” button (#1011)
# Add ability to filter the Manage Payees screen to show orphaned payees
only.

I aimed to modify as little code as possible - we now have a button on
the Manage Payees screen that will filter the table to show orphaned
payees only.
2023-05-15 16:18:14 -04:00
Jonas De Kegel
15b51921b2 Add .devcontainer (#1032)
Adds support for [devcontainers](https://containers.dev/), this should
make onboarding easier and should allow (especially simpler)
contributions entirely online via Github Codespaces 🚀
2023-05-15 11:04:28 -04:00
youngcw
54f9b712e4 Fix infinite loop in repeat goal (#1019)
I believe I found the infinite loop problem in the repeat goal.  

This doesn't mess things up if you are budgeting the same month that the
goal starts, that's probably why we didn't see it before.

Side note: The logic always starts at the start date in the template,
then increments until falling in the right month window. If this
template gets used for, say, a few years, it will start to bog down the
processing. If someone has a good quick fix I can add that.
2023-05-13 08:36:00 -04:00
shall0pass
5afd76fb45 Goals: Compounding changes for By and Schedules (#1028) 2023-05-13 08:23:59 -04:00
jonezy35
54c8d5b7b8 Added Dev Container (#1023) 2023-05-11 09:55:53 -04:00
Jed Fox
e4e9267c08 Pull shiftKey state directly from the initiating event (#1022)
Fixes #1021
2023-05-10 18:45:34 -04:00
TheTrueCaligari
655b677961 bugfix: wrong regex in the space-dot format (#1017)
The regex for the "space-dot" format was incorrect.

When entering the amount on a schedule, the number was incorrectly read
(12.34 became 1234.00)
Strangely, it was not an issue with transactions...
2023-05-10 10:54:33 -04:00
TheTrueCaligari
8faa7bd68d Fix for issue #319 (#1008) 2023-05-09 10:11:04 -04:00
Alberto Gasparin
f618055aab Migrate top common components to TS (#979) 2023-05-09 20:31:22 +10:00
Jed Fox
54fa4bccf6 Enable 'curly' rule (#1015)
Multi-line `if`/`for` statements in JS can be confusing since there
aren’t braces to indicate which code is enclosed in the statement. I set
the configuration to `multi-line` to enforce usage of braces for
multi-line statement bodies, but still allow things like `if (foo)
return;`. I additionally added the `consistent` option to require braces
for all elements of an if/else chain if one element has it. As you can
see, this set of options pretty closely matches the existing code style.

I was going to comment in #1008 about this stylistic change but realized
that it’s (IMO) a little impolite to ask for code style changes unless
they can be automatically enforced.

Note that `if (foo) { \n return; \n }` is still valid and won’t be
collapsed. I tried to automatically collapse all such cases but it was a
lot of files and I didn’t want to pick out the useful from the useless
differences.
2023-05-08 22:54:17 -04:00
Matiss Janis Aboltins
24d4070667 🐛 (TransactionTable) fix split-transaction focus field (#999)
Closes #996
2023-05-08 12:06:30 -06:00
youngcw
7b1c3665d5 Add feature request link to README (#1012) 2023-05-07 18:45:29 -04:00
youngcw
7d80c3eda6 Goal Templates: add option to prevent removing funds when using "up to" in goal (#1004) 2023-05-07 09:35:04 -04:00
Davis Silverman
f7aa313aea Detect more errors in JS OFX importer. (#1005) 2023-05-07 06:38:41 -04:00
Jed Fox
c9576d98e4 Remove “needs votes” label from feature requests that have been implemented (#1001) 2023-05-06 18:06:05 -04:00
Matiss Janis Aboltins
bcc2abf472 🐛 (reports) 1y date range should be 12 months not 13 months (#1000)
Closes #375
2023-05-05 21:51:30 +01:00
Matiss Janis Aboltins
933ca3ecca (reports) ability to fine-tune reports with filters (#994) 2023-05-05 19:54:23 +01:00
TheTrueCaligari
acaff825c1 Add a new number format (space-dot) (#995) 2023-05-05 10:26:35 -04:00
2885 changed files with 87070 additions and 376434 deletions

View File

@@ -0,0 +1,14 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/docker-existing-docker-compose
{
"name": "Actual development",
"dockerComposeFile": [
"../docker-compose.yml",
"docker-compose.yml"
],
// Alternatively:
// "image": "mcr.microsoft.com/devcontainers/typescript-node:0-16",
"service": "actual-development",
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
"postCreateCommand": "yarn install"
}

View File

@@ -0,0 +1,6 @@
version: '3.8'
services:
actual-development:
volumes:
- ..:/workspaces:cached
command: /bin/sh -c "while sleep 1000; do :; done"

27
.eslintignore Normal file
View File

@@ -0,0 +1,27 @@
packages/api/app/bundle.api.js
packages/api/dist
packages/api/@types
packages/api/migrations
packages/crdt/dist
packages/desktop-client/bundle.browser.js
packages/desktop-client/build/
packages/desktop-client/build-stats/
packages/desktop-client/public/kcab/
packages/desktop-client/public/data/
packages/desktop-client/**/node_modules/*
packages/desktop-client/node_modules/
packages/desktop-client/src/icons/**/*
packages/desktop-client/test-results/
packages/desktop-electron/client-build/
packages/desktop-electron/dist/
packages/import-ynab4/**/node_modules/*
packages/import-ynab5/**/node_modules/*
packages/loot-core/**/node_modules/*
packages/loot-core/**/lib-dist/*
packages/loot-core/**/proto/*

View File

@@ -1,3 +1,4 @@
/* eslint-disable rulesdir/typography */
const path = require('path');
const rulesDirPlugin = require('eslint-plugin-rulesdir');
@@ -9,32 +10,112 @@ rulesDirPlugin.RULES_DIR = path.join(
'rules',
);
const ruleFCMsg =
'Type the props argument and let TS infer or use ComponentType for a component prop';
const restrictedImportPatterns = [
{
group: ['*.api', '*.web', '*.electron'],
message: 'Dont directly reference imports from other platforms',
},
{
group: ['uuid'],
importNames: ['*'],
message: "Use `import { v4 as uuidv4 } from 'uuid'` instead",
},
];
const restrictedImportColors = [
{
group: ['**/style', '**/colors'],
importNames: ['colors'],
message: 'Please use themes instead of colors',
},
];
module.exports = {
plugins: ['prettier', 'import', 'rulesdir', '@typescript-eslint'],
extends: ['react-app', 'plugin:@typescript-eslint/recommended'],
extends: [
'react-app',
'plugin:react/recommended',
'plugin:prettier/recommended',
'plugin:@typescript-eslint/recommended',
'plugin:import/typescript',
],
parser: '@typescript-eslint/parser',
parserOptions: { project: [path.join(__dirname, './tsconfig.json')] },
reportUnusedDisableDirectives: true,
globals: {
globalThis: false,
vi: true,
},
rules: {
'prettier/prettier': 'error',
'no-unused-vars': 'off',
'prettier/prettier': 'warn',
'no-restricted-globals': ['error'].concat(
// Note: base rule explicitly disabled in favor of the TS one
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': [
'warn',
{
varsIgnorePattern: '^_',
ignoreRestSiblings: true,
},
],
curly: ['warn', 'multi-line', 'consistent'],
'no-restricted-globals': ['warn'].concat(
require('confusing-browser-globals').filter(g => g !== 'self'),
),
'react/jsx-no-useless-fragment': 'error',
'react/jsx-filename-extension': [
'warn',
{ extensions: ['.jsx', '.tsx'], allow: 'as-needed' },
],
'react/jsx-no-useless-fragment': 'warn',
'react/self-closing-comp': 'warn',
'react/no-unstable-nested-components': [
'warn',
{ allowAsProps: true, customValidators: ['formatter'] },
],
'rulesdir/typography': 'error',
'rulesdir/typography': 'warn',
'rulesdir/prefer-if-statement': 'warn',
// https://github.com/eslint/eslint/issues/16954
// https://github.com/eslint/eslint/issues/16953
'no-loop-func': 'off',
// Do don't need this as we're using TypeScript
'react/prop-types': 'off',
// TODO: re-enable these rules
'react-hooks/exhaustive-deps': 'off',
'react/display-name': 'off',
'react/react-in-jsx-scope': 'off',
// 'react-hooks/exhaustive-deps': [
// 'warn',
// {
// additionalHooks: 'useLiveQuery',
// },
// ],
'import/no-useless-path-segments': 'error',
'no-var': 'warn',
'react/jsx-curly-brace-presence': 'warn',
'object-shorthand': ['warn', 'properties'],
'import/extensions': [
'warn',
'never',
{
json: 'always',
},
],
'import/no-useless-path-segments': 'warn',
'import/no-duplicates': ['warn', { 'prefer-inline': true }],
'import/no-unused-modules': ['warn', { unusedExports: true }],
'import/order': [
'error',
'warn',
{
alphabetize: {
caseInsensitive: true,
@@ -62,18 +143,146 @@ module.exports = {
},
],
// Rules disable during TS migration
'@typescript-eslint/no-var-requires': 'off',
'prefer-const': 'off',
'prefer-spread': 'off',
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/no-unused-vars': [
'error',
'no-restricted-syntax': [
'warn',
{
args: 'none',
varsIgnorePattern: '^_',
ignoreRestSiblings: true,
// forbid React.* as they are legacy https://twitter.com/dan_abramov/status/1308739731551858689
selector:
":matches(MemberExpression[object.name='React'], TSQualifiedName[left.name='React'])",
message:
'Using default React import is discouraged, please use named exports directly instead.',
},
{
// forbid <a> in favor of <LinkButton> or <ExternalLink>
selector: 'JSXOpeningElement[name.name="a"]',
message:
'Using <a> is discouraged, please use <LinkButton> or <ExternalLink> instead.',
},
],
'no-restricted-imports': [
'warn',
{ patterns: [...restrictedImportPatterns, ...restrictedImportColors] },
],
'@typescript-eslint/ban-ts-comment': [
'error',
{ 'ts-ignore': 'allow-with-description' },
],
// Rules disable during TS migration
'@typescript-eslint/no-var-requires': 'off',
'prefer-const': 'warn',
'prefer-spread': 'off',
'@typescript-eslint/no-empty-function': 'off',
'import/no-default-export': 'warn',
},
overrides: [
{
files: ['.eslintrc.js', './**/.eslintrc.js'],
parserOptions: { project: null },
rules: {
'@typescript-eslint/consistent-type-exports': 'off',
},
},
{
files: [
'./packages/desktop-client/**/*.{ts,tsx}',
'./packages/loot-core/src/client/**/*.{ts,tsx}',
],
rules: {
// enforce type over interface
'@typescript-eslint/consistent-type-definitions': ['warn', 'type'],
// enforce import type
'@typescript-eslint/consistent-type-imports': [
'warn',
{ prefer: 'type-imports', fixStyle: 'inline-type-imports' },
],
'@typescript-eslint/ban-types': [
'warn',
{
types: {
// forbid FC as superflous
FunctionComponent: { message: ruleFCMsg },
FC: { message: ruleFCMsg },
},
extendDefaults: true,
},
],
},
},
{
files: ['./packages/desktop-client/**/*'],
excludedFiles: [
'./packages/desktop-client/src/hooks/useNavigate.{ts,tsx}',
],
rules: {
'no-restricted-imports': [
'warn',
{
patterns: [
{
group: ['react-router-dom'],
importNames: ['useNavigate'],
message: 'Please use Actuals useNavigate() hook instead.',
},
],
},
],
},
},
{
files: ['./packages/loot-core/src/**/*'],
rules: {
'no-restricted-imports': [
'warn',
{
patterns: [
...restrictedImportPatterns,
{
group: ['loot-core/**'],
message:
'Please use relative imports in loot-core instead of importing from `loot-core/*`',
},
],
},
],
},
},
{
files: [
'packages/loot-core/src/types/**/*',
'packages/loot-core/src/client/state-types/**/*',
'**/icons/**/*',
'**/{mocks,__mocks__}/**/*',
// can't correctly resolve usages
'**/*.{testing,electron,browser,web,api}.ts',
],
rules: { 'import/no-unused-modules': 'off' },
},
{
files: [
'./packages/desktop-client/src/style/index.*',
'./packages/desktop-client/src/style/palette.*',
],
rules: {
'no-restricted-imports': ['off', { patterns: restrictedImportColors }],
},
},
{
files: [
'./packages/api/migrations/*',
'./packages/loot-core/migrations/*',
],
rules: {
'import/no-default-export': 'off',
},
},
],
settings: {
'import/resolver': {
typescript: {
alwaysTryTypes: true,
},
},
},
};

6
.gitattributes vendored
View File

@@ -8,8 +8,12 @@
# Declare files that will always have LF line endings on checkout.
*.js text eol=lf
*.ts text eol=lf
*.sh text eol=lf
*.tsx text eol=lf
yarn.lock text eol=lf
# Denote all files that are truly binary and should not be modified.
*.png binary
*.jpg binary
*.jpg binary

3
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,3 @@
# Funding policies: https://actualbudget.org/docs/contributing/leadership/funding
open_collective: actual
github: actualbudget

View File

@@ -1,7 +1,7 @@
name: Bug Report
description: File a bug report also known as an issue or problem.
title: '[Bug]: '
labels: ['bug', 'needs triage']
labels: ['bug']
body:
- type: markdown
id: intro-md
@@ -18,6 +18,18 @@ body:
required: true
validations:
required: true
- type: checkboxes
id: bank-sync-issue
attributes:
label: 'Is this related to GoCardless, Simplefin or another bank-sync provider?'
description: 'Most issues with bank-sync providers are due to a lack of a custom bank-mapper (i.e. payee or other fields not coming through). In such cases you can create a custom bank mapper in [actual-server](https://github.com/actualbudget/actual-server/blob/master/src/app-gocardless/README.md) repository. Other likely issue is misconfigured server - in which case please reach out via the [community Discord](https://discord.gg/pRYNYr4W5A) to get support.'
options:
- label: 'I have checked my server logs and could not see any errors there'
- label: 'I will be attaching my server logs to this issue'
- label: 'I will be attaching my client-side (browser) logs to this issue'
- label: 'I understand that this issue will be automatically closed if insufficient information is provided'
validations:
required: false
- type: textarea
id: what-happened
attributes:
@@ -48,6 +60,7 @@ body:
- Docker
- Fly.io
- NAS
- Desktop App (Electron)
- Other
validations:
required: false
@@ -61,6 +74,7 @@ body:
- Chrome
- Safari
- Microsoft Edge
- Desktop App (Electron)
- Other
- type: dropdown
id: operating-system

View File

@@ -1 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Support
url: https://discord.gg/pRYNYr4W5A
about: Need help with something? Perhaps having issues setting up bank-sync with GoCardless or SimpleFin? Reach out to the community on Discord.

View File

@@ -1,7 +1,7 @@
name: Feature request
description: Request a missing feature
title: '[Feature] '
labels: ['feature', 'needs triage']
labels: ['feature']
body:
- type: markdown
id: intro-md

60
.github/actions/check-migrations.js vendored Normal file
View File

@@ -0,0 +1,60 @@
#!/usr/bin/env node
// overview:
// 1. Identify the migrations in packages/loot-core/migrations/* on `master` and HEAD
// 2. Make sure that any new migrations on HEAD are dated after the latest migration on `master`.
const fs = require('fs');
const path = require('path');
const { spawnSync } = require('child_process');
const migrationsDir = path.join(
__dirname,
'..',
'..',
'packages',
'loot-core',
'migrations',
);
function readMigrations(ref) {
const { stdout } = spawnSync('git', [
'ls-tree',
'--name-only',
ref,
migrationsDir + '/',
]);
const files = stdout.toString().split('\n').filter(Boolean);
console.log(`Found ${files.length} migrations on ${ref}.`);
return files
.map(file => path.basename(file))
.filter(file => !file.startsWith('.'))
.map(name => ({
date: parseInt(name.split('_')[0]),
name: name.match(/^\d+_(.+?)(\.sql)?$/)?.[1] ?? '***' + name,
}));
}
spawnSync('git', ['fetch', 'origin', 'master']);
let masterMigrations = readMigrations('origin/master');
let headMigrations = readMigrations('HEAD');
let latestMasterMigration = masterMigrations[masterMigrations.length - 1].date;
let newMigrations = headMigrations.filter(
migration => !masterMigrations.find(m => m.name === migration.name),
);
let badMigrations = newMigrations.filter(
migration => migration.date <= latestMasterMigration,
);
if (badMigrations.length) {
console.error(
`The following migrations are dated before the latest migration on master:`,
);
badMigrations.forEach(migration => {
console.error(` ${migration.name}`);
});
process.exit(1);
} else {
console.log(`All migrations are dated after the latest migration on master.`);
}

View File

@@ -0,0 +1,184 @@
#!/usr/bin/env node
// overview:
// 1. Fetch the issues that are linked to the PR
// 2. Filter out the issues that are not feature requests
// 3. For each feature request:
// 1. Remove the 'help wanted' & 'needs votes' labels
// 3. Find the automated comment, hide the comment as 'outdated'
// 5. Post a new comment saying that the feature request has been implemented, and will be released in the next version. Link to the PR.
async function makeAPIRequest(query, variables) {
const res = await fetch('https://api.github.com/graphql', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
'Content-Type': 'application/json',
Accept: 'application/json',
},
body: JSON.stringify({ query, variables }),
});
return res.json();
}
function group(name, body) {
console.log(`::group::${name}`);
const result = body();
if (result instanceof Promise) {
return result.finally(() => console.log(`::endgroup::`));
}
console.log(`::endgroup::`);
return result;
}
async function main() {
const featureRequests = await group('Pull Request API Response', async () => {
const res = await makeAPIRequest(
/* GraphQL */ `
query FetchLinkedIssues($pr: Int!) {
repository(owner: "actualbudget", name: "actual") {
pullRequest(number: $pr) {
closingIssuesReferences(first: 10) {
nodes {
id
number
labels(first: 10) {
nodes {
id
name
}
}
}
}
}
}
}
`,
{ pr: parseInt(process.env.PR_NUMBER) },
);
console.log(JSON.stringify(res, null, 2));
return res.data.repository.pullRequest.closingIssuesReferences.nodes.filter(
issue => issue.labels.nodes.some(label => label.name === 'feature'),
);
});
if (featureRequests.length === 0) {
console.log('No linked feature requests found');
return;
}
for (const { id, number, labels } of featureRequests) {
await group(`Issue #${number}: Remove labels`, async () => {
const toRemove = labels.nodes
.filter(
label =>
label.name === 'help wanted' ||
label.name === 'needs votes' ||
label.name === 'good first issue',
)
.map(label => label.id);
const res = await makeAPIRequest(
/* GraphQL */ `
mutation RemoveLabels($issue: ID!, $labels: [ID!]!) {
removeLabelsFromLabelable(
input: {
clientMutationId: "1"
labelIds: $labels
labelableId: $issue
}
) {
clientMutationId
}
}
`,
{
issue: id,
labels: toRemove,
},
);
console.log(JSON.stringify(res, null, 2));
});
await group(`Issue #${number}: Collapse automatic comment`, async () => {
const commentRes = await makeAPIRequest(
/* GraphQL */ `
query FetchComments($issue: Int!) {
repository(owner: "actualbudget", name: "actual") {
issue(number: $issue) {
comments(first: 100) {
nodes {
id
body
author {
login
}
}
}
}
}
}
`,
{ issue: number },
);
console.log(JSON.stringify(commentRes, null, 2));
const comments = commentRes.data.repository.issue.comments.nodes.filter(
comment => comment.author.login === 'github-actions',
);
const commentToCollapse =
comments.find(comment =>
comment.body.includes('<!-- feature-auto-close-comment -->'),
) ||
comments.find(comment =>
comment.body.includes(
':sparkles: Thanks for sharing your idea! :sparkles:',
),
);
if (!commentToCollapse) {
console.log('No comment to collapse found');
process.exit(1);
}
const res = await makeAPIRequest(
/* GraphQL */ `
mutation CollapseComment($comment: ID!) {
minimizeComment(
input: { classifier: OUTDATED, subjectId: $comment }
) {
clientMutationId
}
}
`,
{ comment: commentToCollapse.id },
);
console.log(JSON.stringify(res, null, 2));
});
await group(`Issue #${number}: Post comment`, async () => {
const res = await makeAPIRequest(
/* GraphQL */ `
mutation PostComment($issue: ID!, $body: String!) {
addComment(
input: { subjectId: $issue, body: $body, clientMutationId: "1" }
) {
clientMutationId
}
}
`,
{
issue: id,
body: `:tada: This feature has been implemented in #${process.env.PR_NUMBER} and will be released in the next version. Thanks for sharing your idea! :tada:\n\n<!-- feature-implemented-comment -->`,
},
);
console.log(JSON.stringify(res, null, 2));
});
}
}
main().catch(err => {
console.error(err);
process.exit(1);
});

View File

@@ -9,8 +9,8 @@ function get_status() {
curl --header "Authorization: Bearer $GITHUB_TOKEN" "https://api.github.com/repos/actualbudget/actual/commits/$COMMIT_SHA/statuses" > /tmp/status.json
cat /tmp/status.json
echo "::endgroup::"
netlify=$(jq '[.[] | select(.context == "netlify/actualbudget/deploy-preview")][0]' /tmp/status.json)
state=$(jq -r '.state' <<< "$netlify")
netlify=$(yarn jq '[.[] | select(.context == "netlify/actualbudget/deploy-preview")][0]' /tmp/status.json)
state=$(yarn jq -r '.state' <<< "$netlify")
echo "::group::Netlify Status"
echo "$netlify"
echo "::endgroup::"
@@ -32,7 +32,7 @@ done
if [ "$state" == "success" ]; then
echo -e "\033[0;32mNetlify build succeeded!\033[0m"
jq -r '"url=" + .target_url' <<< "$netlify" > $GITHUB_OUTPUT
yarn jq -r '"url=" + .target_url' <<< "$netlify" > $GITHUB_OUTPUT
exit 0
else
echo -e "\033[0;31mNetlify build failed. Cancelling end-to-end tests.\033[0m"

View File

@@ -6,13 +6,13 @@ runs:
- name: Install node
uses: actions/setup-node@v3
with:
node-version: 16.15.0
node-version: 18.16.0
- name: Cache
uses: actions/cache@v3
id: cache
with:
path: '**/node_modules'
key: yarn-v1-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
key: yarn-v1-${{ runner.os }}-${{ hashFiles('.nvmrc') }}-${{ hashFiles('**/yarn.lock') }}
- name: Install
run: yarn --immutable
shell: bash

View File

@@ -12,7 +12,10 @@ on:
branches:
- master
pull_request:
branches: '*'
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
jobs:
api:
@@ -23,6 +26,29 @@ jobs:
uses: ./.github/actions/setup
- name: Build API
run: cd packages/api && yarn build
- name: Create package tgz
run: cd packages/api && yarn pack && mv package.tgz actual-api.tgz
- name: Upload Build
uses: actions/upload-artifact@v3
with:
name: actual-api
path: packages/api/actual-api.tgz
crdt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up environment
uses: ./.github/actions/setup
- name: Build CRDT
run: cd packages/crdt && yarn build
- name: Create package tgz
run: cd packages/crdt && yarn pack && mv package.tgz actual-crdt.tgz
- name: Upload Build
uses: actions/upload-artifact@v3
with:
name: actual-crdt
path: packages/crdt/actual-crdt.tgz
web:
runs-on: ubuntu-latest
@@ -37,21 +63,8 @@ jobs:
with:
name: actual-web
path: packages/desktop-client/build
# TODO: re-enable after solving https://github.com/actualbudget/actual/issues/468
# electron:
# # As electron builds take longer, we only run them in master.
# if: github.event_name != 'pull_request'
# strategy:
# matrix:
# os:
# - ubuntu-latest
# - windows-latest
# - macos-latest
# runs-on: ${{ matrix.os }}
# steps:
# - uses: actions/checkout@v3
# - name: Set up environment
# uses: ./.github/actions/setup
# - name: Build Electron
# run: ./bin/package
- name: Upload Build Stats
uses: actions/upload-artifact@v3
with:
name: build-stats
path: packages/desktop-client/build-stats

View File

@@ -1,16 +0,0 @@
name: Check release notes
on:
pull_request:
branches:
- '*'
- '!release/*'
jobs:
check:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Check release notes
uses: actualbudget/actions/release-notes/check@main

48
.github/workflows/check.yml vendored Normal file
View File

@@ -0,0 +1,48 @@
name: Test
on:
push:
branches:
- master
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up environment
uses: ./.github/actions/setup
- name: Lint
run: yarn lint
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up environment
uses: ./.github/actions/setup
- name: Typecheck
run: yarn typecheck
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up environment
uses: ./.github/actions/setup
- name: Test
run: yarn test
migrations:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '19'
- name: Check migrations
run: node ./.github/actions/check-migrations.js

View File

@@ -4,10 +4,13 @@ on:
push:
branches: [master]
pull_request:
branches: [master]
schedule:
- cron: '23 11 * * 6'
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
jobs:
analyze:
name: Analyze

View File

@@ -5,26 +5,61 @@ on: [pull_request]
env:
GITHUB_PR_NUMBER: ${{github.event.pull_request.number}}
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
test:
name: Run end-to-end tests on Netlify PR preview
netlify:
name: Wait for Netlify build to finish
runs-on: ubuntu-latest
outputs:
netlify_url: ${{ steps.netlify.outputs.url }}
steps:
- uses: actions/checkout@v3
- name: Set up environment
uses: ./.github/actions/setup
- name: Setup Playwright
run: npx playwright install chromium --with-deps
- name: Wait for Netlify build to finish
id: netlify
env:
COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: ./bin/netlify-wait-for-build
run: ./.github/actions/netlify-wait-for-build
functional:
name: Functional
needs: netlify
runs-on: ubuntu-latest
container:
image: mcr.microsoft.com/playwright:v1.41.1-jammy
steps:
- uses: actions/checkout@v3
- name: Set up environment
uses: ./.github/actions/setup
- name: Run E2E Tests on Netlify URL
run: yarn e2e
env:
E2E_START_URL: ${{ steps.netlify.outputs.url }}
E2E_START_URL: ${{ needs.netlify.outputs.netlify_url }}
- uses: actions/upload-artifact@v3
if: always()
with:
name: desktop-client-test-results
path: packages/desktop-client/test-results/
retention-days: 30
vrt:
name: Visual regression
needs: netlify
runs-on: ubuntu-latest
container:
image: mcr.microsoft.com/playwright:v1.41.1-jammy
steps:
- uses: actions/checkout@v3
- name: Set up environment
uses: ./.github/actions/setup
- name: Run VRT Tests on Netlify URL
run: yarn vrt
env:
E2E_START_URL: ${{ needs.netlify.outputs.netlify_url }}
- uses: actions/upload-artifact@v3
if: always()
with:

50
.github/workflows/electron-master.yml vendored Normal file
View File

@@ -0,0 +1,50 @@
name: Electron
defaults:
run:
shell: bash
env:
CI: true
on:
push:
branches:
- master
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false
jobs:
build:
strategy:
matrix:
os:
- ubuntu-latest
- windows-latest
- macos-latest
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- if: ${{ startsWith(matrix.os, 'windows') }}
run: pip.exe install setuptools
- if: ${{ ! startsWith(matrix.os, 'windows') }}
run: python3 -m pip install setuptools
- name: Set up environment
uses: ./.github/actions/setup
- name: Build Electron
run: ./bin/package-electron
env:
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
CSC_LINK: ${{ secrets.CSC_LINK }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
- name: Upload Build
uses: actions/upload-artifact@v3
with:
name: actual-electron-${{ matrix.os }}
path: |
packages/desktop-electron/dist/*.dmg
packages/desktop-electron/dist/*.exe
packages/desktop-electron/dist/*.AppImage

43
.github/workflows/electron-pr.yml vendored Normal file
View File

@@ -0,0 +1,43 @@
name: Electron
defaults:
run:
shell: bash
env:
CI: true
on:
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
build:
strategy:
matrix:
os:
- ubuntu-latest
- windows-latest
- macos-latest
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- if: ${{ startsWith(matrix.os, 'windows') }}
run: pip.exe install setuptools
- if: ${{ ! startsWith(matrix.os, 'windows') }}
run: python3 -m pip install setuptools
- name: Set up environment
uses: ./.github/actions/setup
- name: Build Electron
run: ./bin/package-electron
- name: Upload Build
uses: actions/upload-artifact@v3
with:
name: actual-electron-${{ matrix.os }}
path: |
packages/desktop-electron/dist/*.dmg
packages/desktop-electron/dist/*.exe
packages/desktop-electron/dist/*.AppImage

View File

@@ -1,17 +0,0 @@
name: Generate Release Notes
on:
push:
branches:
- release/*
jobs:
generate:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Generate release notes
uses: actualbudget/actions/release-notes/generate@main
with:
github_token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -9,8 +9,6 @@ jobs:
if: ${{ github.event.label.name == 'feature' }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- uses: actions-ecosystem/action-add-labels@v1
with:
labels: needs votes
@@ -26,12 +24,14 @@ jobs:
body: |
:sparkles: Thanks for sharing your idea! :sparkles:
This repository is now using lodash style issue management for enhancements. This means enhancement issues will now be closed instead of leaving them open. This doesnt mean we dont accept feature requests, though! We will consider implementing ones that receive many upvotes, and we welcome contributions for any feature requests marked as needing votes (just post a comment first so we can help you make a successful contribution).
This repository uses lodash style issue management for enhancements. That means enhancement issues are automatically closed. This doesnt mean we dont accept feature requests, though! We will consider implementing ones that receive many upvotes, and we welcome contributions for any feature requests marked as needing votes (just post a comment first so we can help you make a successful contribution).
The enhancement backlog can be found here: https://github.com/actualbudget/actual/issues?q=label%3A%22needs+votes%22+sort%3Areactions-%2B1-desc+
Dont forget to upvote the top comment with 👍!
<!-- feature-auto-close-comment -->
- name: Close Issue
run: gh issue close "${{ github.event.issue.number }}"
run: gh issue close "https://github.com/actualbudget/actual/issues/${{ github.event.issue.number }}"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -0,0 +1,35 @@
name: Handle completed feature requests
##########################################################################################
# WARNING! This workflow uses the 'pull_request_target' event. That mans that it will #
# always run in the context of the main actualbudget/actual repo, even if the PR is from #
# a fork. This is necessary to get access to a GitHub token that can post a comment on #
# the PR. Be VERY CAREFUL about adding things to this workflow, since forks can inject #
# arbitrary code into their branch, and can pollute the artifacts we download. Arbitrary #
# code execution in this workflow could lead to a compromise of the main repo. #
##########################################################################################
# See: https://securitylab.github.com/research/github-actions-preventing-pwn-requests #
##########################################################################################
on:
pull_request_target:
types: [closed]
permissions:
issues: write
jobs:
handle-feature-requests:
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
steps:
# This is not a security concern because we have approved & merged the PR
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '19'
- name: Handle feature requests
run: node .github/actions/handle-feature-requests.js
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -0,0 +1,14 @@
name: Remove 'help wanted' label from closed issues
on:
issues:
types: [closed]
jobs:
remove-help-wanted:
if: ${{ !contains(github.event.issue.labels.*.name, 'feature') && contains(github.event.issue.labels.*.name, 'help wanted') }}
runs-on: ubuntu-latest
steps:
- uses: actions-ecosystem/action-remove-labels@v1
with:
labels: help wanted

View File

@@ -1,18 +0,0 @@
name: Linter
on:
push:
branches:
- master
pull_request:
branches: '*'
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up environment
uses: ./.github/actions/setup
- name: Lint
run: yarn lint

View File

@@ -1,14 +0,0 @@
name: Mark new issue for triage
on:
issues:
types: [opened]
jobs:
needs-triage:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions-ecosystem/action-add-labels@v1
with:
labels: needs triage

21
.github/workflows/release-notes.yml vendored Normal file
View File

@@ -0,0 +1,21 @@
name: Release notes
on:
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
release-notes:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Check release notes
if: startsWith(github.head_ref, 'release/') == false
uses: actualbudget/actions/release-notes/check@main
- name: Generate release notes
if: startsWith(github.head_ref, 'release/') == true
uses: actualbudget/actions/release-notes/generate@main

85
.github/workflows/size-compare.yml vendored Normal file
View File

@@ -0,0 +1,85 @@
name: Compare Sizes
##########################################################################################
# WARNING! This workflow uses the 'pull_request_target' event. That mans that it will #
# always run in the context of the main actualbudget/actual repo, even if the PR is from #
# a fork. This is necessary to get access to a GitHub token that can post a comment on #
# the PR. Be VERY CAREFUL about adding things to this workflow, since forks can inject #
# arbitrary code into their branch, and can pollute the artifacts we download. Arbitrary #
# code execution in this workflow could lead to a compromise of the main repo. #
##########################################################################################
# See: https://securitylab.github.com/research/github-actions-preventing-pwn-requests #
##########################################################################################
on:
pull_request_target:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
compare:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- name: Wait for ${{github.base_ref}} build to succeed
uses: fountainhead/action-wait-for-check@v1.1.0
id: master-build
with:
token: ${{ secrets.GITHUB_TOKEN }}
checkName: web
ref: ${{github.base_ref}}
- name: Wait for PR build to succeed
uses: fountainhead/action-wait-for-check@v1.1.0
id: wait-for-build
with:
token: ${{ secrets.GITHUB_TOKEN }}
checkName: web
ref: ${{github.event.pull_request.head.sha}}
- name: Report build failure
if: steps.wait-for-build.outputs.conclusion == 'failure'
run: |
echo "Build failed on PR branch or ${{github.base_ref}}"
exit 1
- name: Download build artifact from ${{github.base_ref}}
uses: dawidd6/action-download-artifact@v2
id: pr-build
with:
branch: ${{github.base_ref}}
workflow: build.yml
name: build-stats
path: base
- name: Download build artifact from PR
uses: dawidd6/action-download-artifact@v2
with:
pr: ${{github.event.pull_request.number}}
workflow: build.yml
name: build-stats
path: head
- name: Strip content hashes from stats files
run: |
sed -i -E 's/index\.[0-9a-zA-Z_-]{8,}\./index./g' ./head/web-stats.json
sed -i -E 's/\.[0-9a-zA-Z_-]{8,}\.chunk\././g' ./head/web-stats.json
sed -i -E 's/\.[0-9a-f]{8,}\././g' ./head/*.json
sed -i -E 's/index\.[0-9a-zA-Z_-]{8,}\./index./g' ./base/web-stats.json
sed -i -E 's/\.[0-9a-zA-Z_-]{8,}\.chunk\././g' ./base/web-stats.json
sed -i -E 's/\.[0-9a-f]{8,}\././g' ./base/*.json
- uses: twk3/rollup-size-compare-action@v1.0.0
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
current-stats-json-path: ./head/web-stats.json
base-stats-json-path: ./base/web-stats.json
title: desktop-client
- uses: github/webpack-bundlesize-compare-action@v1.8.2
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
current-stats-json-path: ./head/loot-core-stats.json
base-stats-json-path: ./base/loot-core-stats.json
title: loot-core

View File

@@ -1,20 +1,16 @@
name: Close inactive issues
name: 'Close stale PRs'
on:
schedule:
- cron: "30 1 * * *"
- cron: '30 1 * * *'
jobs:
close-issues:
stale:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v7
- uses: actions/stale@v8
with:
days-before-issue-stale: 90
days-before-issue-close: -1
stale-issue-label: "stale"
stale-issue-message: "🚧🚨 This issue is being marked as stale due to 90 days of inactivity. 🚧🚨"
only-labels: 'needs triage'
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-pr-message: 'This PR is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.'
close-pr-message: 'This PR was closed because it has been stalled for 5 days with no activity.'
days-before-stale: 30
days-before-close: 5
days-before-issue-stale: -1

View File

@@ -1,18 +0,0 @@
name: Test
on:
push:
branches:
- master
pull_request:
branches: '*'
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up environment
uses: ./.github/actions/setup
- name: Test
run: yarn test

View File

@@ -1,18 +0,0 @@
name: Typecheck
on:
push:
branches:
- master
pull_request:
branches: '*'
jobs:
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up environment
uses: ./.github/actions/setup
- name: Typecheck
run: yarn typecheck

6
.gitignore vendored
View File

@@ -2,6 +2,8 @@
!data/.gitkeep
/data2
packages/api/dist
packages/api/@types
packages/crdt/dist
packages/desktop-electron/client-build
packages/desktop-electron/.electron-symbols
packages/desktop-electron/dist
@@ -19,6 +21,7 @@ bundle.mobile.js
bundle.mobile.js.map
export-2020-01-10.csv
.idea
.vscode
**/*.log
@@ -30,3 +33,6 @@ export-2020-01-10.csv
!.yarn/releases
!.yarn/sdks
!.yarn/versions
# VSCode
.vscode

2
.nvmrc
View File

@@ -1 +1 @@
v16.15.0
v18.16.0

1
.prettierignore Normal file
View File

@@ -0,0 +1 @@
sync_pb.*

2
.secret-tokens.example Normal file
View File

@@ -0,0 +1,2 @@
export APPLE_ID=example@email.com
export APPLE_APP_SPECIFIC_PASSWORD=password

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

893
.yarn/releases/yarn-4.0.1.cjs vendored Executable file

File diff suppressed because one or more lines are too long

View File

@@ -1,9 +1,7 @@
compressionLevel: mixed
enableGlobalCache: false
nodeLinker: node-modules
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
spec: "@yarnpkg/plugin-workspace-tools"
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
spec: "@yarnpkg/plugin-interactive-tools"
yarnPath: .yarn/releases/yarn-3.4.1.cjs
yarnPath: .yarn/releases/yarn-4.0.1.cjs

View File

@@ -1,34 +1 @@
## Expectations
For smaller improvements or features - feel free to submit a PR or an issue if you don't have the necessary skills to build it yourself. For larger features we would recommend first opening an issue to discuss it with the team.
We aren't going to take every single little change. Don't be offended if we close your PR. In order for the project to stay healthy, we need to guard our bandwidth and also only take changes that align with Actual.
Here are some initial guidelines for how contributions will be treated:
- The mental health of the maintainers will be prioritized above all else. If this means some things get lost and PRs are unreviewed because maintainers are spending time with family or on themselves, we celebrate that.
- Multiple maintainers are key to this being a healthy project. Currently a few people have maintainer rights (see list below). We are actively looking for more people to come on as maintainers. If nobody steps up, expect less activity on this project.
- An open PR does not automatically deserve time for a full review and acceptance. It's up to the PR author to convince the maintainers that the change is good and worth reviewing. This involves a clear description for why the the change is being made, detailing the tradeoffs.
- We especially welcome improvements in automation: creating github actions to automatically generate builds, making the release process easier, etc.
## Main contributors
(sorted alphabetically)
- @albertogasparin
- @j-f1
- @jlongster
- @MatissJanis
- @rich-howell
- @trevdor
## Project ideas
We welcome all contributions from the community. If you have an idea for a feature you want to build - please go ahead and submit a PR with the implementation or if it's a larger feature - open a new issue so we can discuss it.
If you do not have ideas what to build: the issue list is always a good starting point. Look for issues labeled with "[help wanted](https://github.com/actualbudget/actual/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22)".
For first time contributions you can also filter the issues labeled with "[good first issue](https://github.com/actualbudget/actual/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)".
Please review the contributing documentation on our website: https://actualbudget.org/docs/contributing/

11
Dockerfile Normal file
View File

@@ -0,0 +1,11 @@
###################################################
# This Dockerfile is used by the docker-compose.yml
# file to build the development container.
# Do not make any changes here unless you know what
# you are doing.
###################################################
FROM node:18-bullseye as dev
RUN apt-get update -y && apt-get upgrade -y && apt-get install -y openssl
WORKDIR /app
CMD ["sh", "./bin/docker-start"]

View File

@@ -6,14 +6,14 @@
Actual is a local-first personal finance tool. It is 100% free and open-source, written in NodeJS, it has a synchronization element so that all your changes can move between devices without any heavy lifting.
If you are interested in contributing, or want to know how development works, see [CONTRIBUTING.md](https://github.com/actualbudget/actual/blob/master/CONTRIBUTING.md) we would love to have you.
If you are interested in contributing, or want to know how development works, see our [contributing](https://actualbudget.org/docs/contributing/) document we would love to have you.
Want to say thanks? Click the ⭐ at the top of the page.
## Key Links
- Actual [discord](https://discord.gg/pRYNYr4W5A) community.
- Actual [Community Documentation](https://actualbudget.github.io/docs)
- Actual [Community Documentation](https://actualbudget.org/docs)
## Installation
@@ -23,22 +23,28 @@ If you are only interested in running the latest version and not contributing to
The easiest way to get Actual running is to use the [actual-server](https://github.com/actualbudget/actual-server) project. That is the server for syncing changes across devices, and it comes with the latest version of Actual. The server will provide both the web project and a server for syncing.
You can get up and running quickly and easily by following our [Running Actual Locally Guide](https://actualbudget.github.io/docs/Installing/Local/your-own-machine)
You can get up and running quickly and easily by following our [Running Actual Locally Guide](https://actualbudget.org/docs/install/local)
## Documentation
We have a wide range of documentation on how to use Actual, this is all available in our [Community Documentation](https://actualbudget.github.io/docs), this includes topics on Budgeting, Account Management, Tips & Tricks and some documentation for developers.
We have a wide range of documentation on how to use Actual, this is all available in our [Community Documentation](https://actualbudget.org/docs), this includes topics on Budgeting, Account Management, Tips & Tricks and some documentation for developers.
## Code structure
The Actual app is split up into a few packages:
- loot-core - The core application that runs on any platform
- loot-design - The generic design components that make up the UI
- desktop-client - The desktop UI
- desktop-electron - The desktop app
More information on the project structure is available in our [community documentation](https://actualbudget.github.io/docs/Developers/project-layout).
More information on the project structure is available in our [community documentation](https://actualbudget.org/docs/contributing/project-details).
## Feature Requests
Current feature requests can be seen [here](https://github.com/actualbudget/actual/issues?q=is%3Aissue+label%3A%22needs+votes%22+sort%3Areactions-%2B1-desc).
Vote for your favorite requests by reacting :+1: to the top comment of the request.
To add new feature requests, open a new Issue of the "Feature Request" type.
## Sponsors

13
bin/docker-start Normal file
View File

@@ -0,0 +1,13 @@
#!/bin/sh
#####################################################
# This startup script is used by the docker container
# to check if the node_modules folder is empty and
# if so, run yarn to install the dependencies.
#####################################################
if [ ! -d "node_modules" ] || [ "$(ls -A node_modules)" = "" ]; then
yarn
fi
yarn start:browser

View File

@@ -1,11 +0,0 @@
#!/bin/sh
FILES=$(git diff --cached --name-only --diff-filter=ACMR "*.js" "*.jsx" | sed 's| |\\ |g')
[ -z "$FILES" ] && exit 0
# Prettify all selected files
echo "$FILES" | xargs ./node_modules/.bin/prettier --write
# Add back the modified/prettified files to staging
echo "$FILES" | xargs git add
exit 0

View File

@@ -1,27 +0,0 @@
#!/bin/sh -e
ROOT=$(cd "`dirname $0`"; pwd)
NPM_NAME="$1"
NAME="$2"
PACKAGE_DIR="`dirname "$ROOT"`/packages/$NAME"
if [ -z "$NAME" ] || [ -z "$NPM_NAME" ]; then
echo "Usage: `basename $0` <npm-name> <local-name>"
exit 1
fi
if [ -d "$PACKAGE_DIR" ]; then
read -p "Package exists, remove $PACKAGE_DIR? [y/N] " -r
if [ -z "$REPLY" ] || [ "$REPLY" != "y" ]; then
exit 2
fi
fi
rm -rf "$PACKAGE_DIR"
URL="`npm view "$NPM_NAME" dist.tarball`"
TMPDIR="`mktemp -d`"
cd "$TMPDIR"
wget -O tar.tgz "$URL"
tar xvzf tar.tgz
mv package "$PACKAGE_DIR"

View File

@@ -1,50 +0,0 @@
#!/bin/bash -e
VERSION=""
POSITIONAL=()
while [[ $# -gt 0 ]]; do
key="$1"
case $key in
--version)
VERSION="$2"
shift
shift
;;
*)
POSITIONAL+=("$1")
shift
;;
esac
done
set -- "${POSITIONAL[@]}"
NOTES="$@"
if [ -z "$VERSION" ]; then
echo "--version is required";
exit 1
fi
echo "Version: $VERSION"
echo "Notes: $NOTES"
read -p "Make release? [y/N] " -r
if [ -z "$REPLY" ] || [ "$REPLY" != "y" ]; then
exit 2
fi
source ./.secret-tokens
# Tag and push to make windows and linux versions
git push origin master
git tag -a "$VERSION" -m "$NOTES"
git push origin "$VERSION"
# Make a macOS version
./bin/package --release --version "$VERSION"
# TODO: browser version
# Finally, update github issues
curl -X POST -H "x-release-token: $RELEASE_TOKEN" https://actual-automoto.fly.dev/release/"$VERSION"

View File

@@ -1,111 +0,0 @@
#!/bin/bash -e
ROOT=`dirname $0`
VERSION=""
BETA=""
RELEASE=""
RELEASE_NOTES=""
CI=${CI:-false}
cd "$ROOT/.."
POSITIONAL=()
while [[ $# -gt 0 ]]; do
key="$1"
case $key in
--version)
VERSION="$2"
shift
shift
;;
--beta)
RELEASE="beta"
shift
;;
--release)
RELEASE="production"
shift
;;
*)
POSITIONAL+=("$1")
shift
;;
esac
done
set -- "${POSITIONAL[@]}"
if [ -z "$VERSION" ] && [ -n "$RELEASE" ]; then
echo "Version is required if making a release"
exit 1
fi
if [ -n "$RELEASE" ]; then
if [ -z "$CIRCLE_TAG" ]; then
read -p "Make release: $RELEASE v$VERSION? [y/N] " -r
if [ -z "$REPLY" ] || [ "$REPLY" != "y" ]; then
exit 2
fi
fi
if [ "$RELEASE" == "production" ]; then
if [ -z "$CIRCLE_TAG" ]; then
RELEASE_NOTES=`git tag -l --format="%(contents:subject)" "$VERSION"`
else
RELEASE_NOTES=`git tag -l --format="%(contents:subject)" "$CIRCLE_TAG"`
fi
fi
PACKAGE_VERSION=`node -p -e "require('./packages/desktop-electron/package.json').version"`
if [ "$VERSION" != "$PACKAGE_VERSION" ]; then
echo "Version in desktop-electron/package.json does not match given version! ($PACKAGE_VERSION)"
exit 4
fi
fi
if [ "$OSTYPE" == "msys" ]; then
if [ $CI != true ]; then
read -s -p "Windows certificate password: " -r CSC_KEY_PASSWORD
export CSC_KEY_PASSWORD
elif [ -n "$CIRCLE_TAG" ]; then
# We only want to run this on CircleCI as Github doesn't have the CSC_KEY_PASSWORD secret set.
certutil -f -p ${CSC_KEY_PASSWORD} -importPfx ~/windows-shift-reset-llc.p12
fi
fi
# We only need to run linting once (and this doesn't seem to work on
# Windows for some reason)
if [[ $CI != true && "$OSTYPE" == "darwin"* ]]; then
yarn lint
fi
yarn patch-package
yarn workspace loot-core build:node
yarn workspace @actual-app/web build
yarn workspace Actual update-client
(
cd packages/desktop-electron;
yarn clean;
export npm_config_better_sqlite3_binary_host="https://static.actualbudget.com/prebuild/better-sqlite3"
if [ "$RELEASE" == "production" ]; then
if [ -f ../../.secret-tokens ]; then
source ../../.secret-tokens
fi
yarn build --publish always -c.releaseInfo.releaseNotes="$RELEASE_NOTES" --arm64 --x64
echo "\nCreated release $VERSION with release notes \"$RELEASE_NOTES\""
elif [ "$RELEASE" == "beta" ]; then
yarn build --publish never --arm64 --x64
echo "\nCreated beta release $VERSION"
else
SKIP_NOTARIZATION=true yarn build --publish never --x64
fi
)

View File

@@ -1,63 +1,10 @@
#!/bin/bash -e
ROOT=`dirname $0`
VERSION=""
RELEASE=""
CI=${CI:-false}
cd "$ROOT/.."
POSITIONAL=()
while [[ $# -gt 0 ]]; do
key="$1"
case $key in
--version)
VERSION="$2"
shift
shift
;;
--beta)
RELEASE="beta"
shift
;;
--release)
RELEASE="production"
shift
;;
*)
POSITIONAL+=("$1")
shift
;;
esac
done
set -- "${POSITIONAL[@]}"
if [ -z "$VERSION" ] && [ -n "$RELEASE" ]; then
echo "Version is required if making a release"
exit 1
fi
if [ -n "$RELEASE" ]; then
read -p "Deploy release for browser: $RELEASE v$VERSION? [y/N] " -r
if [ -z "$REPLY" ] || [ "$REPLY" != "y" ]; then
exit 2
fi
PACKAGE_VERSION=`node -p -e "require('./packages/desktop-electron/package.json').version"`
if [ "$VERSION" != "$PACKAGE_VERSION" ] && [ "$VERSION-next" != "$PACKAGE_VERSION" ]; then
echo "Version in desktop-electron/package.json does not match given version! ($PACKAGE_VERSION)"
exit 4
fi
fi
# There's no need to check linting in CI as it'll be done in a different step.
if [ $CI != true ]; then
yarn lint
fi
ACTUAL_RELEASE_TYPE=$RELEASE yarn workspace loot-core build:browser
REACT_APP_RELEASE_TYPE=$RELEASE yarn workspace @actual-app/web build:browser
yarn workspace loot-core build:browser
yarn workspace @actual-app/web build:browser
echo "packages/desktop-client/build"

59
bin/package-electron Executable file
View File

@@ -0,0 +1,59 @@
#!/bin/bash -e
ROOT=`dirname $0`
RELEASE=""
CI=${CI:-false}
cd "$ROOT/.."
POSITIONAL=()
while [[ $# -gt 0 ]]; do
key="$1"
case $key in
--release)
RELEASE="production"
shift
;;
*)
POSITIONAL+=("$1")
shift
;;
esac
done
set -- "${POSITIONAL[@]}"
if [ "$OSTYPE" == "msys" ]; then
if [ $CI != true ]; then
read -s -p "Windows certificate password: " -r CSC_KEY_PASSWORD
export CSC_KEY_PASSWORD
elif [ -n "$CIRCLE_TAG" ]; then
# We only want to run this on CircleCI as Github doesn't have the CSC_KEY_PASSWORD secret set.
certutil -f -p ${CSC_KEY_PASSWORD} -importPfx ~/windows-shift-reset-llc.p12
fi
fi
yarn rebuild-electron
yarn workspace loot-core build:node
yarn workspace @actual-app/web build
yarn workspace desktop-electron update-client
(
cd packages/desktop-electron;
yarn clean;
if [ "$RELEASE" == "production" ]; then
if [ -f ../../.secret-tokens ]; then
source ../../.secret-tokens
fi
yarn build --publish never --arm64 --x64
echo "\nCreated release"
else
SKIP_NOTARIZATION=true yarn build --publish never --x64
fi
)

BIN
demo.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 157 KiB

After

Width:  |  Height:  |  Size: 107 KiB

16
docker-compose.yml Normal file
View File

@@ -0,0 +1,16 @@
###################################################
# This creates and stands up the development
# docker container. Depends on the Dockerfile and
# docker-start.sh files.
###################################################
services:
actual-development:
build: .
image: actual-development
ports:
- '3001:3001'
volumes:
- '.:/app'
restart: 'no'

View File

@@ -19,42 +19,52 @@
},
"scripts": {
"start": "yarn start:browser",
"start:desktop": "npm-run-all --parallel 'start:desktop-*'",
"start:desktop": "yarn rebuild-electron && npm-run-all --parallel 'start:desktop-*'",
"start:desktop-node": "yarn workspace loot-core watch:node",
"start:desktop-client": "yarn workspace @actual-app/web watch",
"start:desktop-electron": "yarn workspace Actual watch",
"start:desktop-electron": "yarn workspace desktop-electron watch",
"start:electron": "yarn start:desktop",
"start:browser": "npm-run-all --parallel 'start:browser-*'",
"start:browser-backend": "yarn workspace loot-core watch:browser",
"start:browser-frontend": "yarn workspace @actual-app/web start:browser",
"build:browser": "./bin/package-browser",
"test": "yarn workspaces foreach --parallel --verbose run test",
"test:debug": "yarn workspaces foreach --verbose run test",
"e2e": "yarn workspaces foreach --parallel --verbose run e2e",
"build:desktop": "./bin/package-electron",
"build:api": "yarn workspace @actual-app/api build",
"test": "yarn workspaces foreach --all --parallel --verbose run test",
"test:debug": "yarn workspaces foreach --all --verbose run test",
"e2e": "yarn workspaces foreach --all --parallel --verbose run e2e",
"vrt": "yarn workspaces foreach --all --parallel --verbose run vrt",
"rebuild-electron": "./node_modules/.bin/electron-rebuild -f -m ./packages/loot-core",
"rebuild-node": "yarn workspace loot-core rebuild",
"lint": "cross-env NODE_ENV=development yarn workspaces foreach --verbose run lint --max-warnings 0",
"typecheck": "yarn tsc",
"postinstall": "patch-package"
"lint": "eslint . --max-warnings 0 --ext .js,.jsx,.ts,.tsx",
"lint:verbose": "DEBUG=eslint:cli-engine eslint . --max-warnings 0",
"typecheck": "yarn tsc && tsc-strict",
"jq": "./node_modules/node-jq/bin/jq"
},
"devDependencies": {
"cross-env": "^5.1.5",
"cross-env": "^7.0.3",
"eslint": "^8.37.0",
"eslint-config-prettier": "^9.1.0",
"eslint-config-react-app": "7.0.1",
"eslint-plugin-prettier": "4.2.1",
"eslint-import-resolver-typescript": "3.5.5",
"eslint-plugin-import": "2.27.5",
"eslint-plugin-prettier": "5.1.3",
"eslint-plugin-react": "7.32.2",
"eslint-plugin-rulesdir": "^0.2.2",
"node-jq": "^4.0.1",
"npm-run-all": "^4.1.3",
"patch-package": "^6.1.2",
"prettier": "2.8.2",
"prettier": "3.2.4",
"react-refresh": "^0.14.0",
"source-map-support": "^0.5.21",
"typescript": "^5.0.2"
"typescript": "^5.0.2",
"typescript-strict-plugin": "^2.2.2-beta.2"
},
"resolutions": {
"react-error-overlay": "6.0.9"
"engines": {
"node": ">=18.0.0"
},
"packageManager": "yarn@3.4.1",
"packageManager": "yarn@4.0.1",
"browserslist": [
"electron 12.0",
"electron 24.0",
"defaults"
]
}

View File

@@ -1,2 +0,0 @@
app/bundle.api.js
dist

View File

@@ -1,3 +1,5 @@
app/bundle.api.js*
app/stats.json
migrations
default-db.sqlite
mocks/budgets/**/*

View File

@@ -2,4 +2,4 @@
npm install @actual-app/api
```
View docs here: https://actualbudget.github.io/docs/Developers/using-the-API
View docs here: https://actualbudget.org/docs/api/

View File

@@ -0,0 +1,21 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`API setup and teardown successfully loads budget 1`] = `
Array [
"2016-10",
"2016-11",
"2016-12",
"2017-01",
"2017-02",
"2017-03",
"2017-04",
"2017-05",
"2017-06",
"2017-07",
"2017-08",
"2017-09",
"2017-10",
"2017-11",
"2017-12",
]
`;

View File

@@ -23,7 +23,7 @@ class Query {
}
unfilter(exprs) {
let exprSet = new Set(exprs);
const exprSet = new Set(exprs);
return new Query({
...this.state,
filterExpressions: this.state.filterExpressions.filter(
@@ -37,13 +37,13 @@ class Query {
exprs = [exprs];
}
let query = new Query({ ...this.state, selectExpressions: exprs });
const query = new Query({ ...this.state, selectExpressions: exprs });
query.state.calculation = false;
return query;
}
calculate(expr) {
let query = this.select({ result: expr });
const query = this.select({ result: expr });
query.state.calculation = true;
return query;
}
@@ -99,6 +99,6 @@ class Query {
}
}
export default function q(table) {
export function q(table) {
return new Query({ table });
}

View File

@@ -1,31 +0,0 @@
import fetch from 'node-fetch';
import * as bundle from './app/bundle.api';
import * as injected from './injected';
let actualApp;
export const internal = bundle.lib;
export * as methods from './methods';
export * as utils from './utils';
export async function init(config = {}) {
if (actualApp) {
return;
}
global.fetch = fetch;
await bundle.init(config);
actualApp = bundle.lib;
injected.override(bundle.lib.send);
return bundle.lib;
}
export async function shutdown() {
if (actualApp) {
await actualApp.send('close-budget');
actualApp = null;
}
}

53
packages/api/index.ts Normal file
View File

@@ -0,0 +1,53 @@
import type {
RequestInfo as FetchInfo,
RequestInit as FetchInit,
// @ts-ignore: false-positive commonjs module error on build until typescript 5.3
} from 'node-fetch'; // with { 'resolution-mode': 'import' };
// loot-core types
import type { InitConfig } from 'loot-core/server/main';
// @ts-ignore: bundle not available until we build it
// eslint-disable-next-line import/extensions
import * as bundle from './app/bundle.api.js';
import * as injected from './injected';
import { validateNodeVersion } from './validateNodeVersion';
let actualApp: null | typeof bundle.lib;
export const internal = bundle.lib;
// DEPRECATED: remove the next line in @actual-app/api v7
export * as methods from './methods';
export * from './methods';
export * as utils from './utils';
export async function init(config: InitConfig = {}) {
if (actualApp) {
return;
}
validateNodeVersion();
if (!globalThis.fetch) {
globalThis.fetch = (url: URL | RequestInfo, init?: RequestInit) => {
return import('node-fetch').then(({ default: fetch }) =>
fetch(url as unknown as FetchInfo, init as unknown as FetchInit),
) as unknown as Promise<Response>;
};
}
await bundle.init(config);
actualApp = bundle.lib;
injected.override(bundle.lib.send);
return bundle.lib;
}
export async function shutdown() {
if (actualApp) {
await actualApp.send('sync');
await actualApp.send('close-budget');
actualApp = null;
}
}

View File

@@ -0,0 +1,24 @@
module.exports = {
moduleFileExtensions: [
'testing.js',
'testing.ts',
'api.js',
'api.ts',
'api.tsx',
'electron.js',
'electron.ts',
'mjs',
'js',
'ts',
'tsx',
'json',
],
testEnvironment: 'node',
testPathIgnorePatterns: ['/node_modules/'],
watchPathIgnorePatterns: ['<rootDir>/mocks/budgets/'],
setupFilesAfterEnv: ['<rootDir>/../loot-core/src/mocks/setup.ts'],
transformIgnorePatterns: ['/node_modules/'],
transform: {
'^.+\\.(t|j)sx?$': '@swc/jest',
},
};

View File

@@ -1,151 +0,0 @@
import * as injected from './injected';
export { default as q } from './app/query';
function send(name, args) {
return injected.send(name, args);
}
export async function runImport(name, func) {
await send('api/start-import', { budgetName: name });
try {
await func();
} catch (e) {
await send('api/abort-import');
throw e;
}
await send('api/finish-import');
}
export async function loadBudget(budgetId) {
return send('api/load-budget', { id: budgetId });
}
export async function downloadBudget(syncId, { password } = {}) {
return send('api/download-budget', { syncId, password });
}
export async function batchBudgetUpdates(func) {
await send('api/batch-budget-start');
try {
await func();
} finally {
await send('api/batch-budget-end');
}
}
export function runQuery(query) {
return send('api/query', { query: query.serialize() });
}
export function getBudgetMonths() {
return send('api/budget-months');
}
export function getBudgetMonth(month) {
return send('api/budget-month', { month });
}
export function setBudgetAmount(month, categoryId, value) {
return send('api/budget-set-amount', { month, categoryId, amount: value });
}
export function setBudgetCarryover(month, categoryId, flag) {
return send('api/budget-set-carryover', { month, categoryId, flag });
}
export function addTransactions(accountId, transactions) {
return send('api/transactions-add', { accountId, transactions });
}
export function importTransactions(accountId, transactions) {
return send('api/transactions-import', { accountId, transactions });
}
export function getTransactions(accountId, startDate, endDate) {
return send('api/transactions-get', { accountId, startDate, endDate });
}
export function filterTransactions(accountId, text) {
return send('api/transactions-filter', { accountId, text });
}
export function updateTransaction(id, fields) {
return send('api/transaction-update', { id, fields });
}
export function deleteTransaction(id) {
return send('api/transaction-delete', { id });
}
export function getAccounts() {
return send('api/accounts-get');
}
export function createAccount(account, initialBalance) {
return send('api/account-create', { account, initialBalance });
}
export function updateAccount(id, fields) {
return send('api/account-update', { id, fields });
}
export function closeAccount(id, transferAccountId, transferCategoryId) {
return send('api/account-close', {
id,
transferAccountId,
transferCategoryId,
});
}
export function reopenAccount(id) {
return send('api/account-reopen', { id });
}
export function deleteAccount(id) {
return send('api/account-delete', { id });
}
export function createCategoryGroup(group) {
return send('api/category-group-create', { group });
}
export function updateCategoryGroup(id, fields) {
return send('api/category-group-update', { id, fields });
}
export function deleteCategoryGroup(id, transferCategoryId) {
return send('api/category-group-delete', { id, transferCategoryId });
}
export function getCategories() {
return send('api/categories-get', { grouped: false });
}
export function createCategory(category) {
return send('api/category-create', { category });
}
export function updateCategory(id, fields) {
return send('api/category-update', { id, fields });
}
export function deleteCategory(id, transferCategoryId) {
return send('api/category-delete', { id, transferCategoryId });
}
export function getPayees() {
return send('api/payees-get');
}
export function createPayee(payee) {
return send('api/payee-create', { payee });
}
export function updatePayee(id, fields) {
return send('api/payee-update', { id, fields });
}
export function deletePayee(id) {
return send('api/payee-delete', { id });
}

View File

@@ -0,0 +1,389 @@
// @ts-strict-ignore
import * as fs from 'fs/promises';
import * as path from 'path';
import * as api from './index';
const budgetName = 'test-budget';
beforeEach(async () => {
// we need real datetime if we are going to mix new timestamps with our mock data
global.restoreDateNow();
const budgetPath = path.join(__dirname, '/mocks/budgets/', budgetName);
await fs.rm(budgetPath, { force: true, recursive: true });
await createTestBudget('default-budget-template', budgetName);
await api.init({
dataDir: path.join(__dirname, '/mocks/budgets/'),
});
});
afterEach(async () => {
global.currentMonth = null;
await api.shutdown();
});
async function createTestBudget(templateName: string, name: string) {
const templatePath = path.join(
__dirname,
'/../loot-core/src/mocks/files',
templateName,
);
const budgetPath = path.join(__dirname, '/mocks/budgets/', name);
await fs.mkdir(budgetPath);
await fs.copyFile(
path.join(templatePath, 'metadata.json'),
path.join(budgetPath, 'metadata.json'),
);
await fs.copyFile(
path.join(templatePath, 'db.sqlite'),
path.join(budgetPath, 'db.sqlite'),
);
}
describe('API setup and teardown', () => {
// apis: loadBudget, getBudgetMonths
test('successfully loads budget', async () => {
await expect(api.loadBudget(budgetName)).resolves.toBeUndefined();
await expect(api.getBudgetMonths()).resolves.toMatchSnapshot();
});
});
describe('API CRUD operations', () => {
beforeEach(async () => {
// load test budget
await api.loadBudget(budgetName);
});
// apis: createCategoryGroup, updateCategoryGroup, deleteCategoryGroup
test('CategoryGroups: successfully update category groups', async () => {
const month = '2023-10';
global.currentMonth = month;
// create our test category group
const mainGroupId = await api.createCategoryGroup({
name: 'test-group',
});
let budgetMonth = await api.getBudgetMonth(month);
expect(budgetMonth.categoryGroups).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: mainGroupId,
}),
]),
);
// update group
await api.updateCategoryGroup(mainGroupId, {
name: 'update-tests',
});
budgetMonth = await api.getBudgetMonth(month);
expect(budgetMonth.categoryGroups).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: mainGroupId,
}),
]),
);
// delete group
await api.deleteCategoryGroup(mainGroupId);
budgetMonth = await api.getBudgetMonth(month);
expect(budgetMonth.categoryGroups).toEqual(
expect.arrayContaining([
expect.not.objectContaining({
id: mainGroupId,
}),
]),
);
});
// apis: createCategory, getCategories, updateCategory, deleteCategory
test('Categories: successfully update categories', async () => {
const month = '2023-10';
global.currentMonth = month;
// create our test category group
const mainGroupId = await api.createCategoryGroup({
name: 'test-group',
});
const secondaryGroupId = await api.createCategoryGroup({
name: 'test-secondary-group',
});
const categoryId = await api.createCategory({
name: 'test-budget',
group_id: mainGroupId,
});
const categoryIdHidden = await api.createCategory({
name: 'test-budget-hidden',
group_id: mainGroupId,
hidden: true,
});
let categories = await api.getCategories();
expect(categories).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: categoryId,
name: 'test-budget',
hidden: false,
group_id: mainGroupId,
}),
expect.objectContaining({
id: categoryIdHidden,
name: 'test-budget-hidden',
hidden: true,
group_id: mainGroupId,
}),
]),
);
// update/move category
await api.updateCategory(categoryId, {
name: 'updated-budget',
group_id: secondaryGroupId,
});
await api.updateCategory(categoryIdHidden, {
name: 'updated-budget-hidden',
group_id: secondaryGroupId,
hidden: false,
});
categories = await api.getCategories();
expect(categories).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: categoryId,
name: 'updated-budget',
hidden: false,
group_id: secondaryGroupId,
}),
expect.objectContaining({
id: categoryIdHidden,
name: 'updated-budget-hidden',
hidden: false,
group_id: secondaryGroupId,
}),
]),
);
// delete categories
await api.deleteCategory(categoryId);
expect(categories).toEqual(
expect.arrayContaining([
expect.not.objectContaining({
id: categoryId,
}),
]),
);
});
// apis: setBudgetAmount, setBudgetCarryover, getBudgetMonth
test('Budgets: successfully update budgets', async () => {
const month = '2023-10';
global.currentMonth = month;
// create some new categories to test with
const groupId = await api.createCategoryGroup({
name: 'tests',
});
const categoryId = await api.createCategory({
name: 'test-budget',
group_id: groupId,
});
await api.setBudgetAmount(month, categoryId, 100);
await api.setBudgetCarryover(month, categoryId, true);
const budgetMonth = await api.getBudgetMonth(month);
expect(budgetMonth.categoryGroups).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: groupId,
categories: expect.arrayContaining([
expect.objectContaining({
id: categoryId,
budgeted: 100,
carryover: true,
}),
]),
}),
]),
);
});
//apis: createAccount, getAccounts, updateAccount, closeAccount, deleteAccount, reopenAccount
test('Accounts: successfully complete account operators', async () => {
const accountId1 = await api.createAccount(
{ name: 'test-account1', offbudget: true },
1000,
);
const accountId2 = await api.createAccount({ name: 'test-account2' }, 0);
let accounts = await api.getAccounts();
// accounts successfully created
expect(accounts).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: accountId1,
name: 'test-account1',
offbudget: true,
}),
expect.objectContaining({ id: accountId2, name: 'test-account2' }),
]),
);
await api.updateAccount(accountId1, { offbudget: false });
await api.closeAccount(accountId1, accountId2, null);
await api.deleteAccount(accountId2);
// accounts successfully updated, and one of them deleted
accounts = await api.getAccounts();
expect(accounts).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: accountId1,
name: 'test-account1',
closed: true,
offbudget: false,
}),
expect.not.objectContaining({ id: accountId2 }),
]),
);
await api.reopenAccount(accountId1);
// the non-deleted account is reopened
accounts = await api.getAccounts();
expect(accounts).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: accountId1,
name: 'test-account1',
closed: false,
}),
]),
);
});
// apis: createPayee, getPayees, updatePayee, deletePayee
test('Payees: successfully update payees', async () => {
const payeeId1 = await api.createPayee({ name: 'test-payee1' });
const payeeId2 = await api.createPayee({ name: 'test-payee2' });
let payees = await api.getPayees();
// payees successfully created
expect(payees).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: payeeId1,
name: 'test-payee1',
}),
expect.objectContaining({
id: payeeId2,
name: 'test-payee2',
}),
]),
);
await api.updatePayee(payeeId1, { name: 'test-updated-payee' });
await api.deletePayee(payeeId2);
// confirm update and delete were successful
payees = await api.getPayees();
expect(payees).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: payeeId1,
name: 'test-updated-payee',
}),
expect.not.objectContaining({
name: 'test-payee1',
}),
expect.not.objectContaining({
id: payeeId2,
}),
]),
);
});
// apis: addTransactions, getTransactions, importTransactions, updateTransaction, deleteTransaction
test('Transactions: successfully update transactions', async () => {
const accountId = await api.createAccount({ name: 'test-account' }, 0);
let newTransaction = [
{ date: '2023-11-03', imported_id: '11', amount: 100 },
{ date: '2023-11-03', imported_id: '11', amount: 100 },
];
const addResult = await api.addTransactions(accountId, newTransaction, {
learnCategories: true,
runTransfers: true,
});
expect(addResult).toBe('ok');
// confirm added transactions exist
let transactions = await api.getTransactions(
accountId,
'2023-11-01',
'2023-11-30',
);
expect(transactions).toEqual(
expect.arrayContaining(
newTransaction.map(trans => expect.objectContaining(trans)),
),
);
expect(transactions).toHaveLength(2);
newTransaction = [
{ date: '2023-12-03', imported_id: '11', amount: 100 },
{ date: '2023-12-03', imported_id: '22', amount: 200 },
];
const reconciled = await api.importTransactions(accountId, newTransaction);
// Expect it to reconcile and to have updated one of the previous transactions
expect(reconciled.added).toHaveLength(1);
expect(reconciled.updated).toHaveLength(1);
// confirm imported transactions exist
transactions = await api.getTransactions(
accountId,
'2023-12-01',
'2023-12-31',
);
expect(transactions).toEqual(
expect.arrayContaining(
newTransaction.map(trans => expect.objectContaining(trans)),
),
);
expect(transactions).toHaveLength(2);
const idToUpdate = reconciled.added[0];
const idToDelete = reconciled.updated[0];
await api.updateTransaction(idToUpdate, { amount: 500 });
await api.deleteTransaction(idToDelete);
// confirm updates and deletions work
transactions = await api.getTransactions(
accountId,
'2023-12-01',
'2023-12-31',
);
expect(transactions).toEqual(
expect.arrayContaining([
expect.objectContaining({ id: idToUpdate, amount: 500 }),
expect.not.objectContaining({ id: idToDelete }),
]),
);
expect(transactions).toHaveLength(1);
});
});

166
packages/api/methods.ts Normal file
View File

@@ -0,0 +1,166 @@
// @ts-strict-ignore
import type { Handlers } from 'loot-core/src/types/handlers';
import * as injected from './injected';
export { q } from './app/query';
function send<K extends keyof Handlers, T extends Handlers[K]>(
name: K,
args?: Parameters<T>[0],
): Promise<Awaited<ReturnType<T>>> {
return injected.send(name, args);
}
export async function runImport(name, func) {
await send('api/start-import', { budgetName: name });
try {
await func();
} catch (e) {
await send('api/abort-import');
throw e;
}
await send('api/finish-import');
}
export async function loadBudget(budgetId) {
return send('api/load-budget', { id: budgetId });
}
export async function downloadBudget(syncId, { password }: { password? } = {}) {
return send('api/download-budget', { syncId, password });
}
export async function sync() {
return send('api/sync');
}
export async function batchBudgetUpdates(func) {
await send('api/batch-budget-start');
try {
await func();
} finally {
await send('api/batch-budget-end');
}
}
export function runQuery(query) {
return send('api/query', { query: query.serialize() });
}
export function getBudgetMonths() {
return send('api/budget-months');
}
export function getBudgetMonth(month) {
return send('api/budget-month', { month });
}
export function setBudgetAmount(month, categoryId, value) {
return send('api/budget-set-amount', { month, categoryId, amount: value });
}
export function setBudgetCarryover(month, categoryId, flag) {
return send('api/budget-set-carryover', { month, categoryId, flag });
}
export function addTransactions(
accountId,
transactions,
{ learnCategories = false, runTransfers = false } = {},
) {
return send('api/transactions-add', {
accountId,
transactions,
learnCategories,
runTransfers,
});
}
export function importTransactions(accountId, transactions) {
return send('api/transactions-import', { accountId, transactions });
}
export function getTransactions(accountId, startDate, endDate) {
return send('api/transactions-get', { accountId, startDate, endDate });
}
export function updateTransaction(id, fields) {
return send('api/transaction-update', { id, fields });
}
export function deleteTransaction(id) {
return send('api/transaction-delete', { id });
}
export function getAccounts() {
return send('api/accounts-get');
}
export function createAccount(account, initialBalance?) {
return send('api/account-create', { account, initialBalance });
}
export function updateAccount(id, fields) {
return send('api/account-update', { id, fields });
}
export function closeAccount(id, transferAccountId?, transferCategoryId?) {
return send('api/account-close', {
id,
transferAccountId,
transferCategoryId,
});
}
export function reopenAccount(id) {
return send('api/account-reopen', { id });
}
export function deleteAccount(id) {
return send('api/account-delete', { id });
}
export function createCategoryGroup(group) {
return send('api/category-group-create', { group });
}
export function updateCategoryGroup(id, fields) {
return send('api/category-group-update', { id, fields });
}
export function deleteCategoryGroup(id, transferCategoryId?) {
return send('api/category-group-delete', { id, transferCategoryId });
}
export function getCategories() {
return send('api/categories-get', { grouped: false });
}
export function createCategory(category) {
return send('api/category-create', { category });
}
export function updateCategory(id, fields) {
return send('api/category-update', { id, fields });
}
export function deleteCategory(id, transferCategoryId?) {
return send('api/category-delete', { id, transferCategoryId });
}
export function getPayees() {
return send('api/payees-get');
}
export function createPayee(payee) {
return send('api/payee-create', { payee });
}
export function updatePayee(id, fields) {
return send('api/payee-update', { id, fields });
}
export function deletePayee(id) {
return send('api/payee-delete', { id });
}

View File

View File

@@ -1,27 +1,38 @@
{
"name": "@actual-app/api",
"version": "6.0.0",
"version": "6.4.0",
"license": "MIT",
"description": "An API for Actual",
"engines": {
"node": ">=18.12.0"
},
"main": "dist/index.js",
"types": "dist/index.d.ts",
"types": "@types/index.d.ts",
"files": [
"dist",
"default-db.sqlite",
"migrations"
"dist"
],
"scripts": {
"lint": "eslint .",
"build:app": "yarn workspace loot-core build:api",
"build:node": "tsc --p tsconfig.dist.json",
"build": "yarn run build:app && yarn run build:node"
"build:node": "tsc --p tsconfig.dist.json && tsc-alias -p tsconfig.dist.json",
"build:migrations": "cp migrations/*.sql dist/migrations",
"build:default-db": "cp default-db.sqlite dist/",
"build": "yarn run clean && yarn run build:app && yarn run build:node && yarn run build:migrations && yarn run build:default-db",
"test": "yarn run build:app && jest -c jest.config.js",
"clean": "rm -rf dist @types"
},
"dependencies": {
"better-sqlite3": "^8.2.0",
"node-fetch": "^2.6.9",
"uuid": "3.3.2"
"better-sqlite3": "^9.2.2",
"compare-versions": "^6.1.0",
"node-fetch": "^3.3.2",
"uuid": "^9.0.0"
},
"devDependencies": {
"@swc/core": "^1.3.105",
"@swc/jest": "^0.2.31",
"@types/jest": "^27.5.0",
"@types/uuid": "^9.0.2",
"jest": "^27.0.0",
"tsc-alias": "^1.8.8",
"typescript": "^5.0.2"
}
}

View File

@@ -1,8 +0,0 @@
import * as api from './index';
async function run() {
let app = await api.init({ config: { dataDir: '/tmp' } });
await app.send('create-budget', { testMode: true });
}
run();

View File

@@ -1,11 +1,18 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"target": "es5",
// Using ES2021 because thats the newest version where
// the latest Node 16.x release supports all of the features
"target": "ES2021",
"module": "CommonJS",
"noEmit": false,
"declaration": true,
"outDir": "dist"
"outDir": "dist",
"declarationDir": "@types",
"paths": {
"loot-core/*": ["./@types/loot-core/*"],
}
},
"include": ["."]
"include": ["."],
"exclude": ["**/node_modules/*", "dist", "@types"]
}

View File

@@ -0,0 +1,16 @@
import { satisfies } from 'compare-versions';
import * as packageJson from './package.json';
export function validateNodeVersion() {
if (process?.versions?.node) {
const nodeVersion = process?.versions?.node;
const minimumNodeVersion = packageJson.engines.node;
if (!satisfies(nodeVersion, minimumNodeVersion)) {
throw new Error(
`@actual-app/api requires a node version ${minimumNodeVersion}. Found that you are using: ${nodeVersion}. Please upgrade to a higher version`,
);
}
}
}

2
packages/crdt/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
bin/protoc-gen-js
bin/protoc

27
packages/crdt/README.md Normal file
View File

@@ -0,0 +1,27 @@
# `@actual-app/crdt`
This package contains the core CRDT logic that enables Actuals syncing. It is shared between the client and server. We may or may not follow semver when updating this package; any usage of it outside Actual is undocumented and at your own risk.
## protobuf
We use [protobuf](https://developers.google.com/protocol-buffers/) to encode messages as binary data to send across the network.
### Generating protobuf
The protobuf is generated by using the [protoc](https://github.com/protocolbuffers/protobuf) compiler.
This can be installed by downloading one of the [pre-built binaries](https://github.com/protocolbuffers/protobuf/releases/) and placing it in your `$PATH`. The version used to build the current protobuf is [v3.20.1](https://github.com/protocolbuffers/protobuf/releases/tag/v3.20.1). Youll also need to [download the latest version of `protoc-gen-js`](https://github.com/protocolbuffers/protobuf-javascript/releases/latest). For convenience, you can put both of these binaries in `./bin`.
Once installed, the protobuf can be generated by running `./bin/generate-proto`.
However there is one very important thing to remember! The default output includes this near the top:
```
var global = (function() { return this || window || global || self || Function('return this')(); }).call(null);
```
This will not work with our CSP directives. You must manually modify this to this:
```
var global = globalThis;
```

View File

@@ -0,0 +1,21 @@
#!/bin/bash
cd "$(dirname "$(dirname "$0")")"
if ! [ -x "$(command -v protoc)" ]; then
echo 'Error: protoc is not installed. See the readme for installation instructions.' >&2
exit 1
fi
export PATH="$PWD/bin:$PATH"
protoc --plugin="protoc-gen-ts=../../node_modules/.bin/protoc-gen-ts" \
--ts_opt=esModuleInterop=true \
--ts_out="src/proto" \
--js_out=import_style=commonjs,binary:src/proto \
--proto_path=src/proto \
sync.proto
../../node_modules/.bin/prettier --write src/proto/*.d.ts
echo 'One more step! Find the `var global = ...` declaration in src/proto/sync_pb.js and change it to `var global = globalThis;`'

1
packages/crdt/index.ts Normal file
View File

@@ -0,0 +1 @@
export * from './src/main';

View File

@@ -0,0 +1,6 @@
module.exports = {
testEnvironment: 'node',
transform: {
'^.+\\.(t|j)sx?$': '@swc/jest',
},
};

View File

@@ -0,0 +1,31 @@
{
"name": "@actual-app/crdt",
"version": "2.1.0",
"license": "MIT",
"description": "CRDT layer of Actual",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
"build:node": "tsc --p tsconfig.dist.json",
"proto:generate": "./bin/generate-proto",
"build": "rm -rf dist && yarn run build:node && cp src/proto/sync_pb.d.ts dist/src/proto/",
"test": "jest -c jest.config.js"
},
"dependencies": {
"google-protobuf": "^3.12.0-rc.1",
"murmurhash": "^2.0.1",
"uuid": "^9.0.0"
},
"devDependencies": {
"@swc/core": "^1.3.105",
"@swc/jest": "^0.2.31",
"@types/jest": "^27.5.0",
"@types/uuid": "^9.0.2",
"jest": "^27.0.0",
"ts-protoc-gen": "^0.15.0",
"typescript": "^5.0.2"
}
}

View File

@@ -0,0 +1,13 @@
import * as merkle from './merkle';
export { merkle };
export {
getClock,
setClock,
makeClock,
makeClientId,
serializeClock,
deserializeClock,
type Clock,
Timestamp,
} from './timestamp';

View File

@@ -1,13 +1,17 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import * as merkle from './merkle';
import { Timestamp } from './timestamp';
function message(timestamp, hash) {
timestamp = Timestamp.parse(timestamp);
function message(timestampStr: string, hash: number) {
const timestamp = Timestamp.parse(timestampStr)!;
timestamp.hash = () => hash;
return { timestamp };
}
function insertMessages(trie, messages) {
function insertMessages(
trie: merkle.TrieNode,
messages: ReturnType<typeof message>[],
) {
messages.forEach(msg => {
trie = merkle.insert(trie, msg.timestamp);
});
@@ -17,19 +21,19 @@ function insertMessages(trie, messages) {
describe('merkle trie', () => {
test('adding an item works', () => {
let trie = merkle.insert(
{},
Timestamp.parse('2018-11-12T13:21:40.122Z-0000-0123456789ABCDEF'),
merkle.emptyTrie(),
Timestamp.parse('2018-11-12T13:21:40.122Z-0000-0123456789ABCDEF')!,
);
trie = merkle.insert(
trie,
Timestamp.parse('2018-11-13T13:21:40.122Z-0000-0123456789ABCDEF'),
Timestamp.parse('2018-11-13T13:21:40.122Z-0000-0123456789ABCDEF')!,
);
expect(trie).toMatchSnapshot();
});
test('diff returns the correct time difference', () => {
let trie1: { hash?: unknown } = {};
let trie2: { hash?: unknown } = {};
let trie1 = merkle.emptyTrie();
let trie2 = merkle.emptyTrie();
const messages = [
// First client messages
@@ -51,7 +55,7 @@ describe('merkle trie', () => {
trie2 = merkle.insert(trie2, messages[4].timestamp);
expect(trie2.hash).toBe(108);
expect(new Date(merkle.diff(trie1, trie2)).toISOString()).toBe(
expect(new Date(merkle.diff(trie1, trie2)!).toISOString()).toBe(
'2018-11-02T17:15:00.000Z',
);
@@ -65,17 +69,17 @@ describe('merkle trie', () => {
});
test('diffing works with empty tries', () => {
let trie1 = {};
let trie2 = merkle.insert(
{},
Timestamp.parse('2009-01-02T10:17:37.789Z-0000-0000testinguuid1'),
const trie1 = merkle.emptyTrie();
const trie2 = merkle.insert(
merkle.emptyTrie(),
Timestamp.parse('2009-01-02T10:17:37.789Z-0000-0000testinguuid1')!,
);
expect(merkle.diff(trie1, trie2)).toBe(0);
});
test('pruning works and keeps correct hashes', () => {
let messages = [
const messages = [
message('2018-11-01T01:00:00.000Z-0000-0123456789ABCDEF', 1000),
message('2018-11-01T01:09:00.000Z-0000-0123456789ABCDEF', 1100),
message('2018-11-01T01:18:00.000Z-0000-0123456789ABCDEF', 1200),
@@ -90,20 +94,20 @@ describe('merkle trie', () => {
message('2018-11-01T02:37:00.000Z-0000-0123456789ABCDEF', 2100),
];
let trie: { hash?: unknown } = {};
let trie = merkle.emptyTrie();
messages.forEach(msg => {
trie = merkle.insert(trie, msg.timestamp);
});
expect(trie.hash).toBe(2496);
expect(trie).toMatchSnapshot();
let pruned = merkle.prune(trie);
const pruned = merkle.prune(trie);
expect(pruned.hash).toBe(2496);
expect(pruned).toMatchSnapshot();
});
test('diffing differently shaped tries returns correct time', () => {
let messages = [
const messages = [
message('2018-11-01T01:00:00.000Z-0000-0123456789ABCDEF', 1000),
message('2018-11-01T01:09:00.000Z-0000-0123456789ABCDEF', 1100),
message('2018-11-01T01:18:00.000Z-0000-0123456789ABCDEF', 1200),
@@ -118,70 +122,70 @@ describe('merkle trie', () => {
message('2018-11-01T02:37:00.000Z-0000-0123456789ABCDEF', 2100),
];
let trie = insertMessages({}, messages);
const trie = insertMessages({}, messages);
// Case 0: It always returns a base time when comparing with an
// empty trie
expect(new Date(merkle.diff({}, trie)).toISOString()).toBe(
expect(new Date(merkle.diff(merkle.emptyTrie(), trie)!).toISOString()).toBe(
'1970-01-01T00:00:00.000Z',
);
expect(new Date(merkle.diff(trie, {})).toISOString()).toBe(
expect(new Date(merkle.diff(trie, merkle.emptyTrie())!).toISOString()).toBe(
'1970-01-01T00:00:00.000Z',
);
// Case 1: Add an older message that modifies the trie in such a
// way that it modifies the 1st out of 3 branches (so it will be
// pruned away)
let trie1 = insertMessages(trie, [
const trie1 = insertMessages(trie, [
message('2018-11-01T00:59:00.000Z-0000-0123456789ABCDEF', 900),
]);
// Normal comparision works
expect(new Date(merkle.diff(trie1, trie)).toISOString()).toBe(
// Normal comparison works
expect(new Date(merkle.diff(trie1, trie)!).toISOString()).toBe(
'2018-11-01T00:54:00.000Z',
);
// Comparing the pruned new trie is lossy, so it returns an even older time
expect(new Date(merkle.diff(merkle.prune(trie1), trie)).toISOString()).toBe(
'2018-11-01T00:45:00.000Z',
);
expect(
new Date(merkle.diff(merkle.prune(trie1), trie)!).toISOString(),
).toBe('2018-11-01T00:45:00.000Z');
// Comparing the pruned original trie is just as lossy
expect(new Date(merkle.diff(trie1, merkle.prune(trie))).toISOString()).toBe(
'2018-11-01T00:45:00.000Z',
);
expect(
new Date(merkle.diff(trie1, merkle.prune(trie))!).toISOString(),
).toBe('2018-11-01T00:45:00.000Z');
// Pruning both tries is just as lossy as well, since the changed
// key is pruned away in both cases and it won't find a changed
// key so it bails at the point
expect(
new Date(
merkle.diff(merkle.prune(trie1), merkle.prune(trie)),
merkle.diff(merkle.prune(trie1), merkle.prune(trie))!,
).toISOString(),
).toBe('2018-11-01T00:45:00.000Z');
// Case 2: Add two messages similar to the above case, but the
// second message modifies the 2nd key at the same level as the
// first message modifiying the 1st key
let trie2 = insertMessages(trie, [
// first message modifying the 1st key
const trie2 = insertMessages(trie, [
message('2018-11-01T00:59:00.000Z-0000-0123456789ABCDEF', 900),
message('2018-11-01T01:15:00.000Z-0000-0123456789ABCDEF', 1422),
]);
// Normal comparision works
expect(new Date(merkle.diff(trie2, trie)).toISOString()).toBe(
// Normal comparison works
expect(new Date(merkle.diff(trie2, trie)!).toISOString()).toBe(
'2018-11-01T00:54:00.000Z',
);
// Same as case 1
expect(new Date(merkle.diff(merkle.prune(trie2), trie)).toISOString()).toBe(
'2018-11-01T00:45:00.000Z',
);
expect(
new Date(merkle.diff(merkle.prune(trie2), trie)!).toISOString(),
).toBe('2018-11-01T00:45:00.000Z');
// Same as case 1
expect(new Date(merkle.diff(trie2, merkle.prune(trie))).toISOString()).toBe(
'2018-11-01T00:45:00.000Z',
);
expect(
new Date(merkle.diff(trie2, merkle.prune(trie))!).toISOString(),
).toBe('2018-11-01T00:45:00.000Z');
// Pruning both tries is very lossy and this ends up returning a
// time that only covers the second message. Syncing will need
@@ -190,7 +194,7 @@ describe('merkle trie', () => {
// ignores the first message.
expect(
new Date(
merkle.diff(merkle.prune(trie2), merkle.prune(trie)),
merkle.diff(merkle.prune(trie2), merkle.prune(trie))!,
).toISOString(),
).toBe('2018-11-01T01:12:00.000Z');
});

View File

@@ -0,0 +1,180 @@
// TODO: Ok, several problems:
//
// * If nothing matches between two merkle trees, we should fallback
// * to the last window instead the front one (use 0 instead of the
// * key)
//
// * Need to check to make sure if account exists when handling
// * transaction changes in syncing
import { Timestamp } from './timestamp';
/**
* Represents a node within a trinary radix trie.
*/
export type TrieNode = {
'0'?: TrieNode;
'1'?: TrieNode;
'2'?: TrieNode;
hash?: number;
};
type NumberTrieNodeKey = keyof Omit<TrieNode, 'hash'>;
export function emptyTrie(): TrieNode {
return { hash: 0 };
}
function isNumberTrieNodeKey(input: string): input is NumberTrieNodeKey {
return ['0', '1', '2'].includes(input);
}
export function getKeys(trie: TrieNode): NumberTrieNodeKey[] {
return Object.keys(trie).filter(isNumberTrieNodeKey);
}
export function keyToTimestamp(key: string): number {
// 16 is the length of the base 3 value of the current time in
// minutes. Ensure it's padded to create the full value
const fullkey = key + '0'.repeat(16 - key.length);
// Parse the base 3 representation
return parseInt(fullkey, 3) * 1000 * 60;
}
/**
* Mutates `trie` to insert a node at `timestamp`
*/
export function insert(trie: TrieNode, timestamp: Timestamp) {
const hash = timestamp.hash();
const key = Number(Math.floor(timestamp.millis() / 1000 / 60)).toString(3);
trie = Object.assign({}, trie, { hash: (trie.hash || 0) ^ hash });
return insertKey(trie, key, hash);
}
function insertKey(trie: TrieNode, key: string, hash: number): TrieNode {
if (key.length === 0) {
return trie;
}
const c = key[0];
const t = isNumberTrieNodeKey(c) ? trie[c] : undefined;
const n = t || {};
return Object.assign({}, trie, {
[c]: Object.assign({}, n, insertKey(n, key.slice(1), hash), {
hash: (n.hash || 0) ^ hash,
}),
});
}
export function build(timestamps: Timestamp[]) {
const trie = emptyTrie();
for (const timestamp of timestamps) {
insert(trie, timestamp);
}
return trie;
}
export function diff(trie1: TrieNode, trie2: TrieNode): number | null {
if (trie1.hash === trie2.hash) {
return null;
}
let node1 = trie1;
let node2 = trie2;
let k = '';
// This loop will eventually stop when it traverses down to find
// where the hashes differ, or otherwise when there are no leaves
// left (this shouldn't happen, if that's the case the hash check at
// the top of this function should pass)
while (1) {
const keyset = new Set([...getKeys(node1), ...getKeys(node2)]);
const keys = [...keyset.values()];
keys.sort();
let diffkey: null | '0' | '1' | '2' = null;
// Traverse down the trie through keys that aren't the same. We
// traverse down the keys in order. Stop in two cases: either one
// of the nodes doesn't have the key, or a different key isn't
// found. For the former case, we have to that because pruning is
// lossy. We don't know if we've pruned off a changed key so we
// can't traverse down anymore. For the latter case, it means two
// things: either we've hit the bottom of the tree, or the changed
// key has been pruned off. In the latter case we have a "partial"
// key and will fill the rest with 0s. Note that if multiple older
// messages were added into one trie, it's possible we will
// generate a time that only encompasses *some* of the those
// messages. Pruning is lossy, and we traverse down the left-most
// changed time that we know of, because of pruning it might take
// multiple passes to sync up a trie.
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const next1 = node1[key];
const next2 = node2[key];
if (!next1 || !next2) {
break;
}
if (next1.hash !== next2.hash) {
diffkey = key;
break;
}
}
if (!diffkey) {
return keyToTimestamp(k);
}
k += diffkey;
node1 = node1[diffkey] || emptyTrie();
node2 = node2[diffkey] || emptyTrie();
}
return null;
}
export function prune(trie: TrieNode, n = 2): TrieNode {
// Do nothing if empty
if (!trie.hash) {
return trie;
}
const keys = getKeys(trie);
keys.sort();
const next: TrieNode = { hash: trie.hash };
// Prune child nodes.
for (const k of keys.slice(-n)) {
const node = trie[k];
if (!node) {
throw new Error(`TrieNode for key ${k} could not be found`);
}
next[k] = prune(node, n);
}
return next;
}
export function debug(trie: TrieNode, k = '', indent = 0): string {
const str =
' '.repeat(indent) +
(k !== '' ? `k: ${k} ` : '') +
`hash: ${trie.hash || '(empty)'}\n`;
return (
str +
getKeys(trie)
.map(key => {
const node = trie[key];
if (!node) return '';
return debug(node, key, indent + 2);
})
.join('')
);
}

View File

@@ -1,8 +1,9 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { Timestamp } from './timestamp';
describe('Timestamp', function () {
let now = 0;
let prevNow;
let prevNow: typeof Date.now;
beforeEach(function () {
prevNow = Date.now;
@@ -16,16 +17,18 @@ describe('Timestamp', function () {
describe('comparison', function () {
it('should be in order', function () {
expect(Timestamp.zero()).toBe(Timestamp.zero());
expect(Timestamp.max() > Timestamp.zero()).toBeTruthy();
expect(Timestamp.send() > Timestamp.zero()).toBeTruthy();
expect(Timestamp.send() < Timestamp.max()).toBeTruthy();
const sendTimestamp = Timestamp.send();
expect(Timestamp.zero).toBe(Timestamp.zero);
expect(Timestamp.max > Timestamp.zero).toBeTruthy();
expect(sendTimestamp && sendTimestamp > Timestamp.zero).toBeTruthy();
expect(sendTimestamp && sendTimestamp < Timestamp.max).toBeTruthy();
});
});
describe('parsing', function () {
it('should not parse', function () {
let invalidInputs = [
const invalidInputs = [
null,
undefined,
{},
@@ -41,19 +44,19 @@ describe('Timestamp', function () {
'9999-12-31T23:59:59.999Z-10000-FFFFFFFFFFFFFFFF',
'9999-12-31T23:59:59.999Z-FFFF-10000000000000000',
];
for (let invalidInput of invalidInputs) {
expect(Timestamp.parse(invalidInput)).toBe(null);
for (const invalidInput of invalidInputs) {
expect(Timestamp.parse(invalidInput as string)).toBe(null);
}
});
it('should parse', function () {
let validInputs = [
const validInputs = [
'1970-01-01T00:00:00.000Z-0000-0000000000000000',
'2015-04-24T22:23:42.123Z-1000-0123456789ABCDEF',
'9999-12-31T23:59:59.999Z-FFFF-FFFFFFFFFFFFFFFF',
];
for (let validInput of validInputs) {
let parsed = Timestamp.parse(validInput);
for (const validInput of validInputs) {
const parsed = Timestamp.parse(validInput)!;
expect(typeof parsed).toBe('object');
expect(parsed.millis() >= 0).toBeTruthy();
expect(parsed.millis() < 253402300800000).toBeTruthy();
@@ -133,7 +136,7 @@ describe('Timestamp', function () {
now = 52;
expect(
Timestamp.recv(
Timestamp.parse('1970-01-01T00:00:00.051Z-0000-0000000000000002'),
Timestamp.parse('1970-01-01T00:00:00.051Z-0000-0000000000000002')!,
),
).toEqual(
Timestamp.parse('1970-01-01T00:00:00.052Z-0000-0000000000000001'),
@@ -141,7 +144,7 @@ describe('Timestamp', function () {
now = 54;
expect(
Timestamp.recv(
Timestamp.parse('1970-01-01T00:00:00.053Z-0000-0000000000000002'),
Timestamp.parse('1970-01-01T00:00:00.053Z-0000-0000000000000002')!,
),
).toEqual(
Timestamp.parse('1970-01-01T00:00:00.054Z-0000-0000000000000001'),
@@ -149,7 +152,7 @@ describe('Timestamp', function () {
now = 56;
expect(
Timestamp.recv(
Timestamp.parse('1970-01-01T00:00:00.055Z-0000-0000000000000002'),
Timestamp.parse('1970-01-01T00:00:00.055Z-0000-0000000000000002')!,
),
).toEqual(
Timestamp.parse('1970-01-01T00:00:00.056Z-0000-0000000000000001'),
@@ -160,7 +163,7 @@ describe('Timestamp', function () {
now = 61;
expect(
Timestamp.recv(
Timestamp.parse('1970-01-01T00:00:00.062Z-0000-0000000000000002'),
Timestamp.parse('1970-01-01T00:00:00.062Z-0000-0000000000000002')!,
),
).toEqual(
Timestamp.parse('1970-01-01T00:00:00.062Z-0001-0000000000000001'),
@@ -168,7 +171,7 @@ describe('Timestamp', function () {
now = 62;
expect(
Timestamp.recv(
Timestamp.parse('1970-01-01T00:00:00.062Z-0001-0000000000000002'),
Timestamp.parse('1970-01-01T00:00:00.062Z-0001-0000000000000002')!,
),
).toEqual(
Timestamp.parse('1970-01-01T00:00:00.062Z-0002-0000000000000001'),
@@ -176,7 +179,7 @@ describe('Timestamp', function () {
now = 62;
expect(
Timestamp.recv(
Timestamp.parse('1970-01-01T00:00:00.062Z-0002-0000000000000002'),
Timestamp.parse('1970-01-01T00:00:00.062Z-0002-0000000000000002')!,
),
).toEqual(
Timestamp.parse('1970-01-01T00:00:00.062Z-0003-0000000000000001'),
@@ -184,7 +187,7 @@ describe('Timestamp', function () {
now = 63;
expect(
Timestamp.recv(
Timestamp.parse('1970-01-01T00:00:00.062Z-0004-0000000000000002'),
Timestamp.parse('1970-01-01T00:00:00.062Z-0004-0000000000000002')!,
),
).toEqual(
Timestamp.parse('1970-01-01T00:00:00.063Z-0000-0000000000000001'),
@@ -195,7 +198,7 @@ describe('Timestamp', function () {
now = 73;
expect(
Timestamp.recv(
Timestamp.parse('1970-01-01T00:00:00.071Z-0000-0000000000000002'),
Timestamp.parse('1970-01-01T00:00:00.071Z-0000-0000000000000002')!,
),
).toEqual(
Timestamp.parse('1970-01-01T00:00:00.073Z-0000-0000000000000001'),
@@ -203,7 +206,7 @@ describe('Timestamp', function () {
now = 73;
expect(
Timestamp.recv(
Timestamp.parse('1970-01-01T00:00:00.072Z-0000-0000000000000002'),
Timestamp.parse('1970-01-01T00:00:00.072Z-0000-0000000000000002')!,
),
).toEqual(
Timestamp.parse('1970-01-01T00:00:00.073Z-0001-0000000000000001'),
@@ -211,7 +214,7 @@ describe('Timestamp', function () {
now = 74;
expect(
Timestamp.recv(
Timestamp.parse('1970-01-01T00:00:00.073Z-0000-0000000000000002'),
Timestamp.parse('1970-01-01T00:00:00.073Z-0000-0000000000000002')!,
),
).toEqual(
Timestamp.parse('1970-01-01T00:00:00.074Z-0000-0000000000000001'),
@@ -222,7 +225,7 @@ describe('Timestamp', function () {
now = 81;
expect(
Timestamp.recv(
Timestamp.parse('1970-01-01T00:00:00.083Z-0000-0000000000000002'),
Timestamp.parse('1970-01-01T00:00:00.083Z-0000-0000000000000002')!,
),
).toEqual(
Timestamp.parse('1970-01-01T00:00:00.083Z-0001-0000000000000001'),
@@ -230,7 +233,7 @@ describe('Timestamp', function () {
now = 82;
expect(
Timestamp.recv(
Timestamp.parse('1970-01-01T00:00:00.083Z-0001-0000000000000002'),
Timestamp.parse('1970-01-01T00:00:00.083Z-0001-0000000000000002')!,
),
).toEqual(
Timestamp.parse('1970-01-01T00:00:00.083Z-0002-0000000000000001'),
@@ -238,7 +241,7 @@ describe('Timestamp', function () {
now = 83;
expect(
Timestamp.recv(
Timestamp.parse('1970-01-01T00:00:00.083Z-0002-0000000000000002'),
Timestamp.parse('1970-01-01T00:00:00.083Z-0002-0000000000000002')!,
),
).toEqual(
Timestamp.parse('1970-01-01T00:00:00.083Z-0003-0000000000000001'),
@@ -246,7 +249,7 @@ describe('Timestamp', function () {
now = 84;
expect(
Timestamp.recv(
Timestamp.parse('1970-01-01T00:00:00.083Z-0003-0000000000000002'),
Timestamp.parse('1970-01-01T00:00:00.083Z-0003-0000000000000002')!,
),
).toEqual(
Timestamp.parse('1970-01-01T00:00:00.084Z-0000-0000000000000001'),
@@ -256,7 +259,7 @@ describe('Timestamp', function () {
now = 93;
expect(
Timestamp.recv(
Timestamp.parse('1970-01-01T00:00:00.091Z-0000-0000000000000002'),
Timestamp.parse('1970-01-01T00:00:00.091Z-0000-0000000000000002')!,
),
).toEqual(
Timestamp.parse('1970-01-01T00:00:00.093Z-0000-0000000000000001'),
@@ -264,7 +267,7 @@ describe('Timestamp', function () {
now = 92;
expect(
Timestamp.recv(
Timestamp.parse('1970-01-01T00:00:00.092Z-0000-0000000000000002'),
Timestamp.parse('1970-01-01T00:00:00.092Z-0000-0000000000000002')!,
),
).toEqual(
Timestamp.parse('1970-01-01T00:00:00.093Z-0001-0000000000000001'),
@@ -272,7 +275,7 @@ describe('Timestamp', function () {
now = 91;
expect(
Timestamp.recv(
Timestamp.parse('1970-01-01T00:00:00.093Z-0000-0000000000000002'),
Timestamp.parse('1970-01-01T00:00:00.093Z-0000-0000000000000002')!,
),
).toEqual(
Timestamp.parse('1970-01-01T00:00:00.093Z-0002-0000000000000001'),
@@ -282,7 +285,7 @@ describe('Timestamp', function () {
now = 101;
expect(
Timestamp.recv(
Timestamp.parse('1970-01-01T00:00:00.103Z-0000-0000000000000002'),
Timestamp.parse('1970-01-01T00:00:00.103Z-0000-0000000000000002')!,
),
).toEqual(
Timestamp.parse('1970-01-01T00:00:00.103Z-0001-0000000000000001'),
@@ -290,7 +293,7 @@ describe('Timestamp', function () {
now = 102;
expect(
Timestamp.recv(
Timestamp.parse('1970-01-01T00:00:00.102Z-0000-0000000000000002'),
Timestamp.parse('1970-01-01T00:00:00.102Z-0000-0000000000000002')!,
),
).toEqual(
Timestamp.parse('1970-01-01T00:00:00.103Z-0002-0000000000000001'),
@@ -298,7 +301,7 @@ describe('Timestamp', function () {
now = 103;
expect(
Timestamp.recv(
Timestamp.parse('1970-01-01T00:00:00.101Z-0000-0000000000000002'),
Timestamp.parse('1970-01-01T00:00:00.101Z-0000-0000000000000002')!,
),
).toEqual(
Timestamp.parse('1970-01-01T00:00:00.103Z-0003-0000000000000001'),
@@ -316,7 +319,7 @@ describe('Timestamp', function () {
it('should fail with clock drift', function () {
expect(function () {
Timestamp.recv(
Timestamp.parse('1980-01-01T00:00:00.101Z-0000-0000000000000002'),
Timestamp.parse('1980-01-01T00:00:00.101Z-0000-0000000000000002')!,
);
}).toThrow(Timestamp.ClockDriftError);
});

View File

@@ -0,0 +1,358 @@
import murmurhash from 'murmurhash';
import { v4 as uuidv4 } from 'uuid';
import { TrieNode } from './merkle';
/**
* Hybrid Unique Logical Clock (HULC) timestamp generator
*
* Globally-unique, monotonic timestamps are generated from the
* combination of the unreliable system time, a counter, and an
* identifier for the current node (instance, machine, process, etc.).
* These timestamps can accommodate clock stuttering (duplicate values),
* regression, and node differences within the configured maximum drift.
*
* In order to generate timestamps locally or for transmission to another
* node, use the send() method. For global causality, timestamps must
* be included in each message processed by the system. Whenever a
* message is received, its timestamp must be passed to the recv()
* method.
*
* Timestamps serialize into a 46-character collatable string
* example: 2015-04-24T22:23:42.123Z-1000-0123456789ABCDEF
* example: 2015-04-24T22:23:42.123Z-1000-A219E7A71CC18912
*
* The 64-bit hybrid clock is based on the HLC specification,
* http://www.cse.buffalo.edu/tech-reports/2014-04.pdf
*/
export type Clock = {
timestamp: MutableTimestamp;
merkle: TrieNode;
};
// A mutable global clock
let clock: Clock;
export function setClock(clock_: Clock): void {
clock = clock_;
}
export function getClock(): Clock {
return clock;
}
export function makeClock(timestamp: Timestamp, merkle: TrieNode = {}) {
return { timestamp: MutableTimestamp.from(timestamp), merkle };
}
export function serializeClock(clock: Clock): string {
return JSON.stringify({
timestamp: clock.timestamp.toString(),
merkle: clock.merkle,
});
}
export function deserializeClock(clock: string): Clock {
let data;
try {
data = JSON.parse(clock);
} catch (e) {
data = {
timestamp: '1970-01-01T00:00:00.000Z-0000-' + makeClientId(),
merkle: {},
};
}
const ts = Timestamp.parse(data.timestamp);
if (!ts) {
throw new Timestamp.InvalidError(data.timestamp);
}
return {
timestamp: MutableTimestamp.from(ts),
merkle: data.merkle,
};
}
export function makeClientId() {
return uuidv4().replace(/-/g, '').slice(-16);
}
const config = {
// Allow 5 minutes of clock drift
maxDrift: 5 * 60 * 1000,
};
const MAX_COUNTER = parseInt('0xFFFF');
const MAX_NODE_LENGTH = 16;
/**
* timestamp instance class
*/
export class Timestamp {
_state: { millis: number; counter: number; node: string };
constructor(millis: number, counter: number, node: string) {
this._state = {
millis,
counter,
node,
};
}
valueOf() {
return this.toString();
}
toString() {
return [
new Date(this.millis()).toISOString(),
('0000' + this.counter().toString(16).toUpperCase()).slice(-4),
('0000000000000000' + this.node()).slice(-16),
].join('-');
}
millis() {
return this._state.millis;
}
counter() {
return this._state.counter;
}
node() {
return this._state.node;
}
hash() {
return murmurhash.v3(this.toString());
}
// Timestamp generator initialization
// * sets the node ID to an arbitrary value
// * useful for mocking/unit testing
static init(options: { maxDrift?: number; node?: string } = {}) {
if (options.maxDrift) {
config.maxDrift = options.maxDrift;
}
setClock(
makeClock(
new Timestamp(
0,
0,
options.node
? ('0000000000000000' + options.node).toString().slice(-16)
: '',
),
),
);
}
/**
* maximum timestamp
*/
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
static max = Timestamp.parse(
'9999-12-31T23:59:59.999Z-FFFF-FFFFFFFFFFFFFFFF',
)!;
/**
* timestamp parsing
* converts a fixed-length string timestamp to the structured value
*/
static parse(timestamp: string | Timestamp): Timestamp | null {
if (timestamp instanceof Timestamp) {
return timestamp;
}
if (typeof timestamp === 'string') {
const parts = timestamp.split('-');
if (parts && parts.length === 5) {
const millis = Date.parse(parts.slice(0, 3).join('-')).valueOf();
const counter = parseInt(parts[3], 16);
const node = parts[4];
if (
!isNaN(millis) &&
millis >= 0 &&
!isNaN(counter) &&
counter <= MAX_COUNTER &&
typeof node === 'string' &&
node.length <= MAX_NODE_LENGTH
) {
return new Timestamp(millis, counter, node);
}
}
}
return null;
}
/**
* Timestamp send. Generates a unique, monotonic timestamp suitable
* for transmission to another system in string format
*/
static send(): Timestamp | null {
if (!clock) {
return null;
}
// retrieve the local wall time
const phys = Date.now();
// unpack the clock.timestamp logical time and counter
const lOld = clock.timestamp.millis();
const cOld = clock.timestamp.counter();
// calculate the next logical time and counter
// * ensure that the logical time never goes backward
// * increment the counter if phys time does not advance
const lNew = Math.max(lOld, phys);
const cNew = lOld === lNew ? cOld + 1 : 0;
// check the result for drift and counter overflow
if (lNew - phys > config.maxDrift) {
throw new Timestamp.ClockDriftError(lNew, phys, config.maxDrift);
}
if (cNew > MAX_COUNTER) {
throw new Timestamp.OverflowError();
}
// repack the logical time/counter
clock.timestamp.setMillis(lNew);
clock.timestamp.setCounter(cNew);
return new Timestamp(
clock.timestamp.millis(),
clock.timestamp.counter(),
clock.timestamp.node(),
);
}
// Timestamp receive. Parses and merges a timestamp from a remote
// system with the local timeglobal uniqueness and monotonicity are
// preserved
static recv(msg: Timestamp): Timestamp | null {
if (!clock) {
return null;
}
// retrieve the local wall time
const phys = Date.now();
// unpack the message wall time/counter
const lMsg = msg.millis();
const cMsg = msg.counter();
// assert the node id and remote clock drift
// if (msg.node() === clock.timestamp.node()) {
// throw new Timestamp.DuplicateNodeError(clock.timestamp.node());
// }
if (lMsg - phys > config.maxDrift) {
throw new Timestamp.ClockDriftError();
}
// unpack the clock.timestamp logical time and counter
const lOld = clock.timestamp.millis();
const cOld = clock.timestamp.counter();
// calculate the next logical time and counter
// . ensure that the logical time never goes backward
// . if all logical clocks are equal, increment the max counter
// . if max = old > message, increment local counter
// . if max = messsage > old, increment message counter
// . otherwise, clocks are monotonic, reset counter
const lNew = Math.max(Math.max(lOld, phys), lMsg);
const cNew =
lNew === lOld && lNew === lMsg
? Math.max(cOld, cMsg) + 1
: lNew === lOld
? cOld + 1
: lNew === lMsg
? cMsg + 1
: 0;
// check the result for drift and counter overflow
if (lNew - phys > config.maxDrift) {
throw new Timestamp.ClockDriftError();
}
if (cNew > MAX_COUNTER) {
throw new Timestamp.OverflowError();
}
// repack the logical time/counter
clock.timestamp.setMillis(lNew);
clock.timestamp.setCounter(cNew);
return new Timestamp(
clock.timestamp.millis(),
clock.timestamp.counter(),
clock.timestamp.node(),
);
}
/**
* zero/minimum timestamp
*/
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
static zero = Timestamp.parse(
'1970-01-01T00:00:00.000Z-0000-0000000000000000',
)!;
static since = (isoString: string) => isoString + '-0000-0000000000000000';
/**
* error classes
*/
static DuplicateNodeError = class DuplicateNodeError extends Error {
constructor(node: string) {
super('duplicate node identifier ' + node);
this.name = 'DuplicateNodeError';
}
};
static ClockDriftError = class ClockDriftError extends Error {
constructor(...args: unknown[]) {
super(
['maximum clock drift exceeded'].concat(args as string[]).join(' '),
);
this.name = 'ClockDriftError';
}
};
static OverflowError = class OverflowError extends Error {
constructor() {
super('timestamp counter overflow');
this.name = 'OverflowError';
}
};
static InvalidError = class InvalidError extends Error {
constructor(...args: unknown[]) {
super(['timestamp is not valid'].concat(args.map(String)).join(' '));
this.name = 'InvalidError';
}
};
}
class MutableTimestamp extends Timestamp {
static from(timestamp: Timestamp) {
return new MutableTimestamp(
timestamp.millis(),
timestamp.counter(),
timestamp.node(),
);
}
setMillis(n: number) {
this._state.millis = n;
}
setCounter(n: number) {
this._state.counter = n;
}
setNode(n: string) {
this._state.node = n;
}
}

14
packages/crdt/src/main.ts Normal file
View File

@@ -0,0 +1,14 @@
import * as SyncPb from './proto/sync_pb';
export {
merkle,
getClock,
setClock,
makeClock,
makeClientId,
serializeClock,
deserializeClock,
type Clock,
Timestamp,
} from './crdt';
export const SyncProtoBuf = SyncPb;

View File

@@ -20,11 +20,11 @@ export class EncryptedData extends jspb.Message {
setData(value: Uint8Array | string): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): EncryptedDataAsObject;
toObject(includeInstance?: boolean): EncryptedData.AsObject;
static toObject(
includeInstance: boolean,
msg: EncryptedData,
): EncryptedDataAsObject;
): EncryptedData.AsObject;
static extensions: { [key: number]: jspb.ExtensionFieldInfo<jspb.Message> };
static extensionsBinary: {
[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>;
@@ -40,11 +40,13 @@ export class EncryptedData extends jspb.Message {
): EncryptedData;
}
export type EncryptedDataAsObject = {
iv: Uint8Array | string;
authtag: Uint8Array | string;
data: Uint8Array | string;
};
export namespace EncryptedData {
export type AsObject = {
iv: Uint8Array | string;
authtag: Uint8Array | string;
data: Uint8Array | string;
};
}
export class Message extends jspb.Message {
getDataset(): string;
@@ -60,8 +62,8 @@ export class Message extends jspb.Message {
setValue(value: string): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): MessageAsObject;
static toObject(includeInstance: boolean, msg: Message): MessageAsObject;
toObject(includeInstance?: boolean): Message.AsObject;
static toObject(includeInstance: boolean, msg: Message): Message.AsObject;
static extensions: { [key: number]: jspb.ExtensionFieldInfo<jspb.Message> };
static extensionsBinary: {
[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>;
@@ -77,12 +79,14 @@ export class Message extends jspb.Message {
): Message;
}
export type MessageAsObject = {
dataset: string;
row: string;
column: string;
value: string;
};
export namespace Message {
export type AsObject = {
dataset: string;
row: string;
column: string;
value: string;
};
}
export class MessageEnvelope extends jspb.Message {
getTimestamp(): string;
@@ -97,11 +101,11 @@ export class MessageEnvelope extends jspb.Message {
setContent(value: Uint8Array | string): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): MessageEnvelopeAsObject;
toObject(includeInstance?: boolean): MessageEnvelope.AsObject;
static toObject(
includeInstance: boolean,
msg: MessageEnvelope,
): MessageEnvelopeAsObject;
): MessageEnvelope.AsObject;
static extensions: { [key: number]: jspb.ExtensionFieldInfo<jspb.Message> };
static extensionsBinary: {
[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>;
@@ -117,11 +121,13 @@ export class MessageEnvelope extends jspb.Message {
): MessageEnvelope;
}
export type MessageEnvelopeAsObject = {
timestamp: string;
isencrypted: boolean;
content: Uint8Array | string;
};
export namespace MessageEnvelope {
export type AsObject = {
timestamp: string;
isencrypted: boolean;
content: Uint8Array | string;
};
}
export class SyncRequest extends jspb.Message {
clearMessagesList(): void;
@@ -142,11 +148,11 @@ export class SyncRequest extends jspb.Message {
setSince(value: string): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): SyncRequestAsObject;
toObject(includeInstance?: boolean): SyncRequest.AsObject;
static toObject(
includeInstance: boolean,
msg: SyncRequest,
): SyncRequestAsObject;
): SyncRequest.AsObject;
static extensions: { [key: number]: jspb.ExtensionFieldInfo<jspb.Message> };
static extensionsBinary: {
[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>;
@@ -162,13 +168,15 @@ export class SyncRequest extends jspb.Message {
): SyncRequest;
}
export type SyncRequestAsObject = {
messagesList: Array<MessageEnvelopeAsObject>;
fileid: string;
groupid: string;
keyid: string;
since: string;
};
export namespace SyncRequest {
export type AsObject = {
messagesList: Array<MessageEnvelope.AsObject>;
fileid: string;
groupid: string;
keyid: string;
since: string;
};
}
export class SyncResponse extends jspb.Message {
clearMessagesList(): void;
@@ -180,11 +188,11 @@ export class SyncResponse extends jspb.Message {
setMerkle(value: string): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): SyncResponseAsObject;
toObject(includeInstance?: boolean): SyncResponse.AsObject;
static toObject(
includeInstance: boolean,
msg: SyncResponse,
): SyncResponseAsObject;
): SyncResponse.AsObject;
static extensions: { [key: number]: jspb.ExtensionFieldInfo<jspb.Message> };
static extensionsBinary: {
[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>;
@@ -200,7 +208,9 @@ export class SyncResponse extends jspb.Message {
): SyncResponse;
}
export type SyncResponseAsObject = {
messagesList: Array<MessageEnvelopeAsObject>;
merkle: string;
};
export namespace SyncResponse {
export type AsObject = {
messagesList: Array<MessageEnvelope.AsObject>;
merkle: string;
};
}

View File

@@ -0,0 +1,15 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
// Using ES2021 because thats the newest version where
// the latest Node 16.x release supports all of the features
"target": "ES2021",
"module": "CommonJS",
"noEmit": false,
"declaration": true,
"strict": true,
"outDir": "dist"
},
"include": ["."],
"exclude": ["dist"]
}

View File

@@ -1,3 +0,0 @@
bundle.browser.js
build/
public/kcab/

View File

@@ -9,13 +9,17 @@ test-results
# production
build
build-stats
stats.json
# misc
.DS_Store
.env
npm-debug.log
.swc
*kcab.*
public/kcab
public/data
public/data-file-index.txt
public/*.wasm

View File

@@ -0,0 +1,16 @@
{
"jsc": {
"target": "es2022",
"transform": {
"react": {
"runtime": "automatic"
}
},
"externalHelpers": true,
"parser": {
"syntax": "typescript",
"tsx": true
}
},
"sourceMaps": true
}

View File

@@ -4,11 +4,13 @@ Actual on the web
E2E (end-to-end) tests use [Playwright](https://playwright.dev/). Running them requires an Actual server to be running either locally or on a remote server.
### Functional
Running against the local server:
```sh
# Start the development server
yarn start:browser
yarn start
# Run against the local server (localhost:3001)
yarn e2e
@@ -19,3 +21,44 @@ Running against a remote server:
```sh
E2E_START_URL=http://my-remote-server.com yarn e2e
```
### Visual regression
Visual regression tests (also known as screenshot tests) check that the visual appearance of the product has not regressed. Each environment has slightly different colors, fonts etc. Mac differs from Windows which differs from Linux. In order to have a stable test environment for visual comparisons - you must use a standartised docker container. This ensures that the tests are always ran in a consistent environment.
Prerequisites:
- Docker installed
#### Running against the local server
First start a dev instance:
```sh
HTTPS=true yarn start
```
Note the network IP address and port the dev instance is listening on.
Next, navigate to the root of your project folder, run the standartised docker container, and launch the visual regression tests from within it.
```sh
# Run docker container
docker run --rm --network host -v $(pwd):/work/ -w /work/ -it mcr.microsoft.com/playwright:v1.41.1-jammy /bin/bash
# If you receive an error such as "docker: invalid reference format", please instead use the following command:
docker run --rm --network host -v ${pwd}:/work/ -w /work/ -it mcr.microsoft.com/playwright:v1.41.1-jammy /bin/bash
# Run the VRT tests: important - they MUST be ran against a HTTPS server. Use the ip and port noted earlier
E2E_START_URL=https://ip:port yarn vrt
# To update snapshots, use the following command:
E2E_START_URL=https://ip:port yarn vrt --update-snapshots
```
#### Running against a remote server
You can also run the tests against a remote server by passing the URL:
```sh
E2E_START_URL=https://my-remote-server.com yarn vrt
```

View File

@@ -3,15 +3,16 @@
ROOT=`dirname $0`
cd "$ROOT/.."
VERSION=`cat "$ROOT/../../desktop-client/package.json" | grep version | head -n1 | awk -F "\"" '{print $4}' | tr -d '\r\n'`
echo "Building version $VERSION for the browser..."
echo "Building the browser..."
rm -fr build
export IS_GENERIC_BROWSER=1
export INLINE_RUNTIME_CHUNK=false
export REACT_APP_BACKEND_WORKER_HASH=`ls "$ROOT"/../public/kcab/kcab.worker.*.js | sed 's/.*kcab\.worker\.\(.*\)\.js/\1/'`
export REACT_APP_ACTUAL_VERSION="$VERSION"
yarn build
rm -fr build-stats
mkdir build-stats
mv build/kcab/stats.json build-stats/loot-core-stats.json
mv ./stats.json build-stats/web-stats.json

View File

@@ -3,11 +3,8 @@
ROOT=`dirname $0`
cd "$ROOT/.."
VERSION=`cat "$ROOT/../../desktop-client/package.json" | grep version | head -n1 | awk -F "\"" '{print $4}' | tr -d '\r\n'`
export IS_GENERIC_BROWSER=1
export PORT=3001
export REACT_APP_BACKEND_WORKER_HASH="dev"
export REACT_APP_ACTUAL_VERSION="$VERSION"
yarn start

View File

@@ -1,45 +0,0 @@
const path = require('path');
const {
addWebpackResolve,
override,
overrideDevServer,
babelInclude,
} = require('customize-cra');
if (process.env.CI) {
process.env.DISABLE_ESLINT_PLUGIN = 'true';
}
module.exports = {
webpack: override(
babelInclude([path.resolve('src'), path.resolve('../loot-core')]),
addWebpackResolve({
extensions: [
...(process.env.IS_GENERIC_BROWSER
? ['.browser.js', '.browser.ts', '.browser.tsx']
: []),
'.web.js',
'.web.ts',
'.web.tsx',
'.js',
'.ts',
'.tsx',
],
}),
config => {
config.cache = false;
return config;
},
),
devServer: overrideDevServer(config => {
return {
...config,
headers: {
...config.headers,
'Cross-Origin-Opener-Policy': 'same-origin',
'Cross-Origin-Embedder-Policy': 'require-corp',
},
};
}),
};

View File

@@ -24,18 +24,17 @@ test.describe('Accounts', () => {
test('creates a new account and views the initial balance transaction', async () => {
const accountPage = await navigation.createAccount({
name: 'New Account',
type: 'Checking / Cash',
offBudget: false,
balance: 100,
});
expect(await accountPage.getNthTransaction(0)).toMatchObject({
payee: 'Starting Balance',
notes: '',
category: 'Starting Balances',
debit: '',
credit: '100.00',
});
const transaction = accountPage.getNthTransaction(0);
await expect(transaction.payee).toHaveText('Starting Balance');
await expect(transaction.notes).toHaveText('');
await expect(transaction.category).toHaveText('Starting Balances');
await expect(transaction.debit).toHaveText('');
await expect(transaction.credit).toHaveText('100.00');
await expect(page).toMatchThemeScreenshots();
});
test('closes an account', async () => {
@@ -45,8 +44,10 @@ test.describe('Accounts', () => {
const modal = await accountPage.clickCloseAccount();
await modal.selectTransferAccount('Vanguard 401k');
await expect(page).toMatchThemeScreenshots();
await modal.closeAccount();
await expect(accountPage.accountName).toHaveText('Closed: Roth IRA');
await expect(page).toMatchThemeScreenshots();
});
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

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