Files
actual/packages/desktop-client/src/components/schedules/PostsOfflineNotification.tsx
d8317c44b7 [AI] Migrate desktop-client to subpath imports (#7446)
* [AI] Migrate desktop-client to subpath imports

Replace the `@desktop-client/*` path alias with Node.js subpath
imports (`#*`) across packages/desktop-client:

- Declare the full `imports` map in packages/desktop-client/package.json
  (bare index entries, root-level files, and per-subdirectory wildcards
  with explicit extension overrides where `.ts` and `.tsx` mix).
- Update all source files to import from `#...` specifiers.
- Drop the `@desktop-client` group from .oxfmtrc.json.
- Enable `actual/prefer-subpath-imports` for desktop-client in
  .oxlintrc.json so future code keeps using the subpath form.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* [AI] Drop legacy desktop-client aliases

Remove the `@desktop-client/*` and `loot-core/*` path aliases from
vite.config.ts and tsconfig.json now that every desktop-client source
file imports via subpath imports / `@actual-app/core`.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Release notes

* [AI] Use electron-renderer condition for renderer-only exports

Desktop-client's Vite build used the `electron` resolve condition, which
overlapped with loot-core exports where `electron` means the Node/main
variant (e.g. `shared/platform.electron.ts` using `os`,
`platform/server/asyncStorage/index.electron.ts` using `fs`). Once the
`loot-core` Vite alias was removed, the renderer bundle started pulling
those Node variants and crashed at runtime with
`It.default.platform is not a function` inside `platform.electron.ts`.

Introduce a distinct `electron-renderer` condition used only by
desktop-client's Vite config, and rename the `electron` key to
`electron-renderer` on the sole loot-core export whose `electron` branch
is the Electron renderer variant (`#/./platform/client/connection`, the
IPC `global.Actual.ipcConnect` file). Every other `electron`-conditioned
export keeps its Node semantics and is still matched by loot-core's own
`vite.desktop.config.ts` (`conditions: ['electron']`).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* [AI] Drop .electron.* extensions from loot-core desktop resolver

Now that every Node/main variant is selected via the `electron` subpath
import condition in `packages/loot-core/package.json`, Vite's
`resolveExtensions` list no longer needs the `.electron.js`,
`.electron.ts`, `.electron.tsx` entries. Remove them to keep resolution
explicit and avoid implicit extension picking.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* [AI] Align desktop-client TS resolution with Vite

- Set `customConditions: ["electron-renderer"]` in
  `packages/desktop-client/tsconfig.json` so TypeScript resolves
  conditional imports (notably `@actual-app/core/platform/client/connection`)
  to the same file Vite picks at runtime. Today the surfaces happen to
  match because both variants import from a shared `index-types.ts`,
  but the alignment prevents a latent drift bug.
- Fix typo in the release note (`Standartise` -> `Standardise`).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 17:03:45 +00:00

108 lines
3.6 KiB
TypeScript

import React from 'react';
import { Trans, useTranslation } from 'react-i18next';
import { useLocation } from 'react-router';
import { Button } from '@actual-app/components/button';
import { Paragraph } from '@actual-app/components/paragraph';
import { SpaceBetween } from '@actual-app/components/space-between';
import { Text } from '@actual-app/components/text';
import { theme } from '@actual-app/components/theme';
import { send } from '@actual-app/core/platform/client/connection';
import type { PayeeEntity } from '@actual-app/core/types/models';
import { Modal, ModalCloseButton, ModalHeader } from '#components/common/Modal';
import { DisplayId } from '#components/util/DisplayId';
import { useFormatList } from '#hooks/useFormatList';
import { popModal } from '#modals/modalsSlice';
import { useDispatch } from '#redux';
export function PostsOfflineNotification() {
const { t, i18n } = useTranslation();
const location = useLocation();
const dispatch = useDispatch();
const locationState = location.state;
const payees =
locationState && 'payees' in locationState
? (locationState.payees as Array<PayeeEntity['id']>)
: [];
async function onPost() {
await send('schedule/force-run-service');
dispatch(popModal());
}
const payeesList = payees.map(id => (
<Text key={id} style={{ color: theme.pageTextPositive }}>
<DisplayId id={id} type="payees" />
</Text>
));
const payeeNamesList = useFormatList(payeesList, i18n.language);
return (
<Modal name="schedule-posts-offline-notification">
{({ state }) => (
<>
<ModalHeader
title={t('Post transactions?')}
rightContent={<ModalCloseButton onPress={() => state.close()} />}
/>
<Paragraph>
<Text>
{payees.length > 0 ? (
<Trans count={payees.length}>
The payees <span>{payeeNamesList}</span> have schedules that
are due today.
</Trans>
) : (
t('There are payees that have schedules that are due today.', {
count: payees.length,
})
)}{' '}
<Trans>
Usually we automatically post transactions for these, but you
are offline or syncing failed. In order to avoid duplicate
transactions, we let you choose whether or not to create
transactions for these schedules.
</Trans>
</Text>
</Paragraph>
<Paragraph>
<Trans>
Be aware that other devices may have already created these
transactions. If you have multiple devices, make sure you only do
this on one device or you will have duplicate transactions.
</Trans>
</Paragraph>
<Paragraph>
<Trans>
You can always manually post a transaction later for a due
schedule by selecting the schedule and clicking "Post transaction
today" in the action menu.
</Trans>
</Paragraph>
<SpaceBetween
gap={10}
style={{ marginTop: 20, justifyContent: 'flex-end' }}
>
<Button onPress={() => state.close()}>
<Trans>Decide later</Trans>
</Button>
<Button
variant="primary"
autoFocus
onPress={() => {
void onPost();
state.close();
}}
>
<Trans>Post transactions</Trans>
</Button>
</SpaceBetween>
</>
)}
</Modal>
);
}