Skip to content

Commit

Permalink
Merge branch 'master' into fix/escaping-in-templating
Browse files Browse the repository at this point in the history
  • Loading branch information
UnderKoen authored Oct 11, 2024
2 parents 0cead09 + 37ad584 commit 31da568
Show file tree
Hide file tree
Showing 25 changed files with 459 additions and 276 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 11 additions & 1 deletion packages/desktop-client/src/components/schedules/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ import { Button } from '../common/Button2';
import { Search } from '../common/Search';
import { View } from '../common/View';
import { Page } from '../Page';
import { UpcomingLengthSettings } from '../settings/Upcoming';

import { SchedulesTable, type ScheduleItemAction } from './SchedulesTable';
import { type ScheduleItemAction, SchedulesTable } from './SchedulesTable';

export function Schedules() {
const { t } = useTranslation();
Expand Down Expand Up @@ -75,6 +76,15 @@ export function Schedules() {
padding: '0 0 15px',
}}
>
<View
style={{
flexDirection: 'row',
alignItems: 'center',
padding: '15px 0 0',
}}
>
<UpcomingLengthSettings />
</View>
<View
style={{
flex: 1,
Expand Down
85 changes: 85 additions & 0 deletions packages/desktop-client/src/components/settings/Upcoming.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import React, { useState } from 'react';
import { Trans } from 'react-i18next';
import { useLocation } from 'react-router-dom';

import { Column } from 'glamor/jsxstyle';

import { type SyncedPrefs } from 'loot-core/types/prefs';

import { useSyncedPref } from '../../hooks/useSyncedPref';
import { type CSSProperties, theme } from '../../style';
import { Button } from '../common/Button2';
import { Select } from '../common/Select';
import { Text } from '../common/Text';
import { View } from '../common/View';

import { Setting } from './UI';

const options: {
value: SyncedPrefs['upcomingScheduledTransactionLength'];
label: string;
}[] = [
{ value: '1', label: '1 Day' },
{ value: '7', label: '1 Week' },
{ value: '14', label: '2 Weeks' },
{ value: '30', label: '1 Month' },
];

export function UpcomingLengthSettings() {
const [_upcomingLength, setUpcomingLength] = useSyncedPref(
'upcomingScheduledTransactionLength',
);
const upcomingLength = _upcomingLength || '7';

const selectButtonStyle: CSSProperties = {
':hover': {
backgroundColor: theme.buttonNormalBackgroundHover,
},
};

const location = useLocation();
const [expanded, setExpanded] = useState(location.hash === '#upcomingLength');

return expanded ? (
<Setting
primaryAction={
<View style={{ flexDirection: 'row', gap: '1em' }}>
<Column title="Upcoming Length">
<Select
options={options.map(x => [x.value || '7', x.label])}
value={upcomingLength}
onChange={newValue => setUpcomingLength(newValue)}
style={selectButtonStyle}
/>
</Column>
</View>
}
>
<View style={{ flexDirection: 'row', gap: 20 }}>
<Text>
<strong>Upcoming Length</strong> does not affect how budget data is
stored, and can be changed at any time.
</Text>
<Button
onPress={() => setExpanded(false)}
aria-label="Close upcoming length settings"
>
Close
</Button>
</View>
</Setting>
) : (
<View>
<Button
aria-label="Edit upcoming length settings"
variant="primary"
onPress={() => setExpanded(true)}
>
<Trans>
Edit Upcoming Length (
{options.find(x => x.value === upcomingLength)?.label ?? '1 Week'})
</Trans>
</Button>
</View>
);
}
2 changes: 1 addition & 1 deletion packages/desktop-client/src/components/settings/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { listen } from 'loot-core/src/platform/client/fetch';
import { useActions } from '../../hooks/useActions';
import { useFeatureFlag } from '../../hooks/useFeatureFlag';
import { useGlobalPref } from '../../hooks/useGlobalPref';
import { useLatestVersion, useIsOutdated } from '../../hooks/useLatestVersion';
import { useIsOutdated, useLatestVersion } from '../../hooks/useLatestVersion';
import { useMetadataPref } from '../../hooks/useMetadataPref';
import { useResponsive } from '../../ResponsiveProvider';
import { theme } from '../../style';
Expand Down
24 changes: 14 additions & 10 deletions packages/loot-core/src/client/data-hooks/schedules.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,32 @@
// @ts-strict-ignore
import React, {
createContext,
useEffect,
useState,
useContext,
useEffect,
useMemo,
useState,
} from 'react';

import { useSyncedPref } from '@actual-app/web/src/hooks/useSyncedPref';

import { q, type Query } from '../../shared/query';
import { getStatus, getHasTransactionsQuery } from '../../shared/schedules';
import { getHasTransactionsQuery, getStatus } from '../../shared/schedules';
import { type ScheduleEntity } from '../../types/models';
import { getAccountFilter } from '../queries';
import { liveQuery } from '../query-helpers';

export type ScheduleStatusType = ReturnType<typeof getStatus>;
export type ScheduleStatuses = Map<ScheduleEntity['id'], ScheduleStatusType>;

function loadStatuses(schedules: ScheduleEntity[], onData) {
function loadStatuses(schedules: ScheduleEntity[], onData, prefs) {
return liveQuery(getHasTransactionsQuery(schedules), onData, {
mapper: data => {
const hasTrans = new Set(data.filter(Boolean).map(row => row.schedule));

return new Map(
schedules.map(s => [
s.id,
getStatus(s.next_date, s.completed, hasTrans.has(s.id)),
getStatus(s.next_date, s.completed, hasTrans.has(s.id), prefs),
]),
);
},
Expand All @@ -36,15 +38,15 @@ type UseSchedulesResult = {
schedules: ScheduleEntity[];
statuses: ScheduleStatuses;
} | null;

export function useSchedules({
transform,
}: UseSchedulesArgs = {}): UseSchedulesResult {
const [data, setData] = useState<UseSchedulesResult>(null);

const upcomingLength = useSyncedPref('upcomingScheduledTransactionLength')[0];
useEffect(() => {
const query = q('schedules').select('*');
let statusQuery;

const scheduleQuery = liveQuery(
transform ? transform(query) : query,
async (schedules: ScheduleEntity[]) => {
Expand All @@ -53,8 +55,10 @@ export function useSchedules({
statusQuery.unsubscribe();
}

statusQuery = loadStatuses(schedules, (statuses: ScheduleStatuses) =>
setData({ schedules, statuses }),
statusQuery = loadStatuses(
schedules,
(statuses: ScheduleStatuses) => setData({ schedules, statuses }),
upcomingLength,
);
}
},
Expand All @@ -68,7 +72,7 @@ export function useSchedules({
statusQuery.unsubscribe();
}
};
}, [transform]);
}, [upcomingLength, transform]);

return data;
}
Expand Down
9 changes: 9 additions & 0 deletions packages/loot-core/src/server/prefs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ import { Message, sendMessages } from './sync';
export const BUDGET_TYPES = ['report', 'rollover'] as const;
export type BudgetType = (typeof BUDGET_TYPES)[number];

export const UPCOMING_SCHEDULED_TRANSACTION_LENGTHS = [
'1',
'7',
'14',
'30',
] as const;
export type UpcomingScheduledTransactionLength =
(typeof UPCOMING_SCHEDULED_TRANSACTION_LENGTHS)[number];

let prefs: MetadataPrefs = null;

export async function loadPrefs(id?: string): Promise<MetadataPrefs> {
Expand Down
22 changes: 16 additions & 6 deletions packages/loot-core/src/server/schedules/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,22 @@ import { v4 as uuidv4 } from 'uuid';

import { captureBreadcrumb } from '../../platform/exceptions';
import * as connection from '../../platform/server/connection';
import { dayFromDate, currentDay, parseDate } from '../../shared/months';
import { currentDay, dayFromDate, parseDate } from '../../shared/months';
import { q } from '../../shared/query';
import {
extractScheduleConds,
recurConfigToRSchedule,
getHasTransactionsQuery,
getStatus,
getScheduledAmount,
getStatus,
recurConfigToRSchedule,
} from '../../shared/schedules';
import { Rule, Condition } from '../accounts/rules';
import { Condition, Rule } from '../accounts/rules';
import { addTransactions } from '../accounts/sync';
import {
insertRule,
updateRule,
getRules,
insertRule,
ruleModel,
updateRule,
} from '../accounts/transaction-rules';
import { createApp } from '../app';
import { runQuery as aqlQuery } from '../aql';
Expand Down Expand Up @@ -360,6 +360,7 @@ async function skipNextDate({ id }) {
},
});
}

function discoverSchedules() {
return findSchedules();
}
Expand Down Expand Up @@ -480,11 +481,20 @@ async function advanceSchedulesService(syncSuccess) {
const failedToPost = [];
let didPost = false;

const { data: upcomingLength } = await aqlQuery(
q('preferences')
.filter({ id: 'upcomingScheduledTransactionLength' })
.select('value'),
);

const upcomingLengthValue = upcomingLength[0]?.value ?? '7'; // Default to 7 days if not set

for (const schedule of schedules) {
const status = getStatus(
schedule.next_date,
schedule.completed,
hasTrans.has(schedule.id),
upcomingLengthValue,
);

if (status === 'paid') {
Expand Down
Loading

0 comments on commit 31da568

Please sign in to comment.