diff --git a/packages/desktop-client/src/components/manager/BudgetList.jsx b/packages/desktop-client/src/components/manager/BudgetList.tsx similarity index 77% rename from packages/desktop-client/src/components/manager/BudgetList.jsx rename to packages/desktop-client/src/components/manager/BudgetList.tsx index b012bdc06a0..9e30b3a1b75 100644 --- a/packages/desktop-client/src/components/manager/BudgetList.jsx +++ b/packages/desktop-client/src/components/manager/BudgetList.tsx @@ -1,4 +1,5 @@ -import React, { useState, useRef } from 'react'; +import type React from 'react'; +import { useState, useRef, type CSSProperties } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { @@ -12,6 +13,12 @@ import { pushModal, } from 'loot-core/client/actions'; import { isNonProductionEnvironment } from 'loot-core/src/shared/environment'; +import { + type File, + type LocalFile, + type SyncableLocalFile, + type SyncedLocalFile, +} from 'loot-core/types/file'; import { useInitialMount } from '../../hooks/useInitialMount'; import { useLocalPref } from '../../hooks/useLocalPref'; @@ -32,7 +39,7 @@ import { Popover } from '../common/Popover'; import { Text } from '../common/Text'; import { View } from '../common/View'; -function getFileDescription(file) { +function getFileDescription(file: File) { if (file.state === 'unknown') { return ( 'This is a cloud-based file but its state is unknown because you ' + @@ -50,8 +57,14 @@ function getFileDescription(file) { return null; } -function FileMenu({ onDelete, onClose }) { - function onMenuSelect(type) { +function FileMenu({ + onDelete, + onClose, +}: { + onDelete: () => void; + onClose: () => void; +}) { + function onMenuSelect(type: string) { onClose(); switch (type) { @@ -83,7 +96,7 @@ function FileMenu({ onDelete, onClose }) { ); } -function FileMenuButton({ state, onDelete }) { +function FileMenuButton({ onDelete }: { onDelete: () => void }) { const triggerRef = useRef(null); const [menuOpen, setMenuOpen] = useState(false); @@ -106,17 +119,13 @@ function FileMenuButton({ state, onDelete }) { isOpen={menuOpen} onOpenChange={() => setMenuOpen(false)} > - setMenuOpen(false)} - /> + setMenuOpen(false)} /> ); } -function FileState({ file }) { +function FileState({ file }: { file: File }) { let Icon; let status; let color; @@ -164,10 +173,20 @@ function FileState({ file }) { ); } -function File({ file, quickSwitchMode, onSelect, onDelete }) { +function FileItem({ + file, + quickSwitchMode, + onSelect, + onDelete, +}: { + file: File; + quickSwitchMode: boolean; + onSelect: (file: File) => void; + onDelete: (file: File) => void; +}) { const selecting = useRef(false); - async function _onSelect(file) { + async function _onSelect(file: File) { // Never allow selecting the file while uploading/downloading, and // make sure to never allow duplicate clicks if (!selecting.current) { @@ -180,7 +199,7 @@ function File({ file, quickSwitchMode, onSelect, onDelete }) { return ( _onSelect(file)} - title={getFileDescription(file)} + title={getFileDescription(file) || ''} style={{ flexDirection: 'row', justifyContent: 'space-between', @@ -193,7 +212,7 @@ function File({ file, quickSwitchMode, onSelect, onDelete }) { flexShrink: 0, cursor: 'pointer', ':hover': { - backgroundColor: theme.hover, + backgroundColor: theme.menuItemBackgroundHover, }, }} > @@ -219,15 +238,27 @@ function File({ file, quickSwitchMode, onSelect, onDelete }) { /> )} - {!quickSwitchMode && ( - onDelete(file)} /> - )} + {!quickSwitchMode && onDelete(file)} />} ); } -function BudgetFiles({ files, quickSwitchMode, onSelect, onDelete }) { +function BudgetFiles({ + files, + quickSwitchMode, + onSelect, + onDelete, +}: { + files: File[]; + quickSwitchMode: boolean; + onSelect: (file: File) => void; + onDelete: (file: File) => void; +}) { + function isLocalFile(file: File): file is LocalFile { + return file.state === 'local'; + } + return ( ) : ( files.map(file => ( - void; +}) { const [loading, setLoading] = useState(false); async function _onRefresh() { @@ -288,7 +325,13 @@ function RefreshButton({ style, onRefresh }) { ); } -function BudgetListHeader({ quickSwitchMode, onRefresh }) { +function BudgetListHeader({ + quickSwitchMode, + onRefresh, +}: { + quickSwitchMode: boolean; + onRefresh: () => void; +}) { return ( state.budgets.allFiles || []); const [id] = useLocalPref('id'); - const files = id ? allFiles.filter(f => f.id !== id) : allFiles; + // Remote files do not have the 'id' field + function isNonRemoteFile( + file: File, + ): file is LocalFile | SyncableLocalFile | SyncedLocalFile { + return file.state !== 'remote'; + } + const nonRemoteFiles = allFiles.filter(isNonRemoteFile); + const files = id ? nonRemoteFiles.filter(f => f.id !== id) : allFiles; const [creating, setCreating] = useState(false); const { isNarrowWidth } = useResponsive(); @@ -324,7 +374,7 @@ export function BudgetList({ showHeader = true, quickSwitchMode = false }) { } : {}; - const onCreate = ({ testMode } = {}) => { + const onCreate = ({ testMode = false } = {}) => { if (!creating) { setCreating(true); dispatch(createBudget({ testMode })); @@ -341,6 +391,22 @@ export function BudgetList({ showHeader = true, quickSwitchMode = false }) { refresh(); } + const onSelect = (file: File): void => { + const isRemoteFile = file.state === 'remote'; + + if (!id) { + if (isRemoteFile) { + dispatch(downloadBudget(file.cloudFileId)); + } else { + dispatch(loadBudget(file.id)); + } + } else if (!isRemoteFile && file.id !== id) { + dispatch(closeAndLoadBudget(file.id)); + } else if (isRemoteFile) { + dispatch(closeAndDownloadBudget(file.cloudFileId)); + } + }; + return ( { - if (!id) { - if (file.state === 'remote') { - dispatch(downloadBudget(file.cloudFileId)); - } else { - dispatch(loadBudget(file.id)); - } - } else if (file.id !== id) { - if (file.state === 'remote') { - dispatch(closeAndDownloadBudget(file.cloudFileId)); - } else { - dispatch(closeAndLoadBudget(file.id)); - } - } - }} + onSelect={onSelect} onDelete={file => dispatch(pushModal('delete-budget', { file }))} /> {!quickSwitchMode && ( @@ -408,7 +460,7 @@ export function BudgetList({ showHeader = true, quickSwitchMode = false }) {