mirror of
https://github.com/actualbudget/actual.git
synced 2026-04-28 10:33:02 -05:00
Compare commits
5 Commits
ts-useSpli
...
v24.10.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0fcfd2d5a | ||
|
|
888d2e9cce | ||
|
|
2e569355bc | ||
|
|
4a1e1511bc | ||
|
|
c456596417 |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@actual-app/api",
|
"name": "@actual-app/api",
|
||||||
"version": "24.10.0",
|
"version": "24.10.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"description": "An API for Actual",
|
"description": "An API for Actual",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 47 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 47 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 47 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 47 KiB |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@actual-app/web",
|
"name": "@actual-app/web",
|
||||||
"version": "24.10.0",
|
"version": "24.10.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"files": [
|
"files": [
|
||||||
"build"
|
"build"
|
||||||
|
|||||||
@@ -78,24 +78,8 @@ function FileMenu({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const items = [{ name: 'delete', text: t('Delete') }];
|
const items = [{ name: 'delete', text: t('Delete') }];
|
||||||
const { isNarrowWidth } = useResponsive();
|
|
||||||
|
|
||||||
const defaultMenuItemStyle = isNarrowWidth
|
return <Menu onMenuSelect={onMenuSelect} items={items} />;
|
||||||
? {
|
|
||||||
...styles.mobileMenuItem,
|
|
||||||
color: theme.menuItemText,
|
|
||||||
borderRadius: 0,
|
|
||||||
borderTop: `1px solid ${theme.pillBorder}`,
|
|
||||||
}
|
|
||||||
: {};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Menu
|
|
||||||
getItemStyle={() => defaultMenuItemStyle}
|
|
||||||
onMenuSelect={onMenuSelect}
|
|
||||||
items={items}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function FileMenuButton({ onDelete }: { onDelete: () => void }) {
|
function FileMenuButton({ onDelete }: { onDelete: () => void }) {
|
||||||
@@ -202,50 +186,60 @@ function FileItem({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<Button
|
||||||
onClick={() => _onSelect(file)}
|
onPress={() => _onSelect(file)}
|
||||||
title={getFileDescription(file, t) || ''}
|
|
||||||
style={{
|
style={{
|
||||||
flexDirection: 'row',
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
alignItems: 'center',
|
|
||||||
...styles.shadow,
|
...styles.shadow,
|
||||||
margin: 10,
|
margin: 10,
|
||||||
padding: '12px 15px',
|
padding: '12px 15px',
|
||||||
backgroundColor: theme.buttonNormalBackground,
|
|
||||||
borderRadius: 6,
|
|
||||||
flexShrink: 0,
|
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
':hover': {
|
borderRadius: 6,
|
||||||
backgroundColor: theme.menuItemBackgroundHover,
|
borderColor: 'transparent',
|
||||||
},
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<View style={{ alignItems: 'flex-start' }}>
|
|
||||||
<Text style={{ fontSize: 16, fontWeight: 700 }}>{file.name}</Text>
|
|
||||||
|
|
||||||
<FileState file={file} />
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<View
|
<View
|
||||||
style={{ flex: '0 0 auto', flexDirection: 'row', alignItems: 'center' }}
|
style={{
|
||||||
|
flexDirection: 'row',
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{file.encryptKeyId && (
|
<View
|
||||||
<SvgKey
|
title={getFileDescription(file, t) || ''}
|
||||||
style={{
|
style={{ alignItems: 'flex-start' }}
|
||||||
width: 13,
|
>
|
||||||
height: 13,
|
<Text style={{ fontSize: 16, fontWeight: 700 }}>{file.name}</Text>
|
||||||
marginRight: 8,
|
|
||||||
color: file.hasKey
|
|
||||||
? theme.formLabelText
|
|
||||||
: theme.buttonNormalDisabledText,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!quickSwitchMode && <FileMenuButton onDelete={() => onDelete(file)} />}
|
<FileState file={file} />
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flex: '0 0 auto',
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{file.encryptKeyId && (
|
||||||
|
<SvgKey
|
||||||
|
style={{
|
||||||
|
width: 13,
|
||||||
|
height: 13,
|
||||||
|
marginRight: 8,
|
||||||
|
color: file.hasKey
|
||||||
|
? theme.formLabelText
|
||||||
|
: theme.buttonNormalDisabledText,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!quickSwitchMode && (
|
||||||
|
<FileMenuButton onDelete={() => onDelete(file)} />
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</Button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ export function EnvelopeBudgetMenuModal({
|
|||||||
fontWeight: 400,
|
fontWeight: 400,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Budget
|
Budgeted
|
||||||
</Text>
|
</Text>
|
||||||
<FocusableAmountInput
|
<FocusableAmountInput
|
||||||
value={integerToAmount(budgeted || 0)}
|
value={integerToAmount(budgeted || 0)}
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ export function TrackingBudgetMenuModal({
|
|||||||
fontWeight: 400,
|
fontWeight: 400,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Budget
|
Budgeted
|
||||||
</Text>
|
</Text>
|
||||||
<FocusableAmountInput
|
<FocusableAmountInput
|
||||||
value={integerToAmount(budgeted || 0)}
|
value={integerToAmount(budgeted || 0)}
|
||||||
|
|||||||
@@ -82,16 +82,26 @@ export function Overview() {
|
|||||||
const isDashboardsFeatureEnabled = useFeatureFlag('dashboards');
|
const isDashboardsFeatureEnabled = useFeatureFlag('dashboards');
|
||||||
const spendingReportFeatureFlag = useFeatureFlag('spendingReport');
|
const spendingReportFeatureFlag = useFeatureFlag('spendingReport');
|
||||||
|
|
||||||
const baseLayout = widgets.map(widget => ({
|
const baseLayout = widgets
|
||||||
i: widget.id,
|
.map(widget => ({
|
||||||
w: widget.width,
|
i: widget.id,
|
||||||
h: widget.height,
|
w: widget.width,
|
||||||
minW:
|
h: widget.height,
|
||||||
isCustomReportWidget(widget) || widget.type === 'markdown-card' ? 2 : 3,
|
minW:
|
||||||
minH:
|
isCustomReportWidget(widget) || widget.type === 'markdown-card' ? 2 : 3,
|
||||||
isCustomReportWidget(widget) || widget.type === 'markdown-card' ? 1 : 2,
|
minH:
|
||||||
...widget,
|
isCustomReportWidget(widget) || widget.type === 'markdown-card' ? 1 : 2,
|
||||||
}));
|
...widget,
|
||||||
|
}))
|
||||||
|
.filter(item => {
|
||||||
|
if (isDashboardsFeatureEnabled) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (item.type === 'custom-report' && !customReportMap.has(item.meta.id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
const layout =
|
const layout =
|
||||||
spendingReportFeatureFlag &&
|
spendingReportFeatureFlag &&
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
|
||||||
import { useQuery } from 'loot-core/client/query-hooks';
|
import { saveSyncedPrefs } from 'loot-core/client/actions';
|
||||||
import { send } from 'loot-core/platform/client/fetch';
|
import { type State } from 'loot-core/client/state-types';
|
||||||
import { q } from 'loot-core/shared/query';
|
|
||||||
import { type SyncedPrefs } from 'loot-core/src/types/prefs';
|
import { type SyncedPrefs } from 'loot-core/src/types/prefs';
|
||||||
|
|
||||||
type SetSyncedPrefAction<K extends keyof SyncedPrefs> = (
|
type SetSyncedPrefAction<K extends keyof SyncedPrefs> = (
|
||||||
@@ -12,21 +12,14 @@ type SetSyncedPrefAction<K extends keyof SyncedPrefs> = (
|
|||||||
export function useSyncedPref<K extends keyof SyncedPrefs>(
|
export function useSyncedPref<K extends keyof SyncedPrefs>(
|
||||||
prefName: K,
|
prefName: K,
|
||||||
): [SyncedPrefs[K], SetSyncedPrefAction<K>] {
|
): [SyncedPrefs[K], SetSyncedPrefAction<K>] {
|
||||||
const { data: queryData, overrideData: setQueryData } = useQuery<
|
const dispatch = useDispatch();
|
||||||
[{ value: string | undefined }]
|
const setPref = useCallback<SetSyncedPrefAction<K>>(
|
||||||
>(
|
value => {
|
||||||
() => q('preferences').filter({ id: prefName }).select('value'),
|
dispatch(saveSyncedPrefs({ [prefName]: value }));
|
||||||
[prefName],
|
|
||||||
);
|
|
||||||
|
|
||||||
const setLocalPref = useCallback<SetSyncedPrefAction<K>>(
|
|
||||||
newValue => {
|
|
||||||
const value = String(newValue);
|
|
||||||
setQueryData([{ value }]);
|
|
||||||
send('preferences/save', { id: prefName, value });
|
|
||||||
},
|
},
|
||||||
[prefName, setQueryData],
|
[prefName, dispatch],
|
||||||
);
|
);
|
||||||
|
const pref = useSelector((state: State) => state.prefs.synced[prefName]);
|
||||||
|
|
||||||
return [queryData?.[0]?.value, setLocalPref];
|
return [pref, setPref];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,39 +1,22 @@
|
|||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
|
||||||
import { useQuery } from 'loot-core/client/query-hooks';
|
import { saveSyncedPrefs } from 'loot-core/client/actions';
|
||||||
import { send } from 'loot-core/platform/client/fetch';
|
import { type State } from 'loot-core/client/state-types';
|
||||||
import { q } from 'loot-core/shared/query';
|
|
||||||
import { type SyncedPrefs } from 'loot-core/src/types/prefs';
|
import { type SyncedPrefs } from 'loot-core/src/types/prefs';
|
||||||
|
|
||||||
type SetSyncedPrefsAction = (value: Partial<SyncedPrefs>) => void;
|
type SetSyncedPrefsAction = (value: Partial<SyncedPrefs>) => void;
|
||||||
|
|
||||||
/** @deprecated: please use `useSyncedPref` (singular) */
|
/** @deprecated: please use `useSyncedPref` (singular) */
|
||||||
export function useSyncedPrefs(): [SyncedPrefs, SetSyncedPrefsAction] {
|
export function useSyncedPrefs(): [SyncedPrefs, SetSyncedPrefsAction] {
|
||||||
const { data: queryData } = useQuery<{ id: string; value: string }[]>(
|
const dispatch = useDispatch();
|
||||||
() => q('preferences').select(['id', 'value']),
|
const setPrefs = useCallback<SetSyncedPrefsAction>(
|
||||||
[],
|
newValue => {
|
||||||
|
dispatch(saveSyncedPrefs(newValue));
|
||||||
|
},
|
||||||
|
[dispatch],
|
||||||
);
|
);
|
||||||
|
const prefs = useSelector((state: State) => state.prefs.synced);
|
||||||
const prefs = useMemo<SyncedPrefs>(
|
|
||||||
() =>
|
|
||||||
(queryData ?? []).reduce(
|
|
||||||
(carry, { id, value }) => ({
|
|
||||||
...carry,
|
|
||||||
[id]: value,
|
|
||||||
}),
|
|
||||||
{},
|
|
||||||
),
|
|
||||||
[queryData],
|
|
||||||
);
|
|
||||||
|
|
||||||
const setPrefs = useCallback<SetSyncedPrefsAction>(newValue => {
|
|
||||||
Object.entries(newValue).forEach(([id, value]) => {
|
|
||||||
send('preferences/save', {
|
|
||||||
id: id as keyof SyncedPrefs,
|
|
||||||
value: String(value),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return [prefs, setPrefs];
|
return [prefs, setPrefs];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"author": "Actual",
|
"author": "Actual",
|
||||||
"productName": "Actual",
|
"productName": "Actual",
|
||||||
"description": "A simple and powerful personal finance system",
|
"description": "A simple and powerful personal finance system",
|
||||||
"version": "24.10.0",
|
"version": "24.10.1",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"clean": "rm -rf dist",
|
"clean": "rm -rf dist",
|
||||||
"update-client": "bin/update-client",
|
"update-client": "bin/update-client",
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import { send } from '../../platform/client/fetch';
|
import { send } from '../../platform/client/fetch';
|
||||||
import { type GlobalPrefs, type MetadataPrefs } from '../../types/prefs';
|
import {
|
||||||
|
type GlobalPrefs,
|
||||||
|
type MetadataPrefs,
|
||||||
|
type SyncedPrefs,
|
||||||
|
} from '../../types/prefs';
|
||||||
import * as constants from '../constants';
|
import * as constants from '../constants';
|
||||||
|
|
||||||
import { closeModal } from './modals';
|
import { closeModal } from './modals';
|
||||||
@@ -19,6 +23,7 @@ export function loadPrefs() {
|
|||||||
type: constants.SET_PREFS,
|
type: constants.SET_PREFS,
|
||||||
prefs,
|
prefs,
|
||||||
globalPrefs: await send('load-global-prefs'),
|
globalPrefs: await send('load-global-prefs'),
|
||||||
|
syncedPrefs: await send('preferences/get'),
|
||||||
});
|
});
|
||||||
|
|
||||||
return prefs;
|
return prefs;
|
||||||
@@ -42,6 +47,7 @@ export function loadGlobalPrefs() {
|
|||||||
type: constants.SET_PREFS,
|
type: constants.SET_PREFS,
|
||||||
prefs: getState().prefs.local,
|
prefs: getState().prefs.local,
|
||||||
globalPrefs,
|
globalPrefs,
|
||||||
|
syncedPrefs: getState().prefs.synced,
|
||||||
});
|
});
|
||||||
return globalPrefs;
|
return globalPrefs;
|
||||||
};
|
};
|
||||||
@@ -60,3 +66,20 @@ export function saveGlobalPrefs(
|
|||||||
onSaveGlobalPrefs?.();
|
onSaveGlobalPrefs?.();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function saveSyncedPrefs(prefs: SyncedPrefs) {
|
||||||
|
return async (dispatch: Dispatch) => {
|
||||||
|
await Promise.all(
|
||||||
|
Object.entries(prefs).map(([prefName, value]) =>
|
||||||
|
send('preferences/save', {
|
||||||
|
id: prefName as keyof SyncedPrefs,
|
||||||
|
value,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
dispatch({
|
||||||
|
type: constants.MERGE_SYNCED_PREFS,
|
||||||
|
syncedPrefs: prefs,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// @ts-strict-ignore
|
|
||||||
import { send } from '../../platform/client/fetch';
|
import { send } from '../../platform/client/fetch';
|
||||||
import { getUploadError } from '../../shared/errors';
|
import { getUploadError } from '../../shared/errors';
|
||||||
|
|
||||||
@@ -32,7 +31,6 @@ export function resetSync() {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await dispatch(sync());
|
await dispatch(sync());
|
||||||
await dispatch(loadPrefs());
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -45,8 +43,12 @@ export function sync() {
|
|||||||
if ('error' in result) {
|
if ('error' in result) {
|
||||||
return { error: result.error };
|
return { error: result.error };
|
||||||
}
|
}
|
||||||
return {};
|
|
||||||
|
// Update the prefs
|
||||||
|
await dispatch(loadPrefs());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export const LOAD_PAYEES = 'LOAD_PAYEES';
|
|||||||
export const SET_PREFS = 'SET_PREFS';
|
export const SET_PREFS = 'SET_PREFS';
|
||||||
export const MERGE_LOCAL_PREFS = 'MERGE_LOCAL_PREFS';
|
export const MERGE_LOCAL_PREFS = 'MERGE_LOCAL_PREFS';
|
||||||
export const MERGE_GLOBAL_PREFS = 'MERGE_GLOBAL_PREFS';
|
export const MERGE_GLOBAL_PREFS = 'MERGE_GLOBAL_PREFS';
|
||||||
|
export const MERGE_SYNCED_PREFS = 'MERGE_SYNCED_PREFS';
|
||||||
export const SET_BUDGETS = 'SET_BUDGETS';
|
export const SET_BUDGETS = 'SET_BUDGETS';
|
||||||
export const SET_REMOTE_FILES = 'SET_REMOTE_FILES';
|
export const SET_REMOTE_FILES = 'SET_REMOTE_FILES';
|
||||||
export const SET_ALL_FILES = 'SET_ALL_FILES';
|
export const SET_ALL_FILES = 'SET_ALL_FILES';
|
||||||
|
|||||||
@@ -1,17 +1,22 @@
|
|||||||
// @ts-strict-ignore
|
|
||||||
import * as constants from '../constants';
|
import * as constants from '../constants';
|
||||||
import type { Action } from '../state-types';
|
import type { Action } from '../state-types';
|
||||||
import type { PrefsState } from '../state-types/prefs';
|
import type { PrefsState } from '../state-types/prefs';
|
||||||
|
|
||||||
const initialState: PrefsState = {
|
const initialState: PrefsState = {
|
||||||
local: null,
|
local: {},
|
||||||
global: null,
|
global: {},
|
||||||
|
synced: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
export function update(state = initialState, action: Action): PrefsState {
|
export function update(state = initialState, action: Action): PrefsState {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case constants.SET_PREFS:
|
case constants.SET_PREFS:
|
||||||
return { local: action.prefs, global: action.globalPrefs };
|
return {
|
||||||
|
...state,
|
||||||
|
local: action.prefs,
|
||||||
|
global: action.globalPrefs,
|
||||||
|
synced: action.syncedPrefs,
|
||||||
|
};
|
||||||
case constants.MERGE_LOCAL_PREFS:
|
case constants.MERGE_LOCAL_PREFS:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
@@ -22,6 +27,11 @@ export function update(state = initialState, action: Action): PrefsState {
|
|||||||
...state,
|
...state,
|
||||||
global: { ...state.global, ...action.globalPrefs },
|
global: { ...state.global, ...action.globalPrefs },
|
||||||
};
|
};
|
||||||
|
case constants.MERGE_SYNCED_PREFS:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
synced: { ...state.synced, ...action.syncedPrefs },
|
||||||
|
};
|
||||||
|
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,21 @@
|
|||||||
import type { GlobalPrefs, MetadataPrefs } from '../../types/prefs';
|
import type {
|
||||||
|
GlobalPrefs,
|
||||||
|
MetadataPrefs,
|
||||||
|
SyncedPrefs,
|
||||||
|
} from '../../types/prefs';
|
||||||
import type * as constants from '../constants';
|
import type * as constants from '../constants';
|
||||||
|
|
||||||
export type PrefsState = {
|
export type PrefsState = {
|
||||||
local: MetadataPrefs;
|
local: MetadataPrefs;
|
||||||
global: GlobalPrefs;
|
global: GlobalPrefs;
|
||||||
|
synced: SyncedPrefs;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SetPrefsAction = {
|
export type SetPrefsAction = {
|
||||||
type: typeof constants.SET_PREFS;
|
type: typeof constants.SET_PREFS;
|
||||||
prefs: MetadataPrefs;
|
prefs: MetadataPrefs;
|
||||||
globalPrefs: GlobalPrefs;
|
globalPrefs: GlobalPrefs;
|
||||||
|
syncedPrefs: SyncedPrefs;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type MergeLocalPrefsAction = {
|
export type MergeLocalPrefsAction = {
|
||||||
@@ -22,7 +28,13 @@ export type MergeGlobalPrefsAction = {
|
|||||||
globalPrefs: GlobalPrefs;
|
globalPrefs: GlobalPrefs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type MergeSyncedPrefsAction = {
|
||||||
|
type: typeof constants.MERGE_SYNCED_PREFS;
|
||||||
|
syncedPrefs: SyncedPrefs;
|
||||||
|
};
|
||||||
|
|
||||||
export type PrefsActions =
|
export type PrefsActions =
|
||||||
| SetPrefsAction
|
| SetPrefsAction
|
||||||
| MergeLocalPrefsAction
|
| MergeLocalPrefsAction
|
||||||
| MergeGlobalPrefsAction;
|
| MergeGlobalPrefsAction
|
||||||
|
| MergeSyncedPrefsAction;
|
||||||
|
|||||||
@@ -18,4 +18,17 @@ const savePreferences = async ({
|
|||||||
await db.update('preferences', { id, value });
|
await db.update('preferences', { id, value });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getPreferences = async (): Promise<SyncedPrefs> => {
|
||||||
|
const prefs = (await db.all('SELECT id, value FROM preferences')) as Array<{
|
||||||
|
id: string;
|
||||||
|
value: string;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
return prefs.reduce<SyncedPrefs>((carry, { value, id }) => {
|
||||||
|
carry[id as keyof SyncedPrefs] = value;
|
||||||
|
return carry;
|
||||||
|
}, {});
|
||||||
|
};
|
||||||
|
|
||||||
app.method('preferences/save', mutator(undoable(savePreferences)));
|
app.method('preferences/save', mutator(undoable(savePreferences)));
|
||||||
|
app.method('preferences/get', getPreferences);
|
||||||
|
|||||||
@@ -5,4 +5,6 @@ export interface PreferencesHandlers {
|
|||||||
id: keyof SyncedPrefs;
|
id: keyof SyncedPrefs;
|
||||||
value: string | undefined;
|
value: string | undefined;
|
||||||
}) => Promise<void>;
|
}) => Promise<void>;
|
||||||
|
|
||||||
|
'preferences/get': () => Promise<SyncedPrefs>;
|
||||||
}
|
}
|
||||||
|
|||||||
6
upcoming-release-notes/3544.md
Normal file
6
upcoming-release-notes/3544.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
category: Maintenance
|
||||||
|
authors: [MatissJanis]
|
||||||
|
---
|
||||||
|
|
||||||
|
SyncedPrefs: preload in redux state and fetch from there; improved performance.
|
||||||
6
upcoming-release-notes/3566.md
Normal file
6
upcoming-release-notes/3566.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
category: Bugfix
|
||||||
|
authors: [MatissJanis]
|
||||||
|
---
|
||||||
|
|
||||||
|
Reports: fix old reports page having empty blocks.
|
||||||
6
upcoming-release-notes/3573.md
Normal file
6
upcoming-release-notes/3573.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
category: Enhancements
|
||||||
|
authors: [joel-jeremy]
|
||||||
|
---
|
||||||
|
|
||||||
|
[Mobile] Update Budget to Budgeted to match the column name in mobile budget table
|
||||||
6
upcoming-release-notes/3574.md
Normal file
6
upcoming-release-notes/3574.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
category: Bugfix
|
||||||
|
authors: [joel-jeremy]
|
||||||
|
---
|
||||||
|
|
||||||
|
[Mobile] Fix budget list on mobile auto selecting a budget file under the Switch budget file menu
|
||||||
Reference in New Issue
Block a user