Implement "infinite scroll" for expenses (#95)

* Extract ExpenseCard vom ExpenseList

* Implement simple pagination of expenses (see #30)

- display only this year's entries by default
- a "Show more" button reveals all expenses

* Turn getPrisma() into constant "prisma"

- getPrisma() is not async and doesn't need to be awaited
- turn getPrisma() into exported constant "prisma"

* Select fields to be returned by getGroupExpenses()

- make JSON more concise and less redundant
- some properties were removed (i.e.instead of "expense.paidById" use "expense.paidBy.id")

* Remove "participants" from ExpenseCard

- no need to search for participant by id to get it's name
- name property is already present in expense

* Add option to fetch a slice of group expenses

- specify offset and length to get expenses for [offset, offset+length[
- add function to get total number of group expenses

* Add api route for client to fetch group expenses

* Remove "Show more" button from expense list

* Implement infinite scroll

- in server component Page
  - only load first 200 expenses max
  - pass preloaded expenses and total count

- in client component ExpenseList, if there are more expenses to show
  - test if there are more expenses
  - append preloading "skeletons" to end of list
  - fetch more expenses when last item in list comes into view
  - after each fetch increase fetch-length by factor 1.5
    - rationale: db fetch usually is not the issue here, the longer the list gets, the longer react needs to redraw

* Use server action instead of api endpoint

* Fixes

---------

Co-authored-by: Sebastien Castiel <sebastien@castiel.me>
This commit is contained in:
Stefan Hynst
2024-05-30 03:36:07 +02:00
committed by GitHub
parent 833237b613
commit d3fd8027a5
12 changed files with 266 additions and 138 deletions

15
package-lock.json generated
View File

@@ -43,6 +43,7 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.47.0",
"react-intersection-observer": "^9.8.0",
"sharp": "^0.33.2",
"tailwind-merge": "^1.14.0",
"tailwindcss-animate": "^1.0.7",
@@ -7510,6 +7511,20 @@
"react": "^16.8.0 || ^17 || ^18"
}
},
"node_modules/react-intersection-observer": {
"version": "9.8.0",
"resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-9.8.0.tgz",
"integrity": "sha512-wXHvMQUsTagh3X0Z6jDtGkIXc3VVCd2tjDRYR9kII3GKrZr0XF0xtpfdamo2n8BSF+zzfeeBVOTjxZWpBp9X0g==",
"peerDependencies": {
"react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
},
"peerDependenciesMeta": {
"react-dom": {
"optional": true
}
}
},
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",