Format notes as clickable tags

This commit is contained in:
Joel Jeremy Marquez
2024-04-26 08:45:02 -07:00
parent 176c8466a4
commit f49c7f516e
5 changed files with 104 additions and 47 deletions

View File

@@ -1457,7 +1457,6 @@ class AccountInternal extends PureComponent {
addNotification,
accountsSyncing,
failedAccounts,
pushModal,
replaceModal,
showExtraBalances,
accountId,
@@ -1622,7 +1621,6 @@ class AccountInternal extends PureComponent {
</View>
) : null
}
pushModal={pushModal}
onSort={this.onSort}
sortField={this.state.sort.field}
ascDesc={this.state.sort.ascDesc}

View File

@@ -136,7 +136,9 @@ type CellProps = Omit<ComponentProps<typeof View>, 'children' | 'value'> & {
plain?: boolean;
exposed?: boolean;
children?: ReactNode | (() => ReactNode);
unexposedContent?: ReactNode;
unexposedContent?: (
props: ComponentProps<typeof UnexposedCellContent>,
) => ReactNode;
value?: string;
valueStyle?: CSSProperties;
onExpose?: (name: string) => void;
@@ -228,7 +230,9 @@ export function Cell({
}
}
>
{unexposedContent || (
{unexposedContent ? (
unexposedContent({ value, formatter })
) : (
<UnexposedCellContent value={value} formatter={formatter} />
)}
</View>

View File

@@ -1,5 +1,7 @@
import React, { useRef, useCallback, useLayoutEffect } from 'react';
import { useDispatch } from 'react-redux';
import { pushModal } from 'loot-core/client/actions';
import { send } from 'loot-core/src/platform/client/fetch';
import {
splitTransaction,
@@ -76,7 +78,6 @@ export function TransactionList({
dateFormat,
hideFraction,
addNotification,
pushModal,
renderEmpty,
onSort,
sortField,
@@ -88,6 +89,7 @@ export function TransactionList({
}) {
const transactionsLatest = useRef();
const navigate = useNavigate();
const dispatch = useDispatch();
useLayoutEffect(() => {
transactionsLatest.current = transactions;
@@ -161,13 +163,12 @@ export function TransactionList({
});
const onNavigateToSchedule = useCallback(scheduleId => {
pushModal('schedule-edit', { id: scheduleId });
dispatch(pushModal('schedule-edit', { id: scheduleId }));
});
return (
<TransactionTable
ref={tableRef}
pushModal={pushModal}
transactions={allTransactions}
loadMoreTransactions={loadMoreTransactions}
accounts={accounts}

View File

@@ -10,6 +10,7 @@ import React, {
useLayoutEffect,
useEffect,
} from 'react';
import { useDispatch } from 'react-redux';
import {
format as formatDate,
@@ -17,6 +18,7 @@ import {
isValid as isDateValid,
} from 'date-fns';
import { pushModal } from 'loot-core/client/actions';
import { useCachedSchedules } from 'loot-core/src/client/data-hooks/schedules';
import {
getAccountsById,
@@ -44,6 +46,7 @@ import {
} from 'loot-core/src/shared/util';
import { useMergedRefs } from '../../hooks/useMergedRefs';
import { useNavigate } from '../../hooks/useNavigate';
import { usePrevious } from '../../hooks/usePrevious';
import { useSelectedDispatch, useSelectedItems } from '../../hooks/useSelected';
import { useSplitsExpanded } from '../../hooks/useSplitsExpanded';
@@ -413,7 +416,8 @@ function HeaderCell({
width={width}
name={id}
alignItems={alignItems}
unexposedContent={
value={value}
unexposedContent={({ value: cellValue }) => (
<Button
type="bare"
onClick={onClick}
@@ -427,7 +431,7 @@ function HeaderCell({
marginRight,
}}
>
<UnexposedCellContent value={value} />
<UnexposedCellContent value={cellValue} />
{icon === 'asc' && (
<SvgArrowDown width={10} height={10} style={{ marginLeft: 5 }} />
)}
@@ -435,22 +439,20 @@ function HeaderCell({
<SvgArrowUp width={10} height={10} style={{ marginLeft: 5 }} />
)}
</Button>
}
)}
/>
);
}
function PayeeCell({
id,
payeeId,
accountId,
payee,
focused,
inherited,
payees,
accounts,
valueStyle,
transaction,
payee,
transferAcct,
isPreview,
onEdit,
@@ -462,16 +464,12 @@ function PayeeCell({
}) {
const isCreatingPayee = useRef(false);
// Filter out the account we're currently in as it is not a valid transfer
accounts = accounts.filter(account => account.id !== accountId);
payees = payees.filter(payee => payee.transfer_acct !== accountId);
return (
<CustomCell
width="flex"
name="payee"
textAlign="flex"
value={payeeId}
value={payee?.id}
valueStyle={{
...valueStyle,
...(inherited && { color: theme.tableTextInactive }),
@@ -488,7 +486,8 @@ function PayeeCell({
isCreatingPayee.current = false;
}
}}
unexposedContent={
formatter={() => getPayeePretty(transaction, payee, transferAcct)}
unexposedContent={props => (
<>
<PayeeIcons
transaction={transaction}
@@ -496,12 +495,9 @@ function PayeeCell({
onNavigateToTransferAccount={onNavigateToTransferAccount}
onNavigateToSchedule={onNavigateToSchedule}
/>
<UnexposedCellContent
value={payeeId}
formatter={() => getPayeePretty(transaction, payee, transferAcct)}
/>
<UnexposedCellContent {...props} />
</>
}
)}
>
{({
onBlur,
@@ -515,7 +511,7 @@ function PayeeCell({
<PayeeAutocomplete
payees={payees}
accounts={accounts}
value={payeeId}
value={payee?.id}
shouldSaveFromKey={shouldSaveFromKey}
inputProps={{
onBlur,
@@ -527,7 +523,7 @@ function PayeeCell({
focused={true}
onUpdate={(id, value) => onUpdate?.(value)}
onSelect={onSave}
onManagePayees={() => onManagePayees(payeeId)}
onManagePayees={() => onManagePayees(payee?.id)}
menuPortalTarget={undefined}
/>
);
@@ -643,7 +639,9 @@ const Transaction = memo(function Transaction(props) {
onNavigateToSchedule,
} = props;
const dispatch = useDispatch();
const dispatchSelected = useSelectedDispatch();
const navigate = useNavigate();
const [prevShowZero, setPrevShowZero] = useState(showZeroInDeposit);
const [prevTransaction, setPrevTransaction] = useState(originalTransaction);
@@ -685,13 +683,15 @@ const Transaction = memo(function Transaction(props) {
) {
if (showReconciliationWarning === false) {
setShowReconciliationWarning(true);
props.pushModal('confirm-transaction-edit', {
onConfirm: () => {
setShowReconciliationWarning(false);
onUpdateAfterConfirm(name, value);
},
confirmReason: 'editReconciled',
});
dispatch(
pushModal('confirm-transaction-edit', {
onConfirm: () => {
setShowReconciliationWarning(false);
onUpdateAfterConfirm(name, value);
},
confirmReason: 'editReconciled',
}),
);
}
} else {
onUpdateAfterConfirm(name, value);
@@ -700,12 +700,14 @@ const Transaction = memo(function Transaction(props) {
// Allow un-reconciling (unlocking) transactions
if (name === 'cleared' && transaction.reconciled) {
props.pushModal('confirm-transaction-edit', {
onConfirm: () => {
onUpdateAfterConfirm('reconciled', false);
},
confirmReason: 'unlockReconciled',
});
dispatch(
pushModal('confirm-transaction-edit', {
onConfirm: () => {
onUpdateAfterConfirm('reconciled', false);
},
confirmReason: 'unlockReconciled',
}),
);
}
}
@@ -756,6 +758,18 @@ const Transaction = memo(function Transaction(props) {
}
}
const onNoteTagClick = noteTag => {
const conditions = [
{ field: 'notes', op: 'contains', value: noteTag, type: 'string' },
];
navigate('/accounts', {
state: {
goBack: true,
conditions,
},
});
};
const {
id,
amount,
@@ -977,15 +991,14 @@ const Transaction = memo(function Transaction(props) {
<PayeeCell
/* Payee field for all transactions */
id={id}
payeeId={payeeId}
accountId={accountId}
payee={payee}
focused={focusedField === 'payee'}
inherited={inheritedFields && inheritedFields.has('payee')}
payees={payees}
accounts={accounts}
/* Filter out the account we're currently in as it is not a valid transfer */
accounts={accounts.filter(account => account.id !== accountId)}
payees={payees.filter(payee => payee.transfer_acct !== accountId)}
valueStyle={valueStyle}
transaction={transaction}
payee={payee}
transferAcct={transferAcct}
importedPayee={importedPayee}
isPreview={isPreview}
@@ -1009,7 +1022,13 @@ const Transaction = memo(function Transaction(props) {
exposed={focusedField === 'notes'}
focused={focusedField === 'notes'}
value={notes || ''}
style={{
overflowX: 'auto',
overflowY: 'hidden',
whiteSpace: 'nowrap',
}}
valueStyle={valueStyle}
formatter={value => notesTagFormatter(value, onNoteTagClick)}
onExpose={name => !isPreview && onEdit(id, name)}
inputProps={{
value: notes || '',
@@ -1626,7 +1645,6 @@ function TransactionTableInner({
onToggleSplit={props.onToggleSplit}
onNavigateToTransferAccount={onNavigateToTransferAccount}
onNavigateToSchedule={onNavigateToSchedule}
pushModal={props.pushModal}
/>
</>
);
@@ -2172,3 +2190,39 @@ export const TransactionTable = forwardRef((props, ref) => {
});
TransactionTable.displayName = 'TransactionTable';
function notesTagFormatter(value, onNoteTagClick) {
const words = value.split(' ');
return (
<>
{words.map((word, i) => {
if (word.startsWith('#') && word.length > 1) {
return (
<>
<Button
type="bare"
key={i}
style={{
display: 'inline-flex',
padding: 4,
borderRadius: 16,
backgroundColor: theme.pillBackground,
userSelect: 'none',
color: theme.pillText,
}}
onClick={e => {
e.stopPropagation();
onNoteTagClick?.(word);
}}
>
{word}
</Button>{' '}
</>
);
}
return `${word} `;
})}
</>
);
}

View File

@@ -176,11 +176,11 @@ export const checkboxBorderSelected = colorPalette.purple300;
export const checkboxShadowSelected = colorPalette.purple500;
export const checkboxToggleBackground = colorPalette.gray700;
export const pillBackground = colorPalette.navy800;
export const pillBackground = colorPalette.navy600;
export const pillBackgroundLight = colorPalette.navy900;
export const pillText = colorPalette.navy200;
export const pillTextHighlighted = colorPalette.purple200;
export const pillBorder = colorPalette.navy700;
export const pillBorder = colorPalette.navy600;
export const pillBorderDark = pillBorder;
export const pillBackgroundSelected = colorPalette.purple600;
export const pillTextSelected = colorPalette.navy150;