mirror of
https://github.com/actualbudget/actual.git
synced 2026-03-18 07:50:25 -05:00
Compare commits
36 Commits
matiss/des
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f821d2849 | ||
|
|
beee16bc8c | ||
|
|
4cdb26f9a7 | ||
|
|
15358b6b54 | ||
|
|
6d9b1a1d72 | ||
|
|
108ccc8aba | ||
|
|
ee8f8bfbba | ||
|
|
c4ee71409e | ||
|
|
dfd6e468a6 | ||
|
|
5b227f5fa1 | ||
|
|
e1606b31ab | ||
|
|
4f7c3c51a5 | ||
|
|
0e1fc07bf3 | ||
|
|
53cdc6fa48 | ||
|
|
1d0281025d | ||
|
|
f73c5e9210 | ||
|
|
a4eb17eff2 | ||
|
|
8a3db77cff | ||
|
|
f5a62627f0 | ||
|
|
6c150cf28a | ||
|
|
e069312ac3 | ||
|
|
f266b761c2 | ||
|
|
328b36f124 | ||
|
|
8934df0cb5 | ||
|
|
ab269fa4ea | ||
|
|
9c61cfc145 | ||
|
|
d86c9cf735 | ||
|
|
f95cfbf82c | ||
|
|
767f77fea3 | ||
|
|
d6dcc30e44 | ||
|
|
541df52441 | ||
|
|
85e3166495 | ||
|
|
5d4bbc9ebb | ||
|
|
031aac9799 | ||
|
|
c53c5c2f36 | ||
|
|
e968213977 |
8
.github/actions/setup/action.yml
vendored
8
.github/actions/setup/action.yml
vendored
@@ -15,7 +15,7 @@ runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Install node
|
||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version: 22
|
||||
- name: Install yarn
|
||||
@@ -27,7 +27,7 @@ runs:
|
||||
run: echo "version=$(node -v)" >> "$GITHUB_OUTPUT"
|
||||
shell: bash
|
||||
- name: Cache
|
||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||
id: cache
|
||||
with:
|
||||
path: ${{ format('{0}/**/node_modules', inputs.working-directory) }}
|
||||
@@ -36,7 +36,7 @@ runs:
|
||||
run: mkdir -p ${{ format('{0}/.lage', inputs.working-directory) }}
|
||||
shell: bash
|
||||
- name: Cache Lage
|
||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||
with:
|
||||
path: ${{ format('{0}/.lage', inputs.working-directory) }}
|
||||
key: lage-${{ runner.os }}-${{ github.sha }}
|
||||
@@ -48,7 +48,7 @@ runs:
|
||||
shell: bash
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
- name: Download translations
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
repository: actualbudget/translations
|
||||
path: ${{ inputs.working-directory }}/packages/desktop-client/locale
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
|
||||
4
.github/workflows/autofix.yml
vendored
4
.github/workflows/autofix.yml
vendored
@@ -15,11 +15,11 @@ jobs:
|
||||
autofix:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
download-translations: 'false'
|
||||
- name: Format code
|
||||
run: yarn lint:fix
|
||||
- uses: autofix-ci/action@635ffb0c9798bd160680f18fd73371e355b85f27
|
||||
- uses: autofix-ci/action@7a166d7532b277f34e16238930461bf77f9d7ed8 # v1.3.3
|
||||
|
||||
20
.github/workflows/build.yml
vendored
20
.github/workflows/build.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
api:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
@@ -34,12 +34,12 @@ jobs:
|
||||
- name: Prepare bundle stats artifact
|
||||
run: cp packages/api/app/stats.json api-stats.json
|
||||
- name: Upload Build
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: actual-api
|
||||
path: packages/api/actual-api.tgz
|
||||
- name: Upload API bundle stats
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: api-build-stats
|
||||
path: api-stats.json
|
||||
@@ -47,7 +47,7 @@ jobs:
|
||||
crdt:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
@@ -57,7 +57,7 @@ jobs:
|
||||
- name: Create package tgz
|
||||
run: cd packages/crdt && yarn pack && mv package.tgz actual-crdt.tgz
|
||||
- name: Upload Build
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: actual-crdt
|
||||
path: packages/crdt/actual-crdt.tgz
|
||||
@@ -65,18 +65,18 @@ jobs:
|
||||
web:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
- name: Build Web
|
||||
run: yarn build:browser
|
||||
- name: Upload Build
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: actual-web
|
||||
path: packages/desktop-client/build
|
||||
- name: Upload Build Stats
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: build-stats
|
||||
path: packages/desktop-client/build-stats
|
||||
@@ -84,7 +84,7 @@ jobs:
|
||||
server:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
@@ -92,7 +92,7 @@ jobs:
|
||||
- name: Build Server
|
||||
run: yarn workspace @actual-app/sync-server build
|
||||
- name: Upload Build
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: sync-server
|
||||
path: packages/sync-server/build
|
||||
|
||||
20
.github/workflows/check.yml
vendored
20
.github/workflows/check.yml
vendored
@@ -12,10 +12,20 @@ concurrency:
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
|
||||
|
||||
jobs:
|
||||
constraints:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
download-translations: 'false'
|
||||
- name: Check dependency version consistency
|
||||
run: yarn constraints
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
@@ -25,7 +35,7 @@ jobs:
|
||||
typecheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
@@ -35,7 +45,7 @@ jobs:
|
||||
validate-cli:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
@@ -47,7 +57,7 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
@@ -59,7 +69,7 @@ jobs:
|
||||
if: github.event_name == 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
|
||||
6
.github/workflows/codeql.yml
vendored
6
.github/workflows/codeql.yml
vendored
@@ -22,14 +22,14 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
uses: github/codeql-action/init@b1bff81932f5cdfc8695c7752dcee935dcd061c8 # v4.33.0
|
||||
with:
|
||||
languages: javascript
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
uses: github/codeql-action/analyze@b1bff81932f5cdfc8695c7752dcee935dcd061c8 # v4.33.0
|
||||
with:
|
||||
category: '/language:javascript'
|
||||
|
||||
2
.github/workflows/count-points.yml
vendored
2
.github/workflows/count-points.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
|
||||
16
.github/workflows/docker-edge.yml
vendored
16
.github/workflows/docker-edge.yml
vendored
@@ -36,17 +36,17 @@ jobs:
|
||||
matrix:
|
||||
os: [ubuntu, alpine]
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
|
||||
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@318604b99e75e41977312d83839a89be02ca4893 # v5.9.0
|
||||
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
|
||||
with:
|
||||
# Push to both Docker Hub and Github Container Registry
|
||||
images: ${{ env.IMAGES }}
|
||||
@@ -54,14 +54,14 @@ jobs:
|
||||
tags: ${{ env.TAGS }}
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
||||
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
|
||||
if: github.event_name != 'pull_request' && !github.event.repository.fork
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
||||
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
|
||||
if: github.event_name != 'pull_request'
|
||||
with:
|
||||
registry: ghcr.io
|
||||
@@ -76,7 +76,7 @@ jobs:
|
||||
run: yarn build:server
|
||||
|
||||
- name: Build image for testing
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
|
||||
with:
|
||||
context: .
|
||||
push: false
|
||||
@@ -93,7 +93,7 @@ jobs:
|
||||
# This will use the cache from the earlier build step and not rebuild the image
|
||||
# https://docs.docker.com/build/ci/github-actions/test-before-push/
|
||||
- name: Build and push images
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
|
||||
with:
|
||||
context: .
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
|
||||
18
.github/workflows/docker-release.yml
vendored
18
.github/workflows/docker-release.yml
vendored
@@ -28,17 +28,17 @@ jobs:
|
||||
name: Build Docker image
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
|
||||
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@318604b99e75e41977312d83839a89be02ca4893 # v5.9.0
|
||||
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
|
||||
with:
|
||||
# Push to both Docker Hub and Github Container Registry
|
||||
images: ${{ env.IMAGES }}
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
|
||||
- name: Docker meta for Alpine image
|
||||
id: alpine-meta
|
||||
uses: docker/metadata-action@318604b99e75e41977312d83839a89be02ca4893 # v5.9.0
|
||||
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
|
||||
with:
|
||||
images: ${{ env.IMAGES }}
|
||||
# Automatically update :latest
|
||||
@@ -58,13 +58,13 @@ jobs:
|
||||
tags: ${{ env.TAGS }}
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
||||
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
||||
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
@@ -78,7 +78,7 @@ jobs:
|
||||
run: yarn build:server
|
||||
|
||||
- name: Build and push ubuntu image
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
@@ -87,7 +87,7 @@ jobs:
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
|
||||
- name: Build and push alpine image
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
|
||||
20
.github/workflows/e2e-test.yml
vendored
20
.github/workflows/e2e-test.yml
vendored
@@ -32,7 +32,7 @@ jobs:
|
||||
container:
|
||||
image: mcr.microsoft.com/playwright:v1.58.2-jammy
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
@@ -41,7 +41,7 @@ jobs:
|
||||
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
||||
- name: Run E2E Tests
|
||||
run: yarn e2e --shard=${{ matrix.shard }}/5
|
||||
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
if: always()
|
||||
with:
|
||||
name: desktop-client-test-results-shard-${{ matrix.shard }}
|
||||
@@ -55,7 +55,7 @@ jobs:
|
||||
container:
|
||||
image: mcr.microsoft.com/playwright:v1.58.2-jammy
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
@@ -65,7 +65,7 @@ jobs:
|
||||
- name: Run Desktop app E2E Tests
|
||||
run: |
|
||||
xvfb-run --auto-servernum --server-args="-screen 0 1920x1080x24" -- yarn e2e:desktop
|
||||
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
if: always()
|
||||
with:
|
||||
name: desktop-app-test-results
|
||||
@@ -83,14 +83,14 @@ jobs:
|
||||
container:
|
||||
image: mcr.microsoft.com/playwright:v1.58.2-jammy
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
download-translations: 'false'
|
||||
- name: Run VRT Tests
|
||||
run: yarn vrt --shard=${{ matrix.shard }}/5
|
||||
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
if: always()
|
||||
with:
|
||||
name: vrt-blob-report-${{ matrix.shard }}
|
||||
@@ -106,11 +106,11 @@ jobs:
|
||||
container:
|
||||
image: mcr.microsoft.com/playwright:v1.58.2-jammy
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
- name: Download all blob reports
|
||||
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
path: packages/desktop-client/all-blob-reports
|
||||
pattern: vrt-blob-report-*
|
||||
@@ -118,7 +118,7 @@ jobs:
|
||||
- name: Merge reports
|
||||
id: merge-reports
|
||||
run: yarn workspace @actual-app/web run playwright merge-reports --reporter html ./all-blob-reports
|
||||
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
id: playwright-report-vrt
|
||||
with:
|
||||
name: html-report--attempt-${{ github.run_attempt }}
|
||||
@@ -134,7 +134,7 @@ jobs:
|
||||
echo "${{ steps.playwright-report-vrt.outputs.artifact-url }}" > vrt-metadata/artifact-url.txt
|
||||
- name: Upload VRT metadata
|
||||
if: github.event_name == 'pull_request'
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: vrt-comment-metadata
|
||||
path: vrt-metadata/
|
||||
|
||||
4
.github/workflows/e2e-vrt-comment.yml
vendored
4
.github/workflows/e2e-vrt-comment.yml
vendored
@@ -18,7 +18,7 @@ jobs:
|
||||
if: github.event.workflow_run.event == 'pull_request'
|
||||
steps:
|
||||
- name: Download VRT metadata
|
||||
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
run-id: ${{ github.event.workflow_run.id }}
|
||||
@@ -53,7 +53,7 @@ jobs:
|
||||
|
||||
- name: Comment on PR with VRT report link
|
||||
if: steps.metadata.outputs.should_comment == 'true'
|
||||
uses: marocchino/sticky-pull-request-comment@773744901bac0e8cbb5a0dc842800d45e9b2b405 # v2.9.4
|
||||
uses: marocchino/sticky-pull-request-comment@70d2764d1a7d5d9560b100cbea0077fc8f633987 # v3.0.2
|
||||
with:
|
||||
number: ${{ steps.metadata.outputs.pr_number }}
|
||||
header: vrt-comment
|
||||
|
||||
10
.github/workflows/electron-master.yml
vendored
10
.github/workflows/electron-master.yml
vendored
@@ -29,7 +29,7 @@ jobs:
|
||||
- macos-latest
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- if: ${{ startsWith(matrix.os, 'windows') }}
|
||||
run: pip.exe install setuptools
|
||||
- if: ${{ ! startsWith(matrix.os, 'windows') }}
|
||||
@@ -74,7 +74,7 @@ jobs:
|
||||
if: ${{ ! startsWith(matrix.os, 'macos') }}
|
||||
run: ./bin/package-electron
|
||||
- name: Upload Build
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: actual-electron-${{ matrix.os }}
|
||||
path: |
|
||||
@@ -85,13 +85,13 @@ jobs:
|
||||
packages/desktop-electron/dist/*.flatpak
|
||||
- name: Upload Windows Store Build
|
||||
if: ${{ startsWith(matrix.os, 'windows') }}
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: actual-electron-${{ matrix.os }}-appx
|
||||
path: |
|
||||
packages/desktop-electron/dist/*.appx
|
||||
- name: Add to new release
|
||||
uses: softprops/action-gh-release@5be0e66d93ac7ed76da52eca8bb058f665c3a5fe # v2.4.2
|
||||
uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1
|
||||
with:
|
||||
draft: true
|
||||
body: |
|
||||
@@ -126,7 +126,7 @@ jobs:
|
||||
Install-Module -Name StoreBroker -AcceptLicense -Force -Scope CurrentUser -Verbose
|
||||
|
||||
- name: Download Microsoft Store artifacts
|
||||
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: actual-electron-windows-latest-appx
|
||||
|
||||
|
||||
26
.github/workflows/electron-pr.yml
vendored
26
.github/workflows/electron-pr.yml
vendored
@@ -33,7 +33,7 @@ jobs:
|
||||
- macos-latest
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- if: ${{ startsWith(matrix.os, 'windows') }}
|
||||
run: pip.exe install setuptools
|
||||
- if: ${{ ! startsWith(matrix.os, 'windows') }}
|
||||
@@ -42,6 +42,8 @@ jobs:
|
||||
python3 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
python3 -m pip install setuptools
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
- if: ${{ startsWith(matrix.os, 'ubuntu') }}
|
||||
name: Setup Flatpak dependencies
|
||||
run: |
|
||||
@@ -56,65 +58,63 @@ jobs:
|
||||
|
||||
METAINFO_FILE="packages/desktop-electron/extra-resources/linux/com.actualbudget.actual.metainfo.xml"
|
||||
TODAY=$(date +%Y-%m-%d)
|
||||
VERSION=$(node ./packages/ci-actions/bin/get-next-package-version.js --package-json ./packages/desktop-electron/package.json --type nightly)
|
||||
VERSION=$(yarn workspace @actual-app/ci-actions tsx bin/get-next-package-version.ts --package-json ./packages/desktop-electron/package.json --type nightly)
|
||||
sed -i "s/%RELEASE_VERSION%/$VERSION/g; s/%RELEASE_DATE%/$TODAY/g" "$METAINFO_FILE"
|
||||
flatpak run --command=flatpak-builder-lint org.flatpak.Builder appstream "$METAINFO_FILE"
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
- name: Build Electron
|
||||
run: ./bin/package-electron
|
||||
|
||||
- name: Upload Linux x64 AppImage
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: Actual-linux-x86_64.AppImage
|
||||
if-no-files-found: ignore
|
||||
path: packages/desktop-electron/dist/Actual-linux-x86_64.AppImage
|
||||
|
||||
- name: Upload Linux arm64 AppImage
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: Actual-linux-arm64.AppImage
|
||||
if-no-files-found: ignore
|
||||
path: packages/desktop-electron/dist/Actual-linux-arm64.AppImage
|
||||
|
||||
- name: Upload Linux x64 flatpak
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: Actual-linux-x86_64.flatpak
|
||||
if-no-files-found: ignore
|
||||
path: packages/desktop-electron/dist/Actual-linux-x86_64.flatpak
|
||||
|
||||
- name: Upload Windows x32 exe
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: Actual-windows-ia32.exe
|
||||
if-no-files-found: ignore
|
||||
path: packages/desktop-electron/dist/Actual-windows-ia32.exe
|
||||
|
||||
- name: Upload Windows x64 exe
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: Actual-windows-x64.exe
|
||||
if-no-files-found: ignore
|
||||
path: packages/desktop-electron/dist/Actual-windows-x64.exe
|
||||
|
||||
- name: Upload Windows arm64 exe
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: Actual-windows-arm64.exe
|
||||
if-no-files-found: ignore
|
||||
path: packages/desktop-electron/dist/Actual-windows-arm64.exe
|
||||
|
||||
- name: Upload Mac x64 dmg
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: Actual-mac-x64.dmg
|
||||
if-no-files-found: ignore
|
||||
path: packages/desktop-electron/dist/Actual-mac-x64.dmg
|
||||
|
||||
- name: Upload Mac arm64 dmg
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: Actual-mac-arm64.dmg
|
||||
if-no-files-found: ignore
|
||||
@@ -122,7 +122,7 @@ jobs:
|
||||
|
||||
- name: Upload Windows Store Build
|
||||
if: ${{ startsWith(matrix.os, 'windows') }}
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: actual-electron-${{ matrix.os }}-appx
|
||||
path: |
|
||||
|
||||
2
.github/workflows/fork-pr-welcome.yml
vendored
2
.github/workflows/fork-pr-welcome.yml
vendored
@@ -25,7 +25,7 @@ jobs:
|
||||
if: github.event.pull_request.head.repo.full_name != github.repository
|
||||
steps:
|
||||
- name: Post welcome comment
|
||||
uses: marocchino/sticky-pull-request-comment@773744901bac0e8cbb5a0dc842800d45e9b2b405 # v2.9.4
|
||||
uses: marocchino/sticky-pull-request-comment@70d2764d1a7d5d9560b100cbea0077fc8f633987 # v3.0.2
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
number: ${{ github.event.pull_request.number }}
|
||||
|
||||
12
.github/workflows/generate-release-pr.yml
vendored
12
.github/workflows/generate-release-pr.yml
vendored
@@ -17,9 +17,13 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
ref: ${{ github.event.inputs.ref }}
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
download-translations: 'false'
|
||||
- name: Bump package versions
|
||||
id: bump_package_versions
|
||||
shell: bash
|
||||
@@ -35,12 +39,12 @@ jobs:
|
||||
pkg="${packages[$key]}"
|
||||
|
||||
if [[ -n "${{ github.event.inputs.version }}" ]]; then
|
||||
version=$(node ./packages/ci-actions/bin/get-next-package-version.js \
|
||||
version=$(yarn workspace @actual-app/ci-actions tsx bin/get-next-package-version.ts \
|
||||
--package-json "./packages/$pkg/package.json" \
|
||||
--version "${{ github.event.inputs.version }}" \
|
||||
--update)
|
||||
else
|
||||
version=$(node ./packages/ci-actions/bin/get-next-package-version.js \
|
||||
version=$(yarn workspace @actual-app/ci-actions tsx bin/get-next-package-version.ts \
|
||||
--package-json "./packages/$pkg/package.json" \
|
||||
--type auto \
|
||||
--update)
|
||||
@@ -51,7 +55,7 @@ jobs:
|
||||
|
||||
echo "version=$NEW_WEB_VERSION" >> "$GITHUB_OUTPUT"
|
||||
- name: Create PR
|
||||
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
|
||||
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
|
||||
with:
|
||||
token: ${{ secrets.ACTIONS_UPDATE_TOKEN }}
|
||||
commit-message: '🔖 (${{ steps.bump_package_versions.outputs.version }})'
|
||||
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
if: github.repository == 'actualbudget/actual'
|
||||
steps:
|
||||
- name: Check out main repository
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
path: actual
|
||||
- name: Set up environment
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
push \
|
||||
actualbudget/actual
|
||||
- name: Check out updated translations
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
ssh-key: ${{ secrets.STRING_IMPORT_DEPLOY_KEY }}
|
||||
repository: actualbudget/translations
|
||||
|
||||
@@ -24,8 +24,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# This is not a security concern because we have approved & merged the PR
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version: 22
|
||||
- name: Handle feature requests
|
||||
|
||||
2
.github/workflows/netlify-release.yml
vendored
2
.github/workflows/netlify-release.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Repository Checkout
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
|
||||
4
.github/workflows/publish-flathub.yml
vendored
4
.github/workflows/publish-flathub.yml
vendored
@@ -92,7 +92,7 @@ jobs:
|
||||
echo "APPIMAGE_ARM64_SHA256=$APPIMAGE_ARM64_SHA256" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Checkout Flathub repo
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
repository: flathub/com.actualbudget.actual
|
||||
token: ${{ secrets.FLATHUB_GITHUB_TOKEN }}
|
||||
@@ -113,7 +113,7 @@ jobs:
|
||||
cat com.actualbudget.actual.yml
|
||||
|
||||
- name: Create PR in Flathub repo
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
|
||||
with:
|
||||
token: ${{ secrets.FLATHUB_GITHUB_TOKEN }}
|
||||
commit-message: 'Update Actual flatpak to version ${{ steps.resolve_version.outputs.version }}'
|
||||
|
||||
27
.github/workflows/publish-nightly-electron.yml
vendored
27
.github/workflows/publish-nightly-electron.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
if: github.event.repository.fork == false
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- if: ${{ startsWith(matrix.os, 'windows') }}
|
||||
run: pip.exe install setuptools
|
||||
|
||||
@@ -39,6 +39,9 @@ jobs:
|
||||
source .venv/bin/activate
|
||||
python3 -m pip install setuptools
|
||||
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
|
||||
- if: ${{ startsWith(matrix.os, 'ubuntu') }}
|
||||
name: Setup Flatpak dependencies
|
||||
run: |
|
||||
@@ -53,16 +56,14 @@ jobs:
|
||||
|
||||
METAINFO_FILE="packages/desktop-electron/extra-resources/linux/com.actualbudget.actual.metainfo.xml"
|
||||
TODAY=$(date +%Y-%m-%d)
|
||||
VERSION=$(node ./packages/ci-actions/bin/get-next-package-version.js --package-json ./packages/desktop-electron/package.json --type nightly)
|
||||
VERSION=$(yarn workspace @actual-app/ci-actions tsx bin/get-next-package-version.ts --package-json ./packages/desktop-electron/package.json --type nightly)
|
||||
sed -i "s/%RELEASE_VERSION%/$VERSION/g; s/%RELEASE_DATE%/$TODAY/g" "$METAINFO_FILE"
|
||||
flatpak run --command=flatpak-builder-lint org.flatpak.Builder appstream "$METAINFO_FILE"
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
|
||||
- name: Update package versions
|
||||
run: |
|
||||
# Get new nightly version
|
||||
NEW_DESKTOP_APP_VERSION=$(node ./packages/ci-actions/bin/get-next-package-version.js --package-json ./packages/desktop-electron/package.json --type nightly)
|
||||
NEW_DESKTOP_APP_VERSION=$(yarn workspace @actual-app/ci-actions tsx bin/get-next-package-version.ts --package-json ./packages/desktop-electron/package.json --type nightly)
|
||||
|
||||
# Set package version
|
||||
npm version $NEW_DESKTOP_APP_VERSION --no-git-tag-version --workspace=desktop-electron --no-workspaces-update
|
||||
@@ -82,49 +83,49 @@ jobs:
|
||||
run: ./bin/package-electron
|
||||
|
||||
- name: Upload Linux x64 AppImage
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: Actual-linux-x86_64.AppImage
|
||||
if-no-files-found: ignore
|
||||
path: packages/desktop-electron/dist/Actual-linux-x86_64.AppImage
|
||||
|
||||
- name: Upload Linux arm64 AppImage
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: Actual-linux-arm64.AppImage
|
||||
if-no-files-found: ignore
|
||||
path: packages/desktop-electron/dist/Actual-linux-arm64.AppImage
|
||||
|
||||
- name: Upload Windows x32 exe
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: Actual-windows-ia32.exe
|
||||
if-no-files-found: ignore
|
||||
path: packages/desktop-electron/dist/Actual-windows-ia32.exe
|
||||
|
||||
- name: Upload Windows x64 exe
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: Actual-windows-x64.exe
|
||||
if-no-files-found: ignore
|
||||
path: packages/desktop-electron/dist/Actual-windows-x64.exe
|
||||
|
||||
- name: Upload Windows arm64 exe
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: Actual-windows-arm64.exe
|
||||
if-no-files-found: ignore
|
||||
path: packages/desktop-electron/dist/Actual-windows-arm64.exe
|
||||
|
||||
- name: Upload Mac x64 dmg
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: Actual-mac-x64.dmg
|
||||
if-no-files-found: ignore
|
||||
path: packages/desktop-electron/dist/Actual-mac-x64.dmg
|
||||
|
||||
- name: Upload Mac arm64 dmg
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: Actual-mac-arm64.dmg
|
||||
if-no-files-found: ignore
|
||||
@@ -132,7 +133,7 @@ jobs:
|
||||
|
||||
- name: Upload Windows Store Build
|
||||
if: ${{ startsWith(matrix.os, 'windows') }}
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: actual-electron-${{ matrix.os }}-appx
|
||||
path: |
|
||||
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
name: Build and pack npm packages
|
||||
if: github.event.repository.fork == false
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
@@ -20,11 +20,13 @@ jobs:
|
||||
- name: Update package versions
|
||||
run: |
|
||||
# Get new nightly versions
|
||||
NEW_WEB_VERSION=$(node ./packages/ci-actions/bin/get-next-package-version.js --package-json ./packages/desktop-client/package.json --type nightly)
|
||||
NEW_SYNC_VERSION=$(node ./packages/ci-actions/bin/get-next-package-version.js --package-json ./packages/sync-server/package.json --type nightly)
|
||||
NEW_API_VERSION=$(node ./packages/ci-actions/bin/get-next-package-version.js --package-json ./packages/api/package.json --type nightly)
|
||||
NEW_CORE_VERSION=$(yarn workspace @actual-app/ci-actions tsx bin/get-next-package-version.ts --package-json ./packages/loot-core/package.json --type nightly)
|
||||
NEW_WEB_VERSION=$(yarn workspace @actual-app/ci-actions tsx bin/get-next-package-version.ts --package-json ./packages/desktop-client/package.json --type nightly)
|
||||
NEW_SYNC_VERSION=$(yarn workspace @actual-app/ci-actions tsx bin/get-next-package-version.ts --package-json ./packages/sync-server/package.json --type nightly)
|
||||
NEW_API_VERSION=$(yarn workspace @actual-app/ci-actions tsx bin/get-next-package-version.ts --package-json ./packages/api/package.json --type nightly)
|
||||
|
||||
# Set package versions
|
||||
npm version $NEW_CORE_VERSION --no-git-tag-version --workspace=@actual-app/core --no-workspaces-update
|
||||
npm version $NEW_WEB_VERSION --no-git-tag-version --workspace=@actual-app/web --no-workspaces-update
|
||||
npm version $NEW_SYNC_VERSION --no-git-tag-version --workspace=@actual-app/sync-server --no-workspaces-update
|
||||
npm version $NEW_API_VERSION --no-git-tag-version --workspace=@actual-app/api --no-workspaces-update
|
||||
@@ -33,6 +35,10 @@ jobs:
|
||||
run: |
|
||||
yarn install
|
||||
|
||||
- name: Pack the core package
|
||||
run: |
|
||||
yarn workspace @actual-app/core pack --filename @actual-app/core.tgz
|
||||
|
||||
- name: Build Server & Web
|
||||
run: yarn build:server
|
||||
|
||||
@@ -49,10 +55,11 @@ jobs:
|
||||
yarn workspace @actual-app/api pack --filename @actual-app/api.tgz
|
||||
|
||||
- name: Upload package artifacts
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: npm-packages
|
||||
path: |
|
||||
packages/loot-core/@actual-app/core.tgz
|
||||
packages/desktop-client/@actual-app/web.tgz
|
||||
packages/sync-server/@actual-app/sync-server.tgz
|
||||
packages/api/@actual-app/api.tgz
|
||||
@@ -66,16 +73,22 @@ jobs:
|
||||
packages: write
|
||||
steps:
|
||||
- name: Download the artifacts
|
||||
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: npm-packages
|
||||
|
||||
- name: Setup node and npm registry
|
||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version: 22
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: Publish Core
|
||||
run: |
|
||||
npm publish loot-core/@actual-app/core.tgz --access public --tag nightly
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
- name: Publish Web
|
||||
run: |
|
||||
npm publish desktop-client/@actual-app/web.tgz --access public --tag nightly
|
||||
|
||||
19
.github/workflows/publish-npm-packages.yml
vendored
19
.github/workflows/publish-npm-packages.yml
vendored
@@ -11,11 +11,15 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: Build and pack npm packages
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
|
||||
- name: Pack the core package
|
||||
run: |
|
||||
yarn workspace @actual-app/core pack --filename @actual-app/core.tgz
|
||||
|
||||
- name: Build Web
|
||||
run: yarn build:server
|
||||
|
||||
@@ -32,10 +36,11 @@ jobs:
|
||||
yarn workspace @actual-app/api pack --filename @actual-app/api.tgz
|
||||
|
||||
- name: Upload package artifacts
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: npm-packages
|
||||
path: |
|
||||
packages/loot-core/@actual-app/core.tgz
|
||||
packages/desktop-client/@actual-app/web.tgz
|
||||
packages/sync-server/@actual-app/sync-server.tgz
|
||||
packages/api/@actual-app/api.tgz
|
||||
@@ -49,16 +54,22 @@ jobs:
|
||||
packages: write
|
||||
steps:
|
||||
- name: Download the artifacts
|
||||
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: npm-packages
|
||||
|
||||
- name: Setup node and npm registry
|
||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version: 22
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: Publish Core
|
||||
run: |
|
||||
npm publish loot-core/@actual-app/core.tgz --access public
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
- name: Publish Web
|
||||
run: |
|
||||
npm publish desktop-client/@actual-app/web.tgz --access public
|
||||
|
||||
2
.github/workflows/release-notes.yml
vendored
2
.github/workflows/release-notes.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Get changed files
|
||||
|
||||
10
.github/workflows/size-compare.yml
vendored
10
.github/workflows/size-compare.yml
vendored
@@ -35,7 +35,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout base branch
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
ref: ${{ github.base_ref }}
|
||||
- name: Set up environment
|
||||
@@ -80,7 +80,7 @@ jobs:
|
||||
exit 1
|
||||
|
||||
- name: Download web build artifact from ${{github.base_ref}}
|
||||
uses: dawidd6/action-download-artifact@ac66b43f0e6a346234dd65d4d0c8fbb31cb316e5 # v11
|
||||
uses: dawidd6/action-download-artifact@1f8785ff7a5130826f848e7f72725c85d241860f # v18
|
||||
id: pr-web-build
|
||||
with:
|
||||
branch: ${{github.base_ref}}
|
||||
@@ -89,7 +89,7 @@ jobs:
|
||||
name: build-stats
|
||||
path: base
|
||||
- name: Download API build artifact from ${{github.base_ref}}
|
||||
uses: dawidd6/action-download-artifact@ac66b43f0e6a346234dd65d4d0c8fbb31cb316e5 # v11
|
||||
uses: dawidd6/action-download-artifact@1f8785ff7a5130826f848e7f72725c85d241860f # v18
|
||||
id: pr-api-build
|
||||
with:
|
||||
branch: ${{github.base_ref}}
|
||||
@@ -98,7 +98,7 @@ jobs:
|
||||
name: api-build-stats
|
||||
path: base
|
||||
- name: Download build stats from PR
|
||||
uses: dawidd6/action-download-artifact@ac66b43f0e6a346234dd65d4d0c8fbb31cb316e5 # v11
|
||||
uses: dawidd6/action-download-artifact@1f8785ff7a5130826f848e7f72725c85d241860f # v18
|
||||
with:
|
||||
pr: ${{github.event.pull_request.number}}
|
||||
workflow: build.yml
|
||||
@@ -107,7 +107,7 @@ jobs:
|
||||
path: head
|
||||
allow_forks: true
|
||||
- name: Download API stats from PR
|
||||
uses: dawidd6/action-download-artifact@ac66b43f0e6a346234dd65d4d0c8fbb31cb316e5 # v11
|
||||
uses: dawidd6/action-download-artifact@1f8785ff7a5130826f848e7f72725c85d241860f # v18
|
||||
with:
|
||||
pr: ${{github.event.pull_request.number}}
|
||||
workflow: build.yml
|
||||
|
||||
6
.github/workflows/stale.yml
vendored
6
.github/workflows/stale.yml
vendored
@@ -8,7 +8,7 @@ jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0
|
||||
- uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
|
||||
with:
|
||||
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.'
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
stale-wip:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0
|
||||
- uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
|
||||
with:
|
||||
stale-pr-message: ':wave: Hi! It looks like this PR has not had any changes for a week now. Would you like someone to review this PR? If so - please remove the "[WIP]" prefix from the PR title. That will let the community know that this PR is open for a review.'
|
||||
days-before-stale: 7
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
stale-needs-info:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0
|
||||
- uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
|
||||
with:
|
||||
stale-issue-label: 'needs info'
|
||||
days-before-stale: -1
|
||||
|
||||
6
.github/workflows/vrt-update-apply.yml
vendored
6
.github/workflows/vrt-update-apply.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
||||
steps:
|
||||
- name: Download patch artifact
|
||||
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
run-id: ${{ github.event.workflow_run.id }}
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
path: /tmp/artifacts
|
||||
|
||||
- name: Download metadata artifact
|
||||
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
run-id: ${{ github.event.workflow_run.id }}
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
|
||||
- name: Checkout fork branch
|
||||
if: steps.metadata.outputs.pr_number != ''
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
repository: ${{ steps.metadata.outputs.head_repo }}
|
||||
ref: ${{ steps.metadata.outputs.head_ref }}
|
||||
|
||||
6
.github/workflows/vrt-update-generate.yml
vendored
6
.github/workflows/vrt-update-generate.yml
vendored
@@ -60,7 +60,7 @@ jobs:
|
||||
core.setOutput('head_ref', pr.head.ref);
|
||||
core.setOutput('head_repo', pr.head.repo.full_name);
|
||||
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
ref: ${{ steps.pr.outputs.head_sha }}
|
||||
|
||||
@@ -113,7 +113,7 @@ jobs:
|
||||
|
||||
- name: Upload patch artifact
|
||||
if: steps.create-patch.outputs.has_changes == 'true'
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: vrt-patch-${{ github.event.issue.number }}
|
||||
path: vrt-update.patch
|
||||
@@ -129,7 +129,7 @@ jobs:
|
||||
|
||||
- name: Upload PR metadata
|
||||
if: steps.create-patch.outputs.has_changes == 'true'
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: vrt-metadata-${{ github.event.issue.number }}
|
||||
path: pr-metadata/
|
||||
|
||||
13
.husky/post-checkout
Executable file
13
.husky/post-checkout
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/bin/sh
|
||||
# Run yarn install when switching branches (if yarn.lock changed)
|
||||
|
||||
# $3 is 1 for branch checkout, 0 for file checkout
|
||||
if [ "$3" != "1" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Check if yarn.lock changed between the old and new HEAD
|
||||
if git diff --name-only "$1" "$2" | grep -q "^yarn.lock$"; then
|
||||
echo "yarn.lock changed — running yarn install..."
|
||||
yarn install
|
||||
fi
|
||||
@@ -10,7 +10,7 @@
|
||||
"builtin",
|
||||
"external",
|
||||
"loot-core",
|
||||
"parent",
|
||||
["parent", "subpath"],
|
||||
"sibling",
|
||||
"index",
|
||||
"desktop-client"
|
||||
@@ -22,11 +22,11 @@
|
||||
},
|
||||
{
|
||||
"groupName": "loot-core",
|
||||
"elementNamePattern": ["loot-core/**"]
|
||||
"elementNamePattern": ["loot-core/**", "@actual-app/core/**"]
|
||||
},
|
||||
{
|
||||
"groupName": "desktop-client",
|
||||
"elementNamePattern": ["@desktop-client/**", "#**", "#**/**"]
|
||||
"elementNamePattern": ["@desktop-client/**"]
|
||||
}
|
||||
],
|
||||
"newlinesBetween": true
|
||||
|
||||
@@ -101,12 +101,18 @@
|
||||
"typescript/no-var-requires": "error",
|
||||
// we want to allow unions such as "{ name: DbAccount['name'] | DbPayee['name'] }"
|
||||
"typescript/no-duplicate-type-constituents": "off",
|
||||
// we want to allow unions such as "string | 'network' | 'file-key-mismatch'"
|
||||
"typescript/no-redundant-type-constituents": "off",
|
||||
"typescript/await-thenable": "error",
|
||||
"typescript/no-floating-promises": "error",
|
||||
"typescript/require-array-sort-compare": "error",
|
||||
"typescript/unbound-method": "error",
|
||||
"typescript/no-for-in-array": "warn", // TODO: covert to error
|
||||
"typescript/restrict-template-expressions": "warn", // TODO: covert to error
|
||||
"typescript/no-for-in-array": "error",
|
||||
"typescript/restrict-template-expressions": "error",
|
||||
"typescript/no-misused-spread": "warn", // TODO: enable this
|
||||
"typescript/no-base-to-string": "warn", // TODO: enable this
|
||||
"typescript/no-unsafe-unary-minus": "warn", // TODO: enable this
|
||||
"typescript/no-unsafe-type-assertion": "warn", // TODO: enable this
|
||||
|
||||
// Import rules
|
||||
"import/consistent-type-specifier-style": "error",
|
||||
|
||||
@@ -84,7 +84,7 @@ The core application logic that runs on any platform.
|
||||
|
||||
```bash
|
||||
# Run all loot-core tests
|
||||
yarn workspace loot-core run test
|
||||
yarn workspace @actual-app/core run test
|
||||
|
||||
# Or run tests across all packages using lage
|
||||
yarn test
|
||||
@@ -219,7 +219,7 @@ yarn test
|
||||
yarn test:debug
|
||||
|
||||
# Run tests for a specific package
|
||||
yarn workspace loot-core run test
|
||||
yarn workspace @actual-app/core run test
|
||||
```
|
||||
|
||||
**E2E Tests (Playwright)**
|
||||
@@ -625,7 +625,7 @@ Standard commands documented in `package.json` scripts and the Quick Start secti
|
||||
|
||||
- `yarn lint` / `yarn lint:fix` (uses oxlint + oxfmt)
|
||||
- `yarn test` (lage across all workspaces)
|
||||
- `yarn typecheck` (tsc + lage typecheck)
|
||||
- `yarn typecheck` (tsgo + lage typecheck)
|
||||
|
||||
### Testing and previewing the app
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ packages/desktop-client/bin/remove-untranslated-languages
|
||||
export NODE_OPTIONS="--max-old-space-size=4096"
|
||||
|
||||
yarn workspace plugins-service build
|
||||
yarn workspace loot-core build:browser
|
||||
yarn workspace @actual-app/core build:browser
|
||||
yarn workspace @actual-app/web build:browser
|
||||
|
||||
echo "packages/desktop-client/build"
|
||||
|
||||
@@ -51,16 +51,16 @@ fi
|
||||
export NODE_OPTIONS="--max-old-space-size=4096"
|
||||
|
||||
yarn workspace plugins-service build
|
||||
yarn workspace loot-core build:node
|
||||
yarn workspace @actual-app/core build:node
|
||||
yarn workspace @actual-app/web build --mode=desktop # electron specific build
|
||||
|
||||
# required for running the sync-server server
|
||||
yarn workspace loot-core build:browser
|
||||
yarn workspace @actual-app/core build:browser
|
||||
yarn workspace @actual-app/web build:browser
|
||||
yarn workspace @actual-app/sync-server build
|
||||
|
||||
# Emit loot-core declarations so desktop-electron (which includes typings/window.ts) can build
|
||||
yarn workspace loot-core exec tsc -p tsconfig.json
|
||||
# Emit @actual-app/core declarations so desktop-electron (which includes typings/window.ts) can build
|
||||
yarn workspace @actual-app/core exec tsgo -p tsconfig.json
|
||||
|
||||
yarn workspace desktop-electron update-client
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ module.exports = {
|
||||
pipeline: {
|
||||
typecheck: {
|
||||
type: 'npmScript',
|
||||
dependsOn: ['^typecheck'],
|
||||
},
|
||||
test: {
|
||||
type: 'npmScript',
|
||||
|
||||
30
package.json
30
package.json
@@ -25,16 +25,16 @@
|
||||
"start:desktop": "yarn desktop-dependencies && npm-run-all --parallel 'start:desktop-*'",
|
||||
"start:docs": "yarn workspace docs start",
|
||||
"desktop-dependencies": "npm-run-all --parallel rebuild-electron build:browser-backend build:plugins-service",
|
||||
"start:desktop-node": "yarn workspace loot-core watch:node",
|
||||
"start:desktop-node": "yarn workspace @actual-app/core watch:node",
|
||||
"start:desktop-client": "yarn workspace @actual-app/web watch",
|
||||
"start:desktop-server-client": "yarn workspace @actual-app/web build:browser",
|
||||
"start:desktop-electron": "yarn workspace desktop-electron watch",
|
||||
"start:browser": "yarn workspace plugins-service build-dev && npm-run-all --parallel 'start:browser-*'",
|
||||
"start:service-plugins": "yarn workspace plugins-service watch",
|
||||
"start:browser-backend": "yarn workspace loot-core watch:browser",
|
||||
"start:browser-backend": "yarn workspace @actual-app/core watch:browser",
|
||||
"start:browser-frontend": "yarn workspace @actual-app/web start:browser",
|
||||
"start:storybook": "yarn workspace @actual-app/components start:storybook",
|
||||
"build:browser-backend": "yarn workspace loot-core build:browser",
|
||||
"build:browser-backend": "yarn workspace @actual-app/core build:browser",
|
||||
"build:server": "yarn build:browser && yarn workspace @actual-app/sync-server build",
|
||||
"build:browser": "./bin/package-browser",
|
||||
"build:desktop": "./bin/package-electron",
|
||||
@@ -53,32 +53,35 @@
|
||||
"vrt": "yarn workspace @actual-app/web run vrt",
|
||||
"vrt:docker": "./bin/run-vrt",
|
||||
"rebuild-electron": "./node_modules/.bin/electron-rebuild -m ./packages/loot-core",
|
||||
"rebuild-node": "yarn workspace loot-core rebuild",
|
||||
"rebuild-node": "yarn workspace @actual-app/core rebuild",
|
||||
"lint": "oxfmt --check . && oxlint --type-aware --quiet",
|
||||
"lint:fix": "oxfmt . && oxlint --fix --type-aware --quiet",
|
||||
"install:server": "yarn workspaces focus @actual-app/sync-server --production",
|
||||
"typecheck": "tsc -b && tsc -p tsconfig.root.json --noEmit && lage typecheck",
|
||||
"constraints": "yarn constraints",
|
||||
"typecheck": "tsgo -p tsconfig.root.json --noEmit && lage typecheck",
|
||||
"jq": "./node_modules/node-jq/bin/jq",
|
||||
"prepare": "husky"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@octokit/rest": "^22.0.1",
|
||||
"@types/node": "^22.19.10",
|
||||
"@types/node": "^22.19.15",
|
||||
"@types/prompts": "^2.4.9",
|
||||
"baseline-browser-mapping": "^2.9.19",
|
||||
"@typescript/native-preview": "^7.0.0-dev.20260309.1",
|
||||
"@yarnpkg/types": "^4.0.1",
|
||||
"baseline-browser-mapping": "^2.10.0",
|
||||
"cross-env": "^10.1.0",
|
||||
"eslint": "^9.39.2",
|
||||
"eslint-plugin-perfectionist": "^4.15.1",
|
||||
"eslint": "^9.39.3",
|
||||
"eslint-plugin-perfectionist": "^5.6.0",
|
||||
"eslint-plugin-typescript-paths": "^0.0.33",
|
||||
"html-to-image": "^1.11.13",
|
||||
"husky": "^9.1.7",
|
||||
"lage": "^2.14.17",
|
||||
"lint-staged": "^16.2.7",
|
||||
"minimatch": "^10.1.2",
|
||||
"lage": "^2.14.19",
|
||||
"lint-staged": "^16.3.2",
|
||||
"minimatch": "^10.2.4",
|
||||
"node-jq": "^6.3.1",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"oxfmt": "^0.32.0",
|
||||
"oxlint": "^1.47.0",
|
||||
"oxlint": "^1.51.0",
|
||||
"oxlint-tsgolint": "^0.13.0",
|
||||
"p-limit": "^7.3.0",
|
||||
"prompts": "^2.4.2",
|
||||
@@ -87,6 +90,7 @@
|
||||
"typescript": "^5.9.3"
|
||||
},
|
||||
"resolutions": {
|
||||
"adm-zip": "patch:adm-zip@npm%3A0.5.16#~/.yarn/patches/adm-zip-npm-0.5.16-4556fea098.patch",
|
||||
"rollup": "4.40.1",
|
||||
"socks": ">=2.8.3"
|
||||
},
|
||||
|
||||
@@ -3,8 +3,8 @@ import type {
|
||||
RequestInit as FetchInit,
|
||||
} from 'node-fetch';
|
||||
|
||||
import { init as initLootCore } from 'loot-core/server/main';
|
||||
import type { InitConfig, lib } from 'loot-core/server/main';
|
||||
import { init as initLootCore } from '@actual-app/core/server/main';
|
||||
import type { InitConfig, lib } from '@actual-app/core/server/main';
|
||||
|
||||
import { validateNodeVersion } from './validateNodeVersion';
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import * as path from 'path';
|
||||
|
||||
import { vi } from 'vitest';
|
||||
|
||||
import type { RuleEntity } from 'loot-core/types/models';
|
||||
import type { RuleEntity } from '@actual-app/core/types/models';
|
||||
|
||||
import * as api from './index';
|
||||
|
||||
@@ -896,6 +896,73 @@ describe('API CRUD operations', () => {
|
||||
);
|
||||
expect(transactions[0].notes).toBeNull();
|
||||
});
|
||||
|
||||
test('Transactions: reimportDeleted=false prevents reimporting deleted transactions', async () => {
|
||||
const accountId = await api.createAccount({ name: 'test-account' }, 0);
|
||||
|
||||
// Import a transaction
|
||||
const result1 = await api.importTransactions(accountId, [
|
||||
{
|
||||
date: '2023-11-03',
|
||||
imported_id: 'reimport-test-1',
|
||||
amount: 100,
|
||||
account: accountId,
|
||||
},
|
||||
]);
|
||||
expect(result1.added).toHaveLength(1);
|
||||
|
||||
// Delete the transaction
|
||||
await api.deleteTransaction(result1.added[0]);
|
||||
|
||||
// Reimport the same transaction with reimportDeleted=false
|
||||
const result2 = await api.importTransactions(
|
||||
accountId,
|
||||
[
|
||||
{
|
||||
date: '2023-11-03',
|
||||
imported_id: 'reimport-test-1',
|
||||
amount: 100,
|
||||
account: accountId,
|
||||
},
|
||||
],
|
||||
{ reimportDeleted: false },
|
||||
);
|
||||
|
||||
// Should match the deleted transaction and not create a new one
|
||||
expect(result2.added).toHaveLength(0);
|
||||
expect(result2.updated).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('Transactions: reimportDeleted=true reimports deleted transactions', async () => {
|
||||
const accountId = await api.createAccount({ name: 'test-account' }, 0);
|
||||
|
||||
// Import a transaction
|
||||
const result1 = await api.importTransactions(accountId, [
|
||||
{
|
||||
date: '2023-11-03',
|
||||
imported_id: 'reimport-test-2',
|
||||
amount: 200,
|
||||
account: accountId,
|
||||
},
|
||||
]);
|
||||
expect(result1.added).toHaveLength(1);
|
||||
|
||||
// Delete the transaction
|
||||
await api.deleteTransaction(result1.added[0]);
|
||||
|
||||
// Reimport the same transaction relying on reimportDeleted=true default
|
||||
const result2 = await api.importTransactions(accountId, [
|
||||
{
|
||||
date: '2023-11-03',
|
||||
imported_id: 'reimport-test-2',
|
||||
amount: 200,
|
||||
account: accountId,
|
||||
},
|
||||
]);
|
||||
|
||||
// Should create a new transaction since deleted ones are ignored
|
||||
expect(result2.added).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
//apis: createSchedule, getSchedules, updateSchedule, deleteSchedule
|
||||
|
||||
@@ -6,16 +6,16 @@ import type {
|
||||
APIPayeeEntity,
|
||||
APIScheduleEntity,
|
||||
APITagEntity,
|
||||
} from 'loot-core/server/api-models';
|
||||
import { lib } from 'loot-core/server/main';
|
||||
import type { Query } from 'loot-core/shared/query';
|
||||
import type { ImportTransactionsOpts } from 'loot-core/types/api-handlers';
|
||||
import type { Handlers } from 'loot-core/types/handlers';
|
||||
} from '@actual-app/core/server/api-models';
|
||||
import { lib } from '@actual-app/core/server/main';
|
||||
import type { Query } from '@actual-app/core/shared/query';
|
||||
import type { ImportTransactionsOpts } from '@actual-app/core/types/api-handlers';
|
||||
import type { Handlers } from '@actual-app/core/types/handlers';
|
||||
import type {
|
||||
ImportTransactionEntity,
|
||||
RuleEntity,
|
||||
TransactionEntity,
|
||||
} from 'loot-core/types/models';
|
||||
} from '@actual-app/core/types/models';
|
||||
|
||||
export { q } from './app/query';
|
||||
|
||||
|
||||
@@ -10,26 +10,26 @@
|
||||
"main": "dist/index.js",
|
||||
"types": "@types/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "yarn workspace loot-core exec tsc && vite build && node scripts/inline-loot-core-types.mjs",
|
||||
"build": "vite build",
|
||||
"test": "vitest --run",
|
||||
"typecheck": "tsc --noEmit && tsc-strict"
|
||||
"typecheck": "tsgo -b && tsc-strict"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actual-app/crdt": "workspace:^",
|
||||
"@actual-app/core": "workspace:*",
|
||||
"@actual-app/crdt": "workspace:*",
|
||||
"better-sqlite3": "^12.6.2",
|
||||
"compare-versions": "^6.1.1",
|
||||
"loot-core": "workspace:^",
|
||||
"node-fetch": "^3.3.2",
|
||||
"uuid": "^13.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"rollup-plugin-visualizer": "^6.0.5",
|
||||
"typescript": "^5.9.3",
|
||||
"@typescript/native-preview": "^7.0.0-dev.20260309.1",
|
||||
"rollup-plugin-visualizer": "^6.0.11",
|
||||
"typescript-strict-plugin": "^2.4.4",
|
||||
"vite": "^7.3.1",
|
||||
"vite": "^8.0.0",
|
||||
"vite-plugin-dts": "^4.5.4",
|
||||
"vite-plugin-peggy-loader": "^2.0.1",
|
||||
"vitest": "^4.0.18"
|
||||
"vitest": "^4.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
/**
|
||||
* Post-build script: copies loot-core declaration tree into @types/loot-core
|
||||
* and rewrites index.d.ts to reference it so the published package is self-contained.
|
||||
* Run after vite build; requires loot-core declarations (yarn workspace loot-core exec tsc).
|
||||
*/
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const apiRoot = path.resolve(__dirname, '..');
|
||||
const typesDir = path.join(apiRoot, '@types');
|
||||
const indexDts = path.join(typesDir, 'index.d.ts');
|
||||
const lootCoreDeclRoot = path.resolve(apiRoot, '../loot-core/lib-dist/decl');
|
||||
const lootCoreDeclSrc = path.join(lootCoreDeclRoot, 'src');
|
||||
const lootCoreDeclTypings = path.join(lootCoreDeclRoot, 'typings');
|
||||
const lootCoreTypesDir = path.join(typesDir, 'loot-core');
|
||||
|
||||
function main() {
|
||||
if (!fs.existsSync(indexDts)) {
|
||||
console.error('Missing @types/index.d.ts; run vite build first.');
|
||||
process.exit(1);
|
||||
}
|
||||
if (!fs.existsSync(lootCoreDeclSrc)) {
|
||||
console.error(
|
||||
'Missing loot-core declarations; run: yarn workspace loot-core exec tsc',
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Remove existing loot-core output (dir or legacy single file)
|
||||
if (fs.existsSync(lootCoreTypesDir)) {
|
||||
fs.rmSync(lootCoreTypesDir, { recursive: true });
|
||||
}
|
||||
const legacyDts = path.join(typesDir, 'loot-core.d.ts');
|
||||
if (fs.existsSync(legacyDts)) {
|
||||
fs.rmSync(legacyDts);
|
||||
}
|
||||
|
||||
// Copy declaration tree: src (main exports) plus emitted typings so no declarations are dropped
|
||||
fs.cpSync(lootCoreDeclSrc, lootCoreTypesDir, { recursive: true });
|
||||
if (fs.existsSync(lootCoreDeclTypings)) {
|
||||
fs.cpSync(lootCoreDeclTypings, path.join(lootCoreTypesDir, 'typings'), {
|
||||
recursive: true,
|
||||
});
|
||||
}
|
||||
|
||||
// Rewrite index.d.ts: remove reference, point imports at local ./loot-core/
|
||||
let indexContent = fs.readFileSync(indexDts, 'utf8');
|
||||
indexContent = indexContent.replace(
|
||||
/\/\/\/ <reference path="\.\/loot-core\.d\.ts" \/>\n?/,
|
||||
'',
|
||||
);
|
||||
indexContent = indexContent
|
||||
.replace(/'loot-core\//g, "'./loot-core/")
|
||||
.replace(/"loot-core\//g, '"./loot-core/');
|
||||
fs.writeFileSync(indexDts, indexContent, 'utf8');
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -1,4 +1,4 @@
|
||||
import { lib } from 'loot-core/server/main';
|
||||
import { lib } from '@actual-app/core/server/main';
|
||||
|
||||
export const amountToInteger = lib.amountToInteger;
|
||||
export const integerToAmount = lib.integerToAmount;
|
||||
|
||||
@@ -81,12 +81,6 @@ export default defineConfig({
|
||||
],
|
||||
resolve: {
|
||||
extensions: ['.api.ts', '.js', '.ts', '.tsx', '.json'],
|
||||
alias: [
|
||||
{
|
||||
find: /^@actual-app\/crdt(\/.*)?$/,
|
||||
replacement: path.resolve(__dirname, '../crdt/src') + '$1',
|
||||
},
|
||||
],
|
||||
},
|
||||
test: {
|
||||
globals: true,
|
||||
|
||||
1
packages/ci-actions/.gitignore
vendored
Normal file
1
packages/ci-actions/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
dist/*
|
||||
44
packages/ci-actions/bin/get-next-package-version.js → packages/ci-actions/bin/get-next-package-version.ts
Executable file → Normal file
44
packages/ci-actions/bin/get-next-package-version.js → packages/ci-actions/bin/get-next-package-version.ts
Executable file → Normal file
@@ -2,13 +2,13 @@
|
||||
|
||||
// This script is used in GitHub Actions to get the next version based on the current package.json version.
|
||||
// It supports three types of versioning: nightly, hotfix, and monthly.
|
||||
|
||||
import fs from 'node:fs';
|
||||
import { parseArgs } from 'node:util';
|
||||
|
||||
import { getNextVersion } from '../src/versions/get-next-package-version.js';
|
||||
|
||||
const args = process.argv;
|
||||
import {
|
||||
getNextVersion,
|
||||
isValidVersionType,
|
||||
} from '../src/versions/get-next-package-version';
|
||||
|
||||
const options = {
|
||||
'package-json': {
|
||||
@@ -28,40 +28,53 @@ const options = {
|
||||
short: 'u',
|
||||
default: false,
|
||||
},
|
||||
};
|
||||
} as const;
|
||||
|
||||
function fail(message: string): never {
|
||||
console.error(message);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const { values } = parseArgs({
|
||||
args,
|
||||
options,
|
||||
allowPositionals: true,
|
||||
});
|
||||
|
||||
if (!values['package-json']) {
|
||||
console.error(
|
||||
const packageJsonPath = values['package-json'];
|
||||
if (!packageJsonPath) {
|
||||
fail(
|
||||
'Please specify the path to package.json using --package-json or -p option.',
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
const packageJsonPath = values['package-json'];
|
||||
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
||||
|
||||
if (!('version' in packageJson) || typeof packageJson.version !== 'string') {
|
||||
fail('The specified package.json does not contain a valid version field.');
|
||||
}
|
||||
|
||||
const currentVersion = packageJson.version;
|
||||
|
||||
const explicitVersion = values.version;
|
||||
let newVersion;
|
||||
|
||||
if (explicitVersion) {
|
||||
newVersion = explicitVersion;
|
||||
} else {
|
||||
const type = values.type;
|
||||
if (!type || !isValidVersionType(type)) {
|
||||
fail('Please specify the release type using --type or -t.');
|
||||
}
|
||||
|
||||
try {
|
||||
newVersion = getNextVersion({
|
||||
currentVersion,
|
||||
type: values.type,
|
||||
type,
|
||||
currentDate: new Date(),
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e.message);
|
||||
process.exit(1);
|
||||
} catch (error) {
|
||||
fail(error instanceof Error ? error.message : String(error));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,6 +89,5 @@ try {
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error:', error.message);
|
||||
process.exit(1);
|
||||
fail(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
||||
}
|
||||
8
packages/ci-actions/bin/tsx
Executable file
8
packages/ci-actions/bin/tsx
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
cd ../../
|
||||
|
||||
script="$1"
|
||||
shift
|
||||
exec node --import=extensionless/register --experimental-strip-types packages/ci-actions/"$script" "$@"
|
||||
@@ -3,14 +3,14 @@
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"tsx": "node --import=extensionless/register --experimental-strip-types",
|
||||
"tsx": "bin/tsx",
|
||||
"test": "vitest --run",
|
||||
"typecheck": "tsc --noEmit"
|
||||
"typecheck": "tsgo -b"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript/native-preview": "^7.0.0-dev.20260309.1",
|
||||
"extensionless": "^2.0.6",
|
||||
"typescript": "^5.9.3",
|
||||
"vitest": "^4.0.18"
|
||||
"vitest": "^4.1.0"
|
||||
},
|
||||
"extensionless": {
|
||||
"lookFor": [
|
||||
|
||||
@@ -77,7 +77,7 @@ describe('getNextVersion (lib)', () => {
|
||||
expect(() =>
|
||||
getNextVersion({
|
||||
currentVersion: '25.8.4',
|
||||
type: 'unknown',
|
||||
type: 'unknown' as never,
|
||||
currentDate: new Date('2025-08-10'),
|
||||
}),
|
||||
).toThrow(/Invalid type/);
|
||||
@@ -1,35 +1,69 @@
|
||||
function parseVersion(version) {
|
||||
export const versionTypeArray = [
|
||||
'auto',
|
||||
'hotfix',
|
||||
'monthly',
|
||||
'nightly',
|
||||
] as const;
|
||||
export type VersionType = (typeof versionTypeArray)[number];
|
||||
|
||||
type ParsedVersion = {
|
||||
versionYear: number;
|
||||
versionMonth: number;
|
||||
versionHotfix: number;
|
||||
};
|
||||
|
||||
type GetNextVersionOptions = {
|
||||
currentVersion: string;
|
||||
type: VersionType;
|
||||
currentDate?: Date;
|
||||
};
|
||||
|
||||
function parseVersion(version: string): ParsedVersion {
|
||||
const [y, m, p] = version.split('.');
|
||||
return {
|
||||
versionYear: parseInt(y, 10),
|
||||
versionMonth: parseInt(m, 10),
|
||||
versionHotfix: parseInt(p, 10),
|
||||
versionYear: Number.parseInt(y, 10),
|
||||
versionMonth: Number.parseInt(m, 10),
|
||||
versionHotfix: Number.parseInt(p, 10),
|
||||
};
|
||||
}
|
||||
|
||||
function computeNextMonth(versionYear, versionMonth) {
|
||||
// Create date and add 1 month
|
||||
const versionDate = new Date(2000 + versionYear, versionMonth - 1, 1); // month is 0-indexed
|
||||
function computeNextMonth(versionYear: number, versionMonth: number) {
|
||||
const versionDate = new Date(2000 + versionYear, versionMonth - 1, 1);
|
||||
const nextVersionMonthDate = new Date(
|
||||
versionDate.getFullYear(),
|
||||
versionDate.getMonth() + 1,
|
||||
1,
|
||||
);
|
||||
|
||||
// Format back to YY.M format
|
||||
const fullYear = nextVersionMonthDate.getFullYear();
|
||||
const nextVersionYear = fullYear.toString().slice(fullYear < 2100 ? -2 : -3);
|
||||
const nextVersionMonth = nextVersionMonthDate.getMonth() + 1; // Convert back to 1-indexed
|
||||
const nextVersionMonth = nextVersionMonthDate.getMonth() + 1;
|
||||
|
||||
return { nextVersionYear, nextVersionMonth };
|
||||
}
|
||||
|
||||
// Determine logical type from 'auto' based on the current date and version
|
||||
function resolveType(type, currentDate, versionYear, versionMonth) {
|
||||
if (type !== 'auto') return type;
|
||||
export function isValidVersionType(value: string): value is VersionType {
|
||||
return versionTypeArray.includes(value as VersionType);
|
||||
}
|
||||
|
||||
function resolveType(
|
||||
type: VersionType,
|
||||
currentDate: Date,
|
||||
versionYear: number,
|
||||
versionMonth: number,
|
||||
) {
|
||||
if (type !== 'auto') {
|
||||
return type;
|
||||
}
|
||||
|
||||
const inPatchMonth =
|
||||
currentDate.getFullYear() === 2000 + versionYear &&
|
||||
currentDate.getMonth() + 1 === versionMonth;
|
||||
if (inPatchMonth && currentDate.getDate() <= 25) return 'hotfix';
|
||||
|
||||
if (inPatchMonth && currentDate.getDate() <= 25) {
|
||||
return 'hotfix';
|
||||
}
|
||||
|
||||
return 'monthly';
|
||||
}
|
||||
|
||||
@@ -37,7 +71,7 @@ export function getNextVersion({
|
||||
currentVersion,
|
||||
type,
|
||||
currentDate = new Date(),
|
||||
}) {
|
||||
}: GetNextVersionOptions) {
|
||||
const { versionYear, versionMonth, versionHotfix } =
|
||||
parseVersion(currentVersion);
|
||||
const { nextVersionYear, nextVersionMonth } = computeNextMonth(
|
||||
@@ -51,11 +85,10 @@ export function getNextVersion({
|
||||
versionMonth,
|
||||
);
|
||||
|
||||
// Format date stamp once for nightly
|
||||
const currentDateString = currentDate
|
||||
.toISOString()
|
||||
.split('T')[0]
|
||||
.replaceAll('-', '');
|
||||
.replace(/-/g, '');
|
||||
|
||||
switch (resolvedType) {
|
||||
case 'nightly':
|
||||
@@ -66,7 +99,7 @@ export function getNextVersion({
|
||||
return `${nextVersionYear}.${nextVersionMonth}.0`;
|
||||
default:
|
||||
throw new Error(
|
||||
'Invalid type specified. Use "auto", "nightly", "hotfix", or "monthly".',
|
||||
`Invalid type ${String(resolvedType satisfies never)} specified. Use "auto", "nightly", "hotfix", or "monthly".`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,14 @@
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"lib": [],
|
||||
"module": "nodenext",
|
||||
"moduleResolution": "nodenext",
|
||||
"module": "es2022",
|
||||
"moduleResolution": "bundler",
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"types": ["node"],
|
||||
"outDir": "dist",
|
||||
"rootDir": "."
|
||||
"rootDir": ".",
|
||||
"composite": true
|
||||
},
|
||||
"include": ["src/**/*", "bin/**/*"],
|
||||
"exclude": ["node_modules"]
|
||||
|
||||
@@ -2,7 +2,7 @@ import { dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
import type { StorybookConfig } from '@storybook/react-vite';
|
||||
import viteTsconfigPaths from 'vite-tsconfig-paths';
|
||||
import react from '@vitejs/plugin-react';
|
||||
|
||||
/**
|
||||
* This function is used to resolve the absolute path of a package.
|
||||
@@ -32,11 +32,9 @@ const config: StorybookConfig = {
|
||||
const { mergeConfig } = await import('vite');
|
||||
|
||||
return mergeConfig(config, {
|
||||
// Telling Vite how to resolve path aliases
|
||||
plugins: [viteTsconfigPaths({ root: '../..' })],
|
||||
esbuild: {
|
||||
// Needed to handle JSX in .ts/.tsx files
|
||||
jsx: 'automatic',
|
||||
plugins: [react()],
|
||||
resolve: {
|
||||
tsconfigPaths: true,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
@@ -5,7 +5,6 @@ import type { Preview } from '@storybook/react-vite';
|
||||
// Not ideal to import from desktop-client, but we need a source of truth for theme variables
|
||||
// TODO: this needs refactoring
|
||||
import * as darkTheme from '../../desktop-client/src/style/themes/dark';
|
||||
import * as developmentTheme from '../../desktop-client/src/style/themes/development';
|
||||
import * as lightTheme from '../../desktop-client/src/style/themes/light';
|
||||
import * as midnightTheme from '../../desktop-client/src/style/themes/midnight';
|
||||
|
||||
@@ -13,7 +12,6 @@ const THEMES = {
|
||||
light: lightTheme,
|
||||
dark: darkTheme,
|
||||
midnight: midnightTheme,
|
||||
development: developmentTheme,
|
||||
} as const;
|
||||
|
||||
type ThemeName = keyof typeof THEMES;
|
||||
@@ -64,7 +62,6 @@ const preview: Preview = {
|
||||
{ value: 'light', title: 'Light' },
|
||||
{ value: 'dark', title: 'Dark' },
|
||||
{ value: 'midnight', title: 'Midnight' },
|
||||
{ value: 'development', title: 'Development' },
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
"test:web": "ENV=web vitest --run -c vitest.web.config.ts",
|
||||
"start:storybook": "storybook dev -p 6006",
|
||||
"build:storybook": "storybook build",
|
||||
"typecheck": "tsc --noEmit"
|
||||
"typecheck": "tsgo -b"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/css": "^11.13.5",
|
||||
@@ -48,18 +48,20 @@
|
||||
"usehooks-ts": "^3.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@chromatic-com/storybook": "^5.0.0",
|
||||
"@storybook/addon-a11y": "^10.2.7",
|
||||
"@storybook/addon-docs": "^10.2.7",
|
||||
"@storybook/react-vite": "^10.2.7",
|
||||
"@chromatic-com/storybook": "^5.0.1",
|
||||
"@storybook/addon-a11y": "^10.2.16",
|
||||
"@storybook/addon-docs": "^10.2.16",
|
||||
"@storybook/react-vite": "^10.2.16",
|
||||
"@svgr/cli": "^8.1.0",
|
||||
"@types/react": "^19.2.5",
|
||||
"eslint-plugin-storybook": "^10.2.7",
|
||||
"@types/react": "^19.2.14",
|
||||
"@typescript/native-preview": "^7.0.0-dev.20260309.1",
|
||||
"@vitejs/plugin-react": "^6.0.0",
|
||||
"eslint-plugin-storybook": "^10.2.16",
|
||||
"react": "19.2.4",
|
||||
"react-dom": "19.2.4",
|
||||
"storybook": "^10.2.7",
|
||||
"vite-tsconfig-paths": "^5.1.4",
|
||||
"vitest": "^4.0.18"
|
||||
"storybook": "^10.2.16",
|
||||
"vite": "^8.0.0",
|
||||
"vitest": "^4.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=19.2",
|
||||
|
||||
@@ -4,7 +4,7 @@ import { css, cx } from '@emotion/css';
|
||||
|
||||
import type { CSSProperties } from './styles';
|
||||
|
||||
type BlockProps = HTMLProps<HTMLDivElement> & {
|
||||
type BlockProps = Omit<HTMLProps<HTMLDivElement>, 'style'> & {
|
||||
innerRef?: Ref<HTMLDivElement>;
|
||||
style?: CSSProperties;
|
||||
};
|
||||
|
||||
@@ -16,8 +16,8 @@ import { View } from './View';
|
||||
|
||||
const MenuLine: unique symbol = Symbol('menu-line');
|
||||
const MenuLabel: unique symbol = Symbol('menu-label');
|
||||
Menu.line = MenuLine;
|
||||
Menu.label = MenuLabel;
|
||||
Menu.line = MenuLine as typeof MenuLine;
|
||||
Menu.label = MenuLabel as typeof MenuLabel;
|
||||
|
||||
type KeybindingProps = {
|
||||
keyName: ReactNode;
|
||||
|
||||
@@ -4,7 +4,7 @@ import { css } from '@emotion/css';
|
||||
|
||||
import type { CSSProperties } from './styles';
|
||||
|
||||
type ParagraphProps = HTMLProps<HTMLDivElement> & {
|
||||
type ParagraphProps = Omit<HTMLProps<HTMLDivElement>, 'style'> & {
|
||||
style?: CSSProperties;
|
||||
isLast?: boolean;
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@ import { css, cx } from '@emotion/css';
|
||||
|
||||
import type { CSSProperties } from './styles';
|
||||
|
||||
type TextProps = HTMLProps<HTMLSpanElement> & {
|
||||
type TextProps = Omit<HTMLProps<HTMLSpanElement>, 'style'> & {
|
||||
innerRef?: Ref<HTMLSpanElement>;
|
||||
className?: string;
|
||||
children?: ReactNode;
|
||||
|
||||
@@ -5,7 +5,7 @@ import { css, cx } from '@emotion/css';
|
||||
|
||||
import type { CSSProperties } from './styles';
|
||||
|
||||
type ViewProps = HTMLProps<HTMLDivElement> & {
|
||||
type ViewProps = Omit<HTMLProps<HTMLDivElement>, 'style'> & {
|
||||
className?: string;
|
||||
style?: CSSProperties;
|
||||
nativeStyle?: CSSProperties;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import path from 'path';
|
||||
|
||||
import react from '@vitejs/plugin-react';
|
||||
import peggyLoader from 'vite-plugin-peggy-loader';
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
@@ -23,13 +24,7 @@ export default defineConfig({
|
||||
maxWorkers: 2,
|
||||
},
|
||||
resolve: {
|
||||
alias: [
|
||||
{
|
||||
find: /^@actual-app\/crdt(\/.*)?$/,
|
||||
replacement: path.resolve('../../../crdt/src$1'),
|
||||
},
|
||||
],
|
||||
extensions: resolveExtensions,
|
||||
},
|
||||
plugins: [peggyLoader()],
|
||||
plugins: [react(), peggyLoader()],
|
||||
});
|
||||
|
||||
@@ -8,12 +8,23 @@
|
||||
],
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"publishConfig": {
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
}
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build:node": "tsc",
|
||||
"build:node": "tsgo",
|
||||
"proto:generate": "./bin/generate-proto",
|
||||
"build": "rm -rf dist && yarn run build:node",
|
||||
"test": "vitest --run",
|
||||
"typecheck": "tsc --noEmit"
|
||||
"typecheck": "tsgo -b"
|
||||
},
|
||||
"dependencies": {
|
||||
"google-protobuf": "^3.21.4",
|
||||
@@ -22,9 +33,9 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/google-protobuf": "3.15.12",
|
||||
"@typescript/native-preview": "^7.0.0-dev.20260309.1",
|
||||
"protoc-gen-js": "3.21.4-4",
|
||||
"ts-protoc-gen": "0.15.0",
|
||||
"typescript": "^5.9.3",
|
||||
"vitest": "^4.0.18"
|
||||
"vitest": "^4.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ export class ConfigurationPage {
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error(`Unrecognized import type: ${type}`);
|
||||
throw new Error(`Unrecognized import type: ${String(type)}`);
|
||||
}
|
||||
|
||||
const fileChooser = await fileChooserPromise;
|
||||
|
||||
@@ -39,7 +39,7 @@ export class CustomReportPage {
|
||||
.click();
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unrecognized mode: ${mode}`);
|
||||
throw new Error(`Unrecognized mode: ${String(mode)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 120 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 122 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 121 KiB After Width: | Height: | Size: 121 KiB |
@@ -5,14 +5,6 @@
|
||||
"files": [
|
||||
"build"
|
||||
],
|
||||
"imports": {
|
||||
"#*": [
|
||||
"./src/*.ts",
|
||||
"./src/*.tsx",
|
||||
"./src/*/index.ts",
|
||||
"./src/*/index.tsx"
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"start": "cross-env PORT=3001 vite",
|
||||
"start:browser": "cross-env ./bin/watch-browser",
|
||||
@@ -24,13 +16,15 @@
|
||||
"e2e": "npx playwright test --browser=chromium",
|
||||
"vrt": "cross-env VRT=true npx playwright test --browser=chromium",
|
||||
"playwright": "playwright",
|
||||
"typecheck": "tsc --noEmit && tsc-strict"
|
||||
"typecheck": "tsgo -b && tsc-strict"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@actual-app/components": "workspace:*",
|
||||
"@codemirror/autocomplete": "^6.20.0",
|
||||
"@codemirror/lang-javascript": "^6.2.4",
|
||||
"@codemirror/language": "^6.12.1",
|
||||
"@actual-app/core": "workspace:*",
|
||||
"@babel/core": "^7.29.0",
|
||||
"@codemirror/autocomplete": "^6.20.1",
|
||||
"@codemirror/lang-javascript": "^6.2.5",
|
||||
"@codemirror/language": "^6.12.2",
|
||||
"@codemirror/state": "^6.5.4",
|
||||
"@codemirror/view": "^6.38.7",
|
||||
"@emotion/css": "^11.13.5",
|
||||
@@ -39,10 +33,11 @@
|
||||
"@juggle/resize-observer": "^3.4.0",
|
||||
"@lezer/highlight": "^1.2.3",
|
||||
"@playwright/test": "1.58.2",
|
||||
"@rolldown/plugin-babel": "~0.1.8",
|
||||
"@rollup/plugin-inject": "^5.0.5",
|
||||
"@swc/core": "^1.15.11",
|
||||
"@swc/helpers": "^0.5.18",
|
||||
"@tanstack/react-query": "^5.90.20",
|
||||
"@swc/core": "^1.15.18",
|
||||
"@swc/helpers": "^0.5.19",
|
||||
"@tanstack/react-query": "^5.90.21",
|
||||
"@testing-library/dom": "10.4.1",
|
||||
"@testing-library/jest-dom": "^6.9.1",
|
||||
"@testing-library/react": "16.3.2",
|
||||
@@ -50,26 +45,26 @@
|
||||
"@types/lodash": "^4",
|
||||
"@types/pikaday": "^1.7.10",
|
||||
"@types/promise-retry": "^1.1.6",
|
||||
"@types/react": "^19.2.5",
|
||||
"@types/react": "^19.2.14",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@types/react-modal": "^3.16.3",
|
||||
"@uiw/react-codemirror": "^4.25.4",
|
||||
"@typescript/native-preview": "^7.0.0-dev.20260309.1",
|
||||
"@uiw/react-codemirror": "^4.25.7",
|
||||
"@use-gesture/react": "^10.3.1",
|
||||
"@vitejs/plugin-basic-ssl": "^2.1.4",
|
||||
"@vitejs/plugin-react": "^5.1.3",
|
||||
"@vitejs/plugin-basic-ssl": "^2.2.0",
|
||||
"@vitejs/plugin-react": "^6.0.0",
|
||||
"auto-text-size": "^0.2.3",
|
||||
"babel-plugin-react-compiler": "^1.0.0",
|
||||
"cmdk": "^1.1.1",
|
||||
"cross-env": "^10.1.0",
|
||||
"date-fns": "^4.1.0",
|
||||
"downshift": "9.0.10",
|
||||
"hyperformula": "^3.1.1",
|
||||
"i18next": "^25.8.4",
|
||||
"i18next-parser": "^9.3.0",
|
||||
"downshift": "9.3.2",
|
||||
"hyperformula": "^3.2.0",
|
||||
"i18next": "^25.8.14",
|
||||
"i18next-parser": "^9.4.0",
|
||||
"i18next-resources-to-backend": "^1.2.1",
|
||||
"jsdom": "^27.4.0",
|
||||
"lodash": "^4.17.23",
|
||||
"loot-core": "workspace:*",
|
||||
"mdast-util-newline-to-break": "^2.0.0",
|
||||
"memoize-one": "^6.0.0",
|
||||
"pikaday": "1.8.2",
|
||||
@@ -85,27 +80,26 @@
|
||||
"react-error-boundary": "^6.0.3",
|
||||
"react-grid-layout": "^2.2.2",
|
||||
"react-hotkeys-hook": "^5.2.4",
|
||||
"react-i18next": "^16.5.4",
|
||||
"react-i18next": "^16.5.6",
|
||||
"react-markdown": "^10.1.0",
|
||||
"react-modal": "3.16.3",
|
||||
"react-redux": "^9.2.0",
|
||||
"react-router": "7.13.0",
|
||||
"react-router": "7.13.1",
|
||||
"react-simple-pull-to-refresh": "^1.3.4",
|
||||
"react-spring": "10.0.0",
|
||||
"react-spring": "^10.0.3",
|
||||
"react-swipeable": "^7.0.2",
|
||||
"react-virtualized-auto-sizer": "^2.0.2",
|
||||
"react-virtualized-auto-sizer": "^2.0.3",
|
||||
"recharts": "^3.7.0",
|
||||
"rehype-external-links": "^3.0.0",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"rollup-plugin-visualizer": "^6.0.5",
|
||||
"rollup-plugin-visualizer": "^6.0.11",
|
||||
"sass": "^1.97.3",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-strict-plugin": "^2.4.4",
|
||||
"usehooks-ts": "^3.1.1",
|
||||
"uuid": "^13.0.0",
|
||||
"vite": "^7.3.1",
|
||||
"vite": "^8.0.0",
|
||||
"vite-plugin-pwa": "^1.2.0",
|
||||
"vitest": "^4.0.18",
|
||||
"vitest": "^4.1.0",
|
||||
"xml2js": "^0.6.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import memoizeOne from 'memoize-one';
|
||||
import { groupById } from 'loot-core/shared/util';
|
||||
import type { AccountEntity } from 'loot-core/types/models';
|
||||
|
||||
import { resetApp } from '#app/appSlice';
|
||||
import { resetApp } from '@desktop-client/app/appSlice';
|
||||
|
||||
const sliceName = 'account';
|
||||
|
||||
|
||||
@@ -23,13 +23,13 @@ import {
|
||||
} from './accountsSlice';
|
||||
import { accountQueries } from './queries';
|
||||
|
||||
import { sync } from '#app/appSlice';
|
||||
import { useAccounts } from '#hooks/useAccounts';
|
||||
import { addNotification } from '#notifications/notificationsSlice';
|
||||
import { payeeQueries } from '#payees';
|
||||
import { useDispatch, useStore } from '#redux';
|
||||
import type { AppDispatch } from '#redux/store';
|
||||
import { setNewTransactions } from '#transactions/transactionsSlice';
|
||||
import { sync } from '@desktop-client/app/appSlice';
|
||||
import { useAccounts } from '@desktop-client/hooks/useAccounts';
|
||||
import { addNotification } from '@desktop-client/notifications/notificationsSlice';
|
||||
import { payeeQueries } from '@desktop-client/payees';
|
||||
import { useDispatch, useStore } from '@desktop-client/redux';
|
||||
import type { AppDispatch } from '@desktop-client/redux/store';
|
||||
import { setNewTransactions } from '@desktop-client/transactions/transactionsSlice';
|
||||
|
||||
const invalidateQueries = (queryClient: QueryClient, queryKey?: QueryKey) => {
|
||||
void queryClient.invalidateQueries({
|
||||
@@ -207,6 +207,7 @@ export function useMoveAccountMutation() {
|
||||
type ImportPreviewTransactionsPayload = {
|
||||
accountId: string;
|
||||
transactions: TransactionEntity[];
|
||||
reimportDeleted?: boolean;
|
||||
};
|
||||
|
||||
export function useImportPreviewTransactionsMutation() {
|
||||
@@ -218,6 +219,7 @@ export function useImportPreviewTransactionsMutation() {
|
||||
mutationFn: async ({
|
||||
accountId,
|
||||
transactions,
|
||||
reimportDeleted,
|
||||
}: ImportPreviewTransactionsPayload) => {
|
||||
const { errors = [], updatedPreview } = await send(
|
||||
'transactions-import',
|
||||
@@ -225,6 +227,7 @@ export function useImportPreviewTransactionsMutation() {
|
||||
accountId,
|
||||
transactions,
|
||||
isPreview: true,
|
||||
opts: reimportDeleted !== undefined ? { reimportDeleted } : undefined,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -259,6 +262,7 @@ type ImportTransactionsPayload = {
|
||||
accountId: string;
|
||||
transactions: TransactionEntity[];
|
||||
reconcile: boolean;
|
||||
reimportDeleted?: boolean;
|
||||
};
|
||||
|
||||
export function useImportTransactionsMutation() {
|
||||
@@ -271,6 +275,7 @@ export function useImportTransactionsMutation() {
|
||||
accountId,
|
||||
transactions,
|
||||
reconcile,
|
||||
reimportDeleted,
|
||||
}: ImportTransactionsPayload) => {
|
||||
if (!reconcile) {
|
||||
await send('api/transactions-add', {
|
||||
@@ -289,6 +294,7 @@ export function useImportTransactionsMutation() {
|
||||
accountId,
|
||||
transactions,
|
||||
isPreview: false,
|
||||
opts: reimportDeleted !== undefined ? { reimportDeleted } : undefined,
|
||||
});
|
||||
|
||||
errors.forEach(error => {
|
||||
|
||||
@@ -5,10 +5,10 @@ import { send } from 'loot-core/platform/client/connection';
|
||||
import { getUploadError } from 'loot-core/shared/errors';
|
||||
import type { AtLeastOne } from 'loot-core/types/util';
|
||||
|
||||
import { pushModal } from '#modals/modalsSlice';
|
||||
import { loadPrefs } from '#prefs/prefsSlice';
|
||||
import { createAppAsyncThunk } from '#redux';
|
||||
import { getIsOutdated, getLatestVersion } from '#util/versions';
|
||||
import { pushModal } from '@desktop-client/modals/modalsSlice';
|
||||
import { loadPrefs } from '@desktop-client/prefs/prefsSlice';
|
||||
import { createAppAsyncThunk } from '@desktop-client/redux';
|
||||
import { getIsOutdated, getLatestVersion } from '@desktop-client/util/versions';
|
||||
|
||||
const sliceName = 'app';
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@ import type { ReactNode } from 'react';
|
||||
|
||||
import type { Permissions } from './types';
|
||||
|
||||
import { useServerURL } from '#components/ServerContext';
|
||||
import { useSelector } from '#redux';
|
||||
import { useServerURL } from '@desktop-client/components/ServerContext';
|
||||
import { useSelector } from '@desktop-client/redux';
|
||||
|
||||
type AuthContextType = {
|
||||
hasPermission: (permission?: Permissions) => boolean;
|
||||
|
||||
@@ -9,8 +9,8 @@ import type { RemoteFile, SyncedLocalFile } from 'loot-core/types/file';
|
||||
import { useAuth } from './AuthProvider';
|
||||
import type { Permissions } from './types';
|
||||
|
||||
import { useMetadataPref } from '#hooks/useMetadataPref';
|
||||
import { useSelector } from '#redux';
|
||||
import { useMetadataPref } from '@desktop-client/hooks/useMetadataPref';
|
||||
import { useSelector } from '@desktop-client/redux';
|
||||
|
||||
type ProtectedRouteProps = {
|
||||
permission: Permissions;
|
||||
|
||||
@@ -6,6 +6,8 @@ import * as Platform from 'loot-core/shared/platform';
|
||||
// oxlint-disable-next-line typescript-paths/absolute-parent-import
|
||||
import packageJson from '../package.json';
|
||||
|
||||
import SharedBrowserServerWorker from './shared-browser-server.ts?sharedworker';
|
||||
|
||||
const backendWorkerUrl = new URL('./browser-server.js', import.meta.url);
|
||||
|
||||
// This file installs global variables that the app expects.
|
||||
@@ -21,9 +23,235 @@ const ACTUAL_VERSION = Platform.isPlaywright
|
||||
: packageJson.version;
|
||||
|
||||
// *** Start the backend ***
|
||||
|
||||
let worker = null;
|
||||
// The regular Worker running the backend, created only on the leader tab
|
||||
let localBackendWorker = null;
|
||||
|
||||
/**
|
||||
* WorkerBridge wraps a SharedWorker port and presents a Worker-like interface
|
||||
* (onmessage, postMessage, addEventListener, start) to the connection layer.
|
||||
*
|
||||
* The SharedWorker coordinator assigns each tab a role per budget:
|
||||
* - LEADER: this tab runs the backend in a dedicated Worker
|
||||
* - FOLLOWER: this tab routes messages through the SharedWorker to the leader
|
||||
*
|
||||
* Multiple budgets can be open simultaneously — each has its own leader.
|
||||
*/
|
||||
class WorkerBridge {
|
||||
constructor(sharedPort) {
|
||||
this._sharedPort = sharedPort;
|
||||
this._onmessage = null;
|
||||
this._listeners = [];
|
||||
this._started = false;
|
||||
|
||||
// Listen for all messages from the SharedWorker port
|
||||
sharedPort.addEventListener('message', e => this._onSharedMessage(e));
|
||||
}
|
||||
|
||||
set onmessage(handler) {
|
||||
this._onmessage = handler;
|
||||
// Setting onmessage on a real MessagePort implicitly starts it.
|
||||
// We need to do this explicitly on the underlying port.
|
||||
if (!this._started) {
|
||||
this._started = true;
|
||||
this._sharedPort.start();
|
||||
}
|
||||
}
|
||||
|
||||
get onmessage() {
|
||||
return this._onmessage;
|
||||
}
|
||||
|
||||
postMessage(msg) {
|
||||
// All messages go through the SharedWorker for coordination.
|
||||
// The SharedWorker forwards to the leader's Worker via __to-worker.
|
||||
this._sharedPort.postMessage(msg);
|
||||
}
|
||||
|
||||
addEventListener(type, handler) {
|
||||
this._listeners.push({ type, handler });
|
||||
}
|
||||
|
||||
start() {
|
||||
if (!this._started) {
|
||||
this._started = true;
|
||||
this._sharedPort.start();
|
||||
}
|
||||
}
|
||||
|
||||
_dispatch(event) {
|
||||
if (this._onmessage) this._onmessage(event);
|
||||
for (const { type, handler } of this._listeners) {
|
||||
if (type === 'message') handler(event);
|
||||
}
|
||||
}
|
||||
|
||||
_onSharedMessage(event) {
|
||||
const msg = event.data;
|
||||
|
||||
// Elected as leader: create the real backend Worker on this tab
|
||||
if (msg && msg.type === '__become-leader') {
|
||||
this._createLocalWorker(msg.initMsg, msg.budgetToRestore, msg.pendingMsg);
|
||||
return;
|
||||
}
|
||||
|
||||
// Forward requests from SharedWorker to our local Worker
|
||||
if (msg && msg.type === '__to-worker') {
|
||||
if (localBackendWorker) {
|
||||
localBackendWorker.postMessage(msg.msg);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Leadership transfer: this tab is closing the budget but other tabs
|
||||
// still need it. Terminate our Worker (don't actually close-budget on
|
||||
// the backend) and dispatch a synthetic reply so the UI navigates to
|
||||
// show-budgets normally.
|
||||
if (msg && msg.type === '__close-and-transfer') {
|
||||
console.log('[WorkerBridge] Leadership transferred — terminating Worker');
|
||||
if (localBackendWorker) {
|
||||
localBackendWorker.terminate();
|
||||
localBackendWorker = null;
|
||||
}
|
||||
// Only dispatch a synthetic reply if there's an actual close-budget
|
||||
// request to complete. When requestId is null the eviction was
|
||||
// triggered externally (e.g. another tab deleted this budget).
|
||||
if (msg.requestId) {
|
||||
this._dispatch({
|
||||
data: { type: 'reply', id: msg.requestId, data: {} },
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Role change notification
|
||||
if (msg && msg.type === '__role-change') {
|
||||
console.log(
|
||||
`[WorkerBridge] Role: ${msg.role}${msg.budgetId ? ` (budget: ${msg.budgetId})` : ''}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Surface SharedWorker console output in this tab's DevTools
|
||||
if (msg && msg.type === '__shared-worker-console') {
|
||||
const method = console[msg.level] || console.log;
|
||||
method(...msg.args);
|
||||
return;
|
||||
}
|
||||
|
||||
// Respond to heartbeat pings
|
||||
if (msg && msg.type === '__heartbeat-ping') {
|
||||
this._sharedPort.postMessage({ type: '__heartbeat-pong' });
|
||||
return;
|
||||
}
|
||||
|
||||
// Everything else goes to the connection layer
|
||||
this._dispatch(event);
|
||||
}
|
||||
|
||||
_createLocalWorker(initMsg, budgetToRestore, pendingMsg) {
|
||||
if (localBackendWorker) {
|
||||
localBackendWorker.terminate();
|
||||
}
|
||||
localBackendWorker = new Worker(backendWorkerUrl);
|
||||
initSQLBackend(localBackendWorker);
|
||||
|
||||
const sharedPort = this._sharedPort;
|
||||
localBackendWorker.onmessage = workerEvent => {
|
||||
const workerMsg = workerEvent.data;
|
||||
// absurd-sql internal messages are handled by initSQLBackend
|
||||
if (
|
||||
workerMsg &&
|
||||
workerMsg.type &&
|
||||
workerMsg.type.startsWith('__absurd:')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
// After the backend connects, automatically reload the budget that was
|
||||
// open before the leader left (e.g. page refresh). This lets other tabs
|
||||
// continue working without being sent to the budget list.
|
||||
if (workerMsg.type === 'connect') {
|
||||
if (budgetToRestore) {
|
||||
console.log(
|
||||
`[WorkerBridge] Backend connected, restoring budget "${budgetToRestore}"`,
|
||||
);
|
||||
const id = budgetToRestore;
|
||||
budgetToRestore = null;
|
||||
localBackendWorker.postMessage({
|
||||
id: '__restore-budget',
|
||||
name: 'load-budget',
|
||||
args: { id },
|
||||
catchErrors: true,
|
||||
});
|
||||
// Tell SharedWorker to track the restore request so
|
||||
// currentBudgetId gets updated when the reply arrives.
|
||||
sharedPort.postMessage({
|
||||
type: '__track-restore',
|
||||
requestId: '__restore-budget',
|
||||
budgetId: id,
|
||||
});
|
||||
} else if (pendingMsg) {
|
||||
const toSend = pendingMsg;
|
||||
pendingMsg = null;
|
||||
localBackendWorker.postMessage(toSend);
|
||||
}
|
||||
}
|
||||
sharedPort.postMessage({ type: '__from-worker', msg: workerMsg });
|
||||
};
|
||||
|
||||
localBackendWorker.postMessage(initMsg);
|
||||
}
|
||||
}
|
||||
|
||||
function createBackendWorker() {
|
||||
// Use SharedWorker as a coordinator for multi-tab, multi-budget support.
|
||||
// Each budget gets its own leader tab running a dedicated Worker. All other
|
||||
// tabs on the same budget are followers — their messages are routed through
|
||||
// the SharedWorker to the leader's Worker.
|
||||
// The SharedWorker never touches SharedArrayBuffer, so this works on all
|
||||
// platforms including iOS/Safari.
|
||||
if (typeof SharedWorker !== 'undefined' && !Platform.isPlaywright) {
|
||||
try {
|
||||
const sharedWorker = new SharedBrowserServerWorker({
|
||||
name: 'actual-backend',
|
||||
});
|
||||
|
||||
const sharedPort = sharedWorker.port;
|
||||
worker = new WorkerBridge(sharedPort);
|
||||
console.log('[WorkerBridge] Connected to SharedWorker coordinator');
|
||||
|
||||
// Don't call start() here. The port must remain un-started so that
|
||||
// messages (especially 'connect') are queued until connectWorker()
|
||||
// sets onmessage, which implicitly starts the port via the bridge.
|
||||
|
||||
if (window.SharedArrayBuffer) {
|
||||
localStorage.removeItem('SharedArrayBufferOverride');
|
||||
}
|
||||
|
||||
sharedPort.postMessage({
|
||||
type: 'init',
|
||||
version: ACTUAL_VERSION,
|
||||
isDev: IS_DEV,
|
||||
publicUrl: process.env.PUBLIC_URL,
|
||||
hash: process.env.REACT_APP_BACKEND_WORKER_HASH,
|
||||
isSharedArrayBufferOverrideEnabled: localStorage.getItem(
|
||||
'SharedArrayBufferOverride',
|
||||
),
|
||||
});
|
||||
|
||||
window.addEventListener('beforeunload', () => {
|
||||
sharedPort.postMessage({ type: 'tab-closing' });
|
||||
});
|
||||
|
||||
return;
|
||||
} catch (e) {
|
||||
console.log('SharedWorker failed, falling back to Worker:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: regular Worker (Playwright, no SharedWorker support, or failure)
|
||||
console.log('[WorkerBridge] No SharedWorker available, using direct Worker');
|
||||
worker = new Worker(backendWorkerUrl);
|
||||
initSQLBackend(worker);
|
||||
|
||||
@@ -37,6 +265,7 @@ function createBackendWorker() {
|
||||
isDev: IS_DEV,
|
||||
publicUrl: process.env.PUBLIC_URL,
|
||||
hash: process.env.REACT_APP_BACKEND_WORKER_HASH,
|
||||
hasSharedArrayBuffer: !!window.SharedArrayBuffer,
|
||||
isSharedArrayBufferOverrideEnabled: localStorage.getItem(
|
||||
'SharedArrayBufferOverride',
|
||||
),
|
||||
|
||||
@@ -14,10 +14,10 @@ import type {
|
||||
|
||||
import { categoryQueries } from './queries';
|
||||
|
||||
import { pushModal } from '#modals/modalsSlice';
|
||||
import { addNotification } from '#notifications/notificationsSlice';
|
||||
import { useDispatch } from '#redux';
|
||||
import type { AppDispatch } from '#redux/store';
|
||||
import { pushModal } from '@desktop-client/modals/modalsSlice';
|
||||
import { addNotification } from '@desktop-client/notifications/notificationsSlice';
|
||||
import { useDispatch } from '@desktop-client/redux';
|
||||
import type { AppDispatch } from '@desktop-client/redux/store';
|
||||
|
||||
function invalidateQueries(queryClient: QueryClient, queryKey?: QueryKey) {
|
||||
void queryClient.invalidateQueries({
|
||||
@@ -779,7 +779,7 @@ export function useBudgetActions() {
|
||||
});
|
||||
return null;
|
||||
default:
|
||||
throw new Error(`Unknown budget action type: ${type}`);
|
||||
throw new Error(`Unknown budget action type: ${String(type)}`);
|
||||
}
|
||||
},
|
||||
onSuccess: notification => {
|
||||
|
||||
@@ -9,11 +9,11 @@ import type { Budget } from 'loot-core/types/budget';
|
||||
import type { File } from 'loot-core/types/file';
|
||||
import type { Handlers } from 'loot-core/types/handlers';
|
||||
|
||||
import { resetApp, setAppState } from '#app/appSlice';
|
||||
import { closeModal, pushModal } from '#modals/modalsSlice';
|
||||
import { loadGlobalPrefs, loadPrefs } from '#prefs/prefsSlice';
|
||||
import { createAppAsyncThunk } from '#redux';
|
||||
import { signOut } from '#users/usersSlice';
|
||||
import { resetApp, setAppState } from '@desktop-client/app/appSlice';
|
||||
import { closeModal, pushModal } from '@desktop-client/modals/modalsSlice';
|
||||
import { loadGlobalPrefs, loadPrefs } from '@desktop-client/prefs/prefsSlice';
|
||||
import { createAppAsyncThunk } from '@desktop-client/redux';
|
||||
import { signOut } from '@desktop-client/users/usersSlice';
|
||||
|
||||
const sliceName = 'budgetfiles';
|
||||
|
||||
|
||||
@@ -26,26 +26,29 @@ import { Modals } from './Modals';
|
||||
import { SidebarProvider } from './sidebar/SidebarProvider';
|
||||
import { UpdateNotification } from './UpdateNotification';
|
||||
|
||||
import { setAppState, sync } from '#app/appSlice';
|
||||
import { closeBudget, loadBudget } from '#budgetfiles/budgetfilesSlice';
|
||||
import { handleGlobalEvents } from '#global-events';
|
||||
import { useIsTestEnv } from '#hooks/useIsTestEnv';
|
||||
import { useMetadataPref } from '#hooks/useMetadataPref';
|
||||
import { useOnVisible } from '#hooks/useOnVisible';
|
||||
import { SpreadsheetProvider } from '#hooks/useSpreadsheet';
|
||||
import { setI18NextLanguage } from '#i18n';
|
||||
import { addNotification } from '#notifications/notificationsSlice';
|
||||
import { installPolyfills } from '#polyfills';
|
||||
import { loadGlobalPrefs } from '#prefs/prefsSlice';
|
||||
import { useDispatch, useSelector, useStore } from '#redux';
|
||||
import { setAppState, sync } from '@desktop-client/app/appSlice';
|
||||
import {
|
||||
closeBudget,
|
||||
loadBudget,
|
||||
} from '@desktop-client/budgetfiles/budgetfilesSlice';
|
||||
import { handleGlobalEvents } from '@desktop-client/global-events';
|
||||
import { useIsTestEnv } from '@desktop-client/hooks/useIsTestEnv';
|
||||
import { useMetadataPref } from '@desktop-client/hooks/useMetadataPref';
|
||||
import { useOnVisible } from '@desktop-client/hooks/useOnVisible';
|
||||
import { SpreadsheetProvider } from '@desktop-client/hooks/useSpreadsheet';
|
||||
import { setI18NextLanguage } from '@desktop-client/i18n';
|
||||
import { addNotification } from '@desktop-client/notifications/notificationsSlice';
|
||||
import { installPolyfills } from '@desktop-client/polyfills';
|
||||
import { loadGlobalPrefs } from '@desktop-client/prefs/prefsSlice';
|
||||
import { useDispatch, useSelector, useStore } from '@desktop-client/redux';
|
||||
import {
|
||||
CustomThemeStyle,
|
||||
hasHiddenScrollbars,
|
||||
ThemeStyle,
|
||||
useTheme,
|
||||
} from '#style';
|
||||
import { signOut } from '#users/usersSlice';
|
||||
import { ExposeNavigate } from '#util/router-tools';
|
||||
} from '@desktop-client/style';
|
||||
import { signOut } from '@desktop-client/users/usersSlice';
|
||||
import { ExposeNavigate } from '@desktop-client/util/router-tools';
|
||||
|
||||
function AppInner() {
|
||||
const [budgetId] = useMetadataPref('id');
|
||||
|
||||
@@ -9,7 +9,7 @@ import { css } from '@emotion/css';
|
||||
|
||||
import { Background } from './Background';
|
||||
|
||||
import { useSelector } from '#redux';
|
||||
import { useSelector } from '@desktop-client/redux';
|
||||
|
||||
type AppBackgroundProps = {
|
||||
isLoading?: boolean;
|
||||
|
||||
@@ -9,7 +9,7 @@ import { View } from '@actual-app/components/view';
|
||||
|
||||
import { AnimatedRefresh } from './AnimatedRefresh';
|
||||
|
||||
import { useSelector } from '#redux';
|
||||
import { useSelector } from '@desktop-client/redux';
|
||||
|
||||
export function BankSyncStatus() {
|
||||
const accountsSyncing = useSelector(state => state.account.accountsSyncing);
|
||||
|
||||
@@ -24,19 +24,23 @@ import { Command } from 'cmdk';
|
||||
|
||||
import { CellValue, CellValueText } from './spreadsheet/CellValue';
|
||||
|
||||
import { useAccounts } from '#hooks/useAccounts';
|
||||
import { useDashboardPages } from '#hooks/useDashboardPages';
|
||||
import { useMetadataPref } from '#hooks/useMetadataPref';
|
||||
import { useModalState } from '#hooks/useModalState';
|
||||
import { useNavigate } from '#hooks/useNavigate';
|
||||
import { useReports } from '#hooks/useReports';
|
||||
import type { Binding, SheetFields, SheetNames } from '#spreadsheet';
|
||||
import { useAccounts } from '@desktop-client/hooks/useAccounts';
|
||||
import { useDashboardPages } from '@desktop-client/hooks/useDashboardPages';
|
||||
import { useMetadataPref } from '@desktop-client/hooks/useMetadataPref';
|
||||
import { useModalState } from '@desktop-client/hooks/useModalState';
|
||||
import { useNavigate } from '@desktop-client/hooks/useNavigate';
|
||||
import { useReports } from '@desktop-client/hooks/useReports';
|
||||
import type {
|
||||
Binding,
|
||||
SheetFields,
|
||||
SheetNames,
|
||||
} from '@desktop-client/spreadsheet';
|
||||
import {
|
||||
accountBalance,
|
||||
allAccountBalance,
|
||||
offBudgetAccountBalance,
|
||||
onBudgetAccountBalance,
|
||||
} from '#spreadsheet/bindings';
|
||||
} from '@desktop-client/spreadsheet/bindings';
|
||||
|
||||
type SearchableItem = {
|
||||
id: string;
|
||||
|
||||
@@ -15,7 +15,7 @@ import { Link } from './common/Link';
|
||||
import { Modal, ModalHeader } from './common/Modal';
|
||||
import { Checkbox } from './forms';
|
||||
|
||||
import { useModalState } from '#hooks/useModalState';
|
||||
import { useModalState } from '@desktop-client/hooks/useModalState';
|
||||
|
||||
type AppError = Error & {
|
||||
type?: string;
|
||||
|
||||
@@ -28,18 +28,18 @@ import { FloatableSidebar } from './sidebar';
|
||||
import { ManageTagsPage } from './tags/ManageTagsPage';
|
||||
import { Titlebar } from './Titlebar';
|
||||
|
||||
import { accountQueries } from '#accounts';
|
||||
import { getLatestAppVersion, sync } from '#app/appSlice';
|
||||
import { ProtectedRoute } from '#auth/ProtectedRoute';
|
||||
import { Permissions } from '#auth/types';
|
||||
import { useAccounts } from '#hooks/useAccounts';
|
||||
import { useGlobalPref } from '#hooks/useGlobalPref';
|
||||
import { useLocalPref } from '#hooks/useLocalPref';
|
||||
import { useMetaThemeColor } from '#hooks/useMetaThemeColor';
|
||||
import { useNavigate } from '#hooks/useNavigate';
|
||||
import { ScrollProvider } from '#hooks/useScrollListener';
|
||||
import { addNotification } from '#notifications/notificationsSlice';
|
||||
import { useDispatch, useSelector } from '#redux';
|
||||
import { accountQueries } from '@desktop-client/accounts';
|
||||
import { getLatestAppVersion, sync } from '@desktop-client/app/appSlice';
|
||||
import { ProtectedRoute } from '@desktop-client/auth/ProtectedRoute';
|
||||
import { Permissions } from '@desktop-client/auth/types';
|
||||
import { useAccounts } from '@desktop-client/hooks/useAccounts';
|
||||
import { useGlobalPref } from '@desktop-client/hooks/useGlobalPref';
|
||||
import { useLocalPref } from '@desktop-client/hooks/useLocalPref';
|
||||
import { useMetaThemeColor } from '@desktop-client/hooks/useMetaThemeColor';
|
||||
import { useNavigate } from '@desktop-client/hooks/useNavigate';
|
||||
import { ScrollProvider } from '@desktop-client/hooks/useScrollListener';
|
||||
import { addNotification } from '@desktop-client/notifications/notificationsSlice';
|
||||
import { useDispatch, useSelector } from '@desktop-client/redux';
|
||||
|
||||
function NarrowNotSupported({
|
||||
redirectTo = '/budget',
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useEffect } from 'react';
|
||||
|
||||
import * as Platform from 'loot-core/shared/platform';
|
||||
|
||||
import { useNavigate } from '#hooks/useNavigate';
|
||||
import { useNavigate } from '@desktop-client/hooks/useNavigate';
|
||||
|
||||
export function GlobalKeys() {
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -10,9 +10,9 @@ import { Popover } from '@actual-app/components/popover';
|
||||
import { SpaceBetween } from '@actual-app/components/space-between';
|
||||
import { useToggle } from 'usehooks-ts';
|
||||
|
||||
import { useFeatureFlag } from '#hooks/useFeatureFlag';
|
||||
import { pushModal } from '#modals/modalsSlice';
|
||||
import { useDispatch } from '#redux';
|
||||
import { useFeatureFlag } from '@desktop-client/hooks/useFeatureFlag';
|
||||
import { pushModal } from '@desktop-client/modals/modalsSlice';
|
||||
import { useDispatch } from '@desktop-client/redux';
|
||||
|
||||
const getPageDocs = (page: string) => {
|
||||
switch (page) {
|
||||
@@ -95,7 +95,7 @@ export const HelpMenu = () => {
|
||||
dispatch(pushModal({ modal: { name: 'goal-templates' } }));
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unrecognized menu option: ${item}`);
|
||||
throw new Error(`Unrecognized menu option: ${String(item)}`);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -18,13 +18,13 @@ import type { TransObjectLiteral } from 'loot-core/types/util';
|
||||
import { PrivacyFilter } from './PrivacyFilter';
|
||||
import { useMultiuserEnabled, useServerURL } from './ServerContext';
|
||||
|
||||
import { useAuth } from '#auth/AuthProvider';
|
||||
import { Permissions } from '#auth/types';
|
||||
import { closeBudget } from '#budgetfiles/budgetfilesSlice';
|
||||
import { useMetadataPref } from '#hooks/useMetadataPref';
|
||||
import { useNavigate } from '#hooks/useNavigate';
|
||||
import { useDispatch, useSelector } from '#redux';
|
||||
import { getUserData, signOut } from '#users/usersSlice';
|
||||
import { useAuth } from '@desktop-client/auth/AuthProvider';
|
||||
import { Permissions } from '@desktop-client/auth/types';
|
||||
import { closeBudget } from '@desktop-client/budgetfiles/budgetfilesSlice';
|
||||
import { useMetadataPref } from '@desktop-client/hooks/useMetadataPref';
|
||||
import { useNavigate } from '@desktop-client/hooks/useNavigate';
|
||||
import { useDispatch, useSelector } from '@desktop-client/redux';
|
||||
import { getUserData, signOut } from '@desktop-client/users/usersSlice';
|
||||
|
||||
type LoggedInUserProps = {
|
||||
hideIfNoServer?: boolean;
|
||||
|
||||
@@ -28,13 +28,16 @@ import { Search } from './common/Search';
|
||||
import { RulesHeader } from './rules/RulesHeader';
|
||||
import { RulesList } from './rules/RulesList';
|
||||
|
||||
import { useAccounts } from '#hooks/useAccounts';
|
||||
import { useCategories } from '#hooks/useCategories';
|
||||
import { usePayees } from '#hooks/usePayees';
|
||||
import { useSchedules } from '#hooks/useSchedules';
|
||||
import { SelectedProvider, useSelected } from '#hooks/useSelected';
|
||||
import { pushModal } from '#modals/modalsSlice';
|
||||
import { useDispatch } from '#redux';
|
||||
import { useAccounts } from '@desktop-client/hooks/useAccounts';
|
||||
import { useCategories } from '@desktop-client/hooks/useCategories';
|
||||
import { usePayees } from '@desktop-client/hooks/usePayees';
|
||||
import { useSchedules } from '@desktop-client/hooks/useSchedules';
|
||||
import {
|
||||
SelectedProvider,
|
||||
useSelected,
|
||||
} from '@desktop-client/hooks/useSelected';
|
||||
import { pushModal } from '@desktop-client/modals/modalsSlice';
|
||||
import { useDispatch } from '@desktop-client/redux';
|
||||
|
||||
export type FilterData = {
|
||||
payees?: Array<{ id: string; name: string }>;
|
||||
|
||||
@@ -17,6 +17,7 @@ import { CategoryMenuModal } from './modals/CategoryMenuModal';
|
||||
import { CloseAccountModal } from './modals/CloseAccountModal';
|
||||
import { ConfirmCategoryDeleteModal } from './modals/ConfirmCategoryDeleteModal';
|
||||
import { ConfirmDeleteModal } from './modals/ConfirmDeleteModal';
|
||||
import { ConfirmPayeesMergeModal } from './modals/ConfirmPayeesMergeModal';
|
||||
import { ConfirmTransactionEditModal } from './modals/ConfirmTransactionEditModal';
|
||||
import { ConfirmUnlinkAccountModal } from './modals/ConfirmUnlinkAccountModal';
|
||||
import { ConvertToScheduleModal } from './modals/ConvertToScheduleModal';
|
||||
@@ -78,11 +79,11 @@ import { ScheduleEditModal } from './schedules/ScheduleEditModal';
|
||||
import { ScheduleLink } from './schedules/ScheduleLink';
|
||||
import { UpcomingLength } from './schedules/UpcomingLength';
|
||||
|
||||
import { useMetadataPref } from '#hooks/useMetadataPref';
|
||||
import { useModalState } from '#hooks/useModalState';
|
||||
import { SheetNameProvider } from '#hooks/useSheetName';
|
||||
import { closeModal } from '#modals/modalsSlice';
|
||||
import { useDispatch } from '#redux';
|
||||
import { useMetadataPref } from '@desktop-client/hooks/useMetadataPref';
|
||||
import { useModalState } from '@desktop-client/hooks/useModalState';
|
||||
import { SheetNameProvider } from '@desktop-client/hooks/useSheetName';
|
||||
import { closeModal } from '@desktop-client/modals/modalsSlice';
|
||||
import { useDispatch } from '@desktop-client/redux';
|
||||
|
||||
export function Modals() {
|
||||
const location = useLocation();
|
||||
@@ -140,6 +141,9 @@ export function Modals() {
|
||||
case 'confirm-category-delete':
|
||||
return <ConfirmCategoryDeleteModal key={key} {...modal.options} />;
|
||||
|
||||
case 'confirm-payees-merge':
|
||||
return <ConfirmPayeesMergeModal key={key} {...modal.options} />;
|
||||
|
||||
case 'confirm-unlink-account':
|
||||
return <ConfirmUnlinkAccountModal key={key} {...modal.options} />;
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
markdownBaseStyles,
|
||||
remarkBreaks,
|
||||
sequentialNewlinesPlugin,
|
||||
} from '#util/markdown';
|
||||
} from '@desktop-client/util/markdown';
|
||||
|
||||
const remarkPlugins = [sequentialNewlinesPlugin, remarkGfm, remarkBreaks];
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import type { ComponentProps, CSSProperties } from 'react';
|
||||
import type { ComponentProps } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { Button } from '@actual-app/components/button';
|
||||
import { SvgCustomNotesPaper } from '@actual-app/components/icons/v2';
|
||||
import { Popover } from '@actual-app/components/popover';
|
||||
import type { CSSProperties } from '@actual-app/components/styles';
|
||||
import { theme } from '@actual-app/components/theme';
|
||||
import { Tooltip } from '@actual-app/components/tooltip';
|
||||
import { View } from '@actual-app/components/view';
|
||||
@@ -14,7 +15,7 @@ import { send } from 'loot-core/platform/client/connection';
|
||||
|
||||
import { Notes } from './Notes';
|
||||
|
||||
import { useNotes } from '#hooks/useNotes';
|
||||
import { useNotes } from '@desktop-client/hooks/useNotes';
|
||||
|
||||
type NotesButtonProps = {
|
||||
id: string;
|
||||
|
||||
@@ -18,9 +18,9 @@ import { css } from '@emotion/css';
|
||||
import { Link } from './common/Link';
|
||||
import { MODAL_Z_INDEX } from './common/Modal';
|
||||
|
||||
import { removeNotification } from '#notifications/notificationsSlice';
|
||||
import type { NotificationWithId } from '#notifications/notificationsSlice';
|
||||
import { useDispatch, useSelector } from '#redux';
|
||||
import { removeNotification } from '@desktop-client/notifications/notificationsSlice';
|
||||
import type { NotificationWithId } from '@desktop-client/notifications/notificationsSlice';
|
||||
import { useDispatch, useSelector } from '@desktop-client/redux';
|
||||
|
||||
// Notification stacking configuration
|
||||
const MAX_VISIBLE_NOTIFICATIONS = 3; // Maximum number of notifications visible in the stack
|
||||
@@ -153,22 +153,27 @@ function Notification({
|
||||
const yOffset = index * Y_OFFSET_PER_LEVEL;
|
||||
|
||||
const [isSwiped, setIsSwiped] = useState(false);
|
||||
const [spring, api] = useSpring(() => ({
|
||||
x: 0,
|
||||
y: yOffset,
|
||||
opacity: stackOpacity,
|
||||
scale,
|
||||
}));
|
||||
const [spring, api] = useSpring(
|
||||
() => ({
|
||||
from: {
|
||||
x: 0,
|
||||
y: yOffset,
|
||||
opacity: stackOpacity,
|
||||
scale,
|
||||
},
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
// Update scale, opacity, and y-position when index changes
|
||||
useEffect(() => {
|
||||
void api.start({ scale, opacity: stackOpacity, y: yOffset });
|
||||
void api.start({ to: { scale, opacity: stackOpacity, y: yOffset } });
|
||||
}, [index, scale, stackOpacity, yOffset, api]);
|
||||
|
||||
const swipeHandlers = useSwipeable({
|
||||
onSwiping: ({ deltaX }) => {
|
||||
if (!isSwiped) {
|
||||
void api.start({ x: deltaX });
|
||||
void api.start({ to: { x: deltaX } });
|
||||
}
|
||||
},
|
||||
onSwiped: ({ velocity, deltaX }) => {
|
||||
@@ -179,14 +184,13 @@ function Notification({
|
||||
if (Math.abs(deltaX) > threshold || velocity > 0.5) {
|
||||
// Animate out & remove item after animation
|
||||
void api.start({
|
||||
x: direction * 1000,
|
||||
opacity: 0,
|
||||
to: { x: direction * 1000, opacity: 0 },
|
||||
onRest: onRemove,
|
||||
});
|
||||
setIsSwiped(true);
|
||||
} else {
|
||||
// Reset position if not swiped far enough
|
||||
void api.start({ x: 0 });
|
||||
void api.start({ to: { x: 0 } });
|
||||
}
|
||||
},
|
||||
trackMouse: true,
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useResponsive } from '@actual-app/components/hooks/useResponsive';
|
||||
import { View } from '@actual-app/components/view';
|
||||
import { css } from '@emotion/css';
|
||||
|
||||
import { usePrivacyMode } from '#hooks/usePrivacyMode';
|
||||
import { usePrivacyMode } from '@desktop-client/hooks/usePrivacyMode';
|
||||
|
||||
type ConditionalPrivacyFilterProps = {
|
||||
children: ReactNode;
|
||||
|
||||
@@ -12,9 +12,9 @@ import { t } from 'i18next';
|
||||
import { send } from 'loot-core/platform/client/connection';
|
||||
import type { Handlers } from 'loot-core/types/handlers';
|
||||
|
||||
import { useOnVisible } from '#hooks/useOnVisible';
|
||||
import { addNotification } from '#notifications/notificationsSlice';
|
||||
import { useDispatch } from '#redux';
|
||||
import { useOnVisible } from '@desktop-client/hooks/useOnVisible';
|
||||
import { addNotification } from '@desktop-client/notifications/notificationsSlice';
|
||||
import { useDispatch } from '@desktop-client/redux';
|
||||
|
||||
type LoginMethod = {
|
||||
method: string;
|
||||
|
||||
@@ -14,7 +14,7 @@ import { Popover } from '@actual-app/components/popover';
|
||||
|
||||
import type { Theme } from 'loot-core/types/prefs';
|
||||
|
||||
import { themeOptions, useTheme } from '#style';
|
||||
import { themeOptions, useTheme } from '@desktop-client/style';
|
||||
|
||||
type ThemeSelectorProps = {
|
||||
style?: CSSProperties;
|
||||
@@ -33,7 +33,6 @@ export function ThemeSelector({ style }: ThemeSelectorProps) {
|
||||
dark: SvgMoonStars,
|
||||
auto: SvgSystem,
|
||||
midnight: SvgMoonStars,
|
||||
development: SvgMoonStars,
|
||||
} as const;
|
||||
|
||||
type ThemeIconKey = keyof typeof themeIcons;
|
||||
|
||||
@@ -34,15 +34,15 @@ import { useServerURL } from './ServerContext';
|
||||
import { useSidebar } from './sidebar/SidebarProvider';
|
||||
import { ThemeSelector } from './ThemeSelector';
|
||||
|
||||
import { sync } from '#app/appSlice';
|
||||
import { useGlobalPref } from '#hooks/useGlobalPref';
|
||||
import { useIsTestEnv } from '#hooks/useIsTestEnv';
|
||||
import { useMetadataPref } from '#hooks/useMetadataPref';
|
||||
import { useNavigate } from '#hooks/useNavigate';
|
||||
import { useSheetValue } from '#hooks/useSheetValue';
|
||||
import { useSyncedPref } from '#hooks/useSyncedPref';
|
||||
import { useDispatch } from '#redux';
|
||||
import * as bindings from '#spreadsheet/bindings';
|
||||
import { sync } from '@desktop-client/app/appSlice';
|
||||
import { useGlobalPref } from '@desktop-client/hooks/useGlobalPref';
|
||||
import { useIsTestEnv } from '@desktop-client/hooks/useIsTestEnv';
|
||||
import { useMetadataPref } from '@desktop-client/hooks/useMetadataPref';
|
||||
import { useNavigate } from '@desktop-client/hooks/useNavigate';
|
||||
import { useSheetValue } from '@desktop-client/hooks/useSheetValue';
|
||||
import { useSyncedPref } from '@desktop-client/hooks/useSyncedPref';
|
||||
import { useDispatch } from '@desktop-client/redux';
|
||||
import * as bindings from '@desktop-client/spreadsheet/bindings';
|
||||
|
||||
function UncategorizedButton() {
|
||||
const count: number | null = useSheetValue(bindings.uncategorizedCount());
|
||||
|
||||
@@ -9,8 +9,8 @@ import { View } from '@actual-app/components/view';
|
||||
|
||||
import { Link } from './common/Link';
|
||||
|
||||
import { setAppState, updateApp } from '#app/appSlice';
|
||||
import { useDispatch, useSelector } from '#redux';
|
||||
import { setAppState, updateApp } from '@desktop-client/app/appSlice';
|
||||
import { useDispatch, useSelector } from '@desktop-client/redux';
|
||||
|
||||
export function UpdateNotification() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -46,44 +46,44 @@ import {
|
||||
useSyncAndDownloadMutation,
|
||||
useUnlinkAccountMutation,
|
||||
useUpdateAccountMutation,
|
||||
} from '#accounts';
|
||||
import { markAccountRead } from '#accounts/accountsSlice';
|
||||
import type { SavedFilter } from '#components/filters/SavedFilterMenuButton';
|
||||
import { TransactionList } from '#components/transactions/TransactionList';
|
||||
import { validateAccountName } from '#components/util/accountValidation';
|
||||
import { useAccountPreviewTransactions } from '#hooks/useAccountPreviewTransactions';
|
||||
import { useAccounts } from '#hooks/useAccounts';
|
||||
import { SchedulesProvider } from '#hooks/useCachedSchedules';
|
||||
import { useCategories } from '#hooks/useCategories';
|
||||
import { useDateFormat } from '#hooks/useDateFormat';
|
||||
import { useFailedAccounts } from '#hooks/useFailedAccounts';
|
||||
import { useLocalPref } from '#hooks/useLocalPref';
|
||||
import { usePayees } from '#hooks/usePayees';
|
||||
import { getSchedulesQuery } from '#hooks/useSchedules';
|
||||
import { SelectedProviderWithItems } from '#hooks/useSelected';
|
||||
import type { Actions } from '#hooks/useSelected';
|
||||
} from '@desktop-client/accounts';
|
||||
import { markAccountRead } from '@desktop-client/accounts/accountsSlice';
|
||||
import type { SavedFilter } from '@desktop-client/components/filters/SavedFilterMenuButton';
|
||||
import { TransactionList } from '@desktop-client/components/transactions/TransactionList';
|
||||
import { validateAccountName } from '@desktop-client/components/util/accountValidation';
|
||||
import { useAccountPreviewTransactions } from '@desktop-client/hooks/useAccountPreviewTransactions';
|
||||
import { useAccounts } from '@desktop-client/hooks/useAccounts';
|
||||
import { SchedulesProvider } from '@desktop-client/hooks/useCachedSchedules';
|
||||
import { useCategories } from '@desktop-client/hooks/useCategories';
|
||||
import { useDateFormat } from '@desktop-client/hooks/useDateFormat';
|
||||
import { useFailedAccounts } from '@desktop-client/hooks/useFailedAccounts';
|
||||
import { useLocalPref } from '@desktop-client/hooks/useLocalPref';
|
||||
import { usePayees } from '@desktop-client/hooks/usePayees';
|
||||
import { getSchedulesQuery } from '@desktop-client/hooks/useSchedules';
|
||||
import { SelectedProviderWithItems } from '@desktop-client/hooks/useSelected';
|
||||
import type { Actions } from '@desktop-client/hooks/useSelected';
|
||||
import {
|
||||
SplitsExpandedProvider,
|
||||
useSplitsExpanded,
|
||||
} from '#hooks/useSplitsExpanded';
|
||||
import { useSyncedPref } from '#hooks/useSyncedPref';
|
||||
import { useTransactionBatchActions } from '#hooks/useTransactionBatchActions';
|
||||
import { useTransactionFilters } from '#hooks/useTransactionFilters';
|
||||
import { calculateRunningBalancesBottomUp } from '#hooks/useTransactions';
|
||||
} from '@desktop-client/hooks/useSplitsExpanded';
|
||||
import { useSyncedPref } from '@desktop-client/hooks/useSyncedPref';
|
||||
import { useTransactionBatchActions } from '@desktop-client/hooks/useTransactionBatchActions';
|
||||
import { useTransactionFilters } from '@desktop-client/hooks/useTransactionFilters';
|
||||
import { calculateRunningBalancesBottomUp } from '@desktop-client/hooks/useTransactions';
|
||||
import {
|
||||
openAccountCloseModal,
|
||||
pushModal,
|
||||
replaceModal,
|
||||
} from '#modals/modalsSlice';
|
||||
import { addNotification } from '#notifications/notificationsSlice';
|
||||
import { useCreatePayeeMutation } from '#payees';
|
||||
import * as queries from '#queries';
|
||||
import { aqlQuery } from '#queries/aqlQuery';
|
||||
import { pagedQuery } from '#queries/pagedQuery';
|
||||
import type { PagedQuery } from '#queries/pagedQuery';
|
||||
import { useDispatch, useSelector } from '#redux';
|
||||
import type { AppDispatch } from '#redux/store';
|
||||
import { updateNewTransactions } from '#transactions/transactionsSlice';
|
||||
} from '@desktop-client/modals/modalsSlice';
|
||||
import { addNotification } from '@desktop-client/notifications/notificationsSlice';
|
||||
import { useCreatePayeeMutation } from '@desktop-client/payees';
|
||||
import * as queries from '@desktop-client/queries';
|
||||
import { aqlQuery } from '@desktop-client/queries/aqlQuery';
|
||||
import { pagedQuery } from '@desktop-client/queries/pagedQuery';
|
||||
import type { PagedQuery } from '@desktop-client/queries/pagedQuery';
|
||||
import { useDispatch, useSelector } from '@desktop-client/redux';
|
||||
import type { AppDispatch } from '@desktop-client/redux/store';
|
||||
import { updateNewTransactions } from '@desktop-client/transactions/transactionsSlice';
|
||||
|
||||
type ConditionEntity = Partial<RuleConditionEntity> | TransactionFilterEntity;
|
||||
|
||||
@@ -1661,6 +1661,11 @@ class AccountInternal extends PureComponent<
|
||||
}
|
||||
|
||||
maybeSortByPreviousField(this, sortPrevField, sortPrevAscDesc);
|
||||
|
||||
// Always add sort_order as a final tiebreaker to maintain stable ordering
|
||||
// when transactions have the same values in the sorted column(s)
|
||||
this.currentQuery = this.currentQuery.orderBy({ sort_order: sortAscDesc });
|
||||
|
||||
this.updateQuery(this.currentQuery, isFiltered);
|
||||
};
|
||||
|
||||
@@ -1861,6 +1866,12 @@ class AccountInternal extends PureComponent<
|
||||
accountId === 'onbudget' ||
|
||||
accountId === 'uncategorized'
|
||||
}
|
||||
allowReorder={
|
||||
!!accountId &&
|
||||
accountId !== 'offbudget' &&
|
||||
accountId !== 'onbudget' &&
|
||||
accountId !== 'uncategorized'
|
||||
}
|
||||
isAdding={this.state.isAdding}
|
||||
isNew={this.isNew}
|
||||
isMatched={this.isMatched}
|
||||
|
||||
@@ -10,12 +10,12 @@ import { View } from '@actual-app/components/view';
|
||||
|
||||
import type { AccountEntity } from 'loot-core/types/models';
|
||||
|
||||
import { useUnlinkAccountMutation } from '#accounts';
|
||||
import { Link } from '#components/common/Link';
|
||||
import { authorizeBank } from '#gocardless';
|
||||
import { useAccounts } from '#hooks/useAccounts';
|
||||
import { useFailedAccounts } from '#hooks/useFailedAccounts';
|
||||
import { useDispatch } from '#redux';
|
||||
import { useUnlinkAccountMutation } from '@desktop-client/accounts';
|
||||
import { Link } from '@desktop-client/components/common/Link';
|
||||
import { authorizeBank } from '@desktop-client/gocardless';
|
||||
import { useAccounts } from '@desktop-client/hooks/useAccounts';
|
||||
import { useFailedAccounts } from '@desktop-client/hooks/useFailedAccounts';
|
||||
import { useDispatch } from '@desktop-client/redux';
|
||||
|
||||
function useErrorMessage() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -15,14 +15,17 @@ import { getScheduledAmount } from 'loot-core/shared/schedules';
|
||||
import { isPreviewId } from 'loot-core/shared/transactions';
|
||||
import type { AccountEntity } from 'loot-core/types/models';
|
||||
|
||||
import { FinancialText } from '#components/FinancialText';
|
||||
import { PrivacyFilter } from '#components/PrivacyFilter';
|
||||
import { CellValue, CellValueText } from '#components/spreadsheet/CellValue';
|
||||
import { useCachedSchedules } from '#hooks/useCachedSchedules';
|
||||
import { useFormat } from '#hooks/useFormat';
|
||||
import { useSelectedItems } from '#hooks/useSelected';
|
||||
import { useSheetValue } from '#hooks/useSheetValue';
|
||||
import type { Binding } from '#spreadsheet';
|
||||
import { FinancialText } from '@desktop-client/components/FinancialText';
|
||||
import { PrivacyFilter } from '@desktop-client/components/PrivacyFilter';
|
||||
import {
|
||||
CellValue,
|
||||
CellValueText,
|
||||
} from '@desktop-client/components/spreadsheet/CellValue';
|
||||
import { useCachedSchedules } from '@desktop-client/hooks/useCachedSchedules';
|
||||
import { useFormat } from '@desktop-client/hooks/useFormat';
|
||||
import { useSelectedItems } from '@desktop-client/hooks/useSelected';
|
||||
import { useSheetValue } from '@desktop-client/hooks/useSheetValue';
|
||||
import type { Binding } from '@desktop-client/spreadsheet';
|
||||
|
||||
type DetailedBalanceProps = {
|
||||
name: string;
|
||||
|
||||
@@ -13,12 +13,12 @@ import { Area, AreaChart, Tooltip as RechartsTooltip, YAxis } from 'recharts';
|
||||
import * as monthUtils from 'loot-core/shared/months';
|
||||
import { integerToCurrency } from 'loot-core/shared/util';
|
||||
|
||||
import { PrivacyFilter } from '#components/PrivacyFilter';
|
||||
import { useRechartsAnimation } from '#components/reports/chart-theme';
|
||||
import { LoadingIndicator } from '#components/reports/LoadingIndicator';
|
||||
import { useLocale } from '#hooks/useLocale';
|
||||
import * as query from '#queries';
|
||||
import { liveQuery } from '#queries/liveQuery';
|
||||
import { PrivacyFilter } from '@desktop-client/components/PrivacyFilter';
|
||||
import { useRechartsAnimation } from '@desktop-client/components/reports/chart-theme';
|
||||
import { LoadingIndicator } from '@desktop-client/components/reports/LoadingIndicator';
|
||||
import { useLocale } from '@desktop-client/hooks/useLocale';
|
||||
import * as query from '@desktop-client/queries';
|
||||
import { liveQuery } from '@desktop-client/queries/liveQuery';
|
||||
|
||||
const LABEL_WIDTH = 70;
|
||||
|
||||
|
||||
@@ -41,19 +41,19 @@ import { Balances } from './Balance';
|
||||
import { BalanceHistoryGraph } from './BalanceHistoryGraph';
|
||||
import { ReconcileMenu, ReconcilingMessage } from './Reconcile';
|
||||
|
||||
import { AnimatedRefresh } from '#components/AnimatedRefresh';
|
||||
import { Search } from '#components/common/Search';
|
||||
import { FilterButton } from '#components/filters/FiltersMenu';
|
||||
import { FiltersStack } from '#components/filters/FiltersStack';
|
||||
import type { SavedFilter } from '#components/filters/SavedFilterMenuButton';
|
||||
import { NotesButton } from '#components/NotesButton';
|
||||
import { SelectedTransactionsButton } from '#components/transactions/SelectedTransactionsButton';
|
||||
import { useDateFormat } from '#hooks/useDateFormat';
|
||||
import { useLocale } from '#hooks/useLocale';
|
||||
import { useLocalPref } from '#hooks/useLocalPref';
|
||||
import { useSplitsExpanded } from '#hooks/useSplitsExpanded';
|
||||
import { useSyncedPref } from '#hooks/useSyncedPref';
|
||||
import { useSyncServerStatus } from '#hooks/useSyncServerStatus';
|
||||
import { AnimatedRefresh } from '@desktop-client/components/AnimatedRefresh';
|
||||
import { Search } from '@desktop-client/components/common/Search';
|
||||
import { FilterButton } from '@desktop-client/components/filters/FiltersMenu';
|
||||
import { FiltersStack } from '@desktop-client/components/filters/FiltersStack';
|
||||
import type { SavedFilter } from '@desktop-client/components/filters/SavedFilterMenuButton';
|
||||
import { NotesButton } from '@desktop-client/components/NotesButton';
|
||||
import { SelectedTransactionsButton } from '@desktop-client/components/transactions/SelectedTransactionsButton';
|
||||
import { useDateFormat } from '@desktop-client/hooks/useDateFormat';
|
||||
import { useLocale } from '@desktop-client/hooks/useLocale';
|
||||
import { useLocalPref } from '@desktop-client/hooks/useLocalPref';
|
||||
import { useSplitsExpanded } from '@desktop-client/hooks/useSplitsExpanded';
|
||||
import { useSyncedPref } from '@desktop-client/hooks/useSyncedPref';
|
||||
import { useSyncServerStatus } from '@desktop-client/hooks/useSyncServerStatus';
|
||||
|
||||
type AccountHeaderProps = {
|
||||
tableRef: TableRef;
|
||||
|
||||
@@ -9,10 +9,10 @@ import type { AccountEntity } from 'loot-core/types/models';
|
||||
|
||||
import { ReconcileMenu, ReconcilingMessage } from './Reconcile';
|
||||
|
||||
import { useSheetValue } from '#hooks/useSheetValue';
|
||||
import { TestProviders } from '#mocks';
|
||||
import { useSheetValue } from '@desktop-client/hooks/useSheetValue';
|
||||
import { TestProviders } from '@desktop-client/mocks';
|
||||
|
||||
vi.mock('#hooks/useSheetValue', () => ({
|
||||
vi.mock('@desktop-client/hooks/useSheetValue', () => ({
|
||||
useSheetValue: vi.fn(),
|
||||
}));
|
||||
|
||||
|
||||
@@ -19,11 +19,11 @@ import { tsToRelativeTime } from 'loot-core/shared/util';
|
||||
import type { AccountEntity } from 'loot-core/types/models';
|
||||
import type { TransObjectLiteral } from 'loot-core/types/util';
|
||||
|
||||
import { useDateFormat } from '#hooks/useDateFormat';
|
||||
import { useFormat } from '#hooks/useFormat';
|
||||
import { useLocale } from '#hooks/useLocale';
|
||||
import { useSheetValue } from '#hooks/useSheetValue';
|
||||
import * as bindings from '#spreadsheet/bindings';
|
||||
import { useDateFormat } from '@desktop-client/hooks/useDateFormat';
|
||||
import { useFormat } from '@desktop-client/hooks/useFormat';
|
||||
import { useLocale } from '@desktop-client/hooks/useLocale';
|
||||
import { useSheetValue } from '@desktop-client/hooks/useSheetValue';
|
||||
import * as bindings from '@desktop-client/spreadsheet/bindings';
|
||||
|
||||
type ReconcilingMessageProps = {
|
||||
balanceQuery: { name: `balance-query-${string}`; query: Query };
|
||||
|
||||
@@ -19,13 +19,13 @@ import type { UserAccessEntity, UserAvailable } from 'loot-core/types/models';
|
||||
import { UserAccessHeader } from './UserAccessHeader';
|
||||
import { UserAccessRow } from './UserAccessRow';
|
||||
|
||||
import { InfiniteScrollWrapper } from '#components/common/InfiniteScrollWrapper';
|
||||
import { Link } from '#components/common/Link';
|
||||
import { Search } from '#components/common/Search';
|
||||
import { useMetadataPref } from '#hooks/useMetadataPref';
|
||||
import { pushModal } from '#modals/modalsSlice';
|
||||
import { addNotification } from '#notifications/notificationsSlice';
|
||||
import { useDispatch } from '#redux';
|
||||
import { InfiniteScrollWrapper } from '@desktop-client/components/common/InfiniteScrollWrapper';
|
||||
import { Link } from '@desktop-client/components/common/Link';
|
||||
import { Search } from '@desktop-client/components/common/Search';
|
||||
import { useMetadataPref } from '@desktop-client/hooks/useMetadataPref';
|
||||
import { pushModal } from '@desktop-client/modals/modalsSlice';
|
||||
import { addNotification } from '@desktop-client/notifications/notificationsSlice';
|
||||
import { useDispatch } from '@desktop-client/redux';
|
||||
|
||||
type ManageUserAccessContentProps = {
|
||||
isModal: boolean;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user