diff --git a/docker/dev/compose.yml b/docker/dev/compose.yml index 35ca3e62..20bcb4ed 100644 --- a/docker/dev/compose.yml +++ b/docker/dev/compose.yml @@ -2,7 +2,7 @@ name: split-pro-dev services: postgres: - image: ossapps/postgres:17.7-trixie + image: ossapps/postgres:18.3-trixie container_name: ${POSTGRES_CONTAINER_NAME:-splitpro-db} restart: always environment: @@ -11,7 +11,7 @@ services: - POSTGRES_DB=${POSTGRES_DB:-splitpro} - POSTGRES_PORT=${POSTGRES_PORT:-5432} volumes: - - database:/var/lib/postgresql/data + - database:/var/lib/postgresql command: > postgres -c shared_preload_libraries=pg_cron diff --git a/src/components/AddExpense/AddExpensePage.tsx b/src/components/AddExpense/AddExpensePage.tsx index f93d6bd6..11f4f578 100644 --- a/src/components/AddExpense/AddExpensePage.tsx +++ b/src/components/AddExpense/AddExpensePage.tsx @@ -25,8 +25,9 @@ import { UploadFile } from './UploadFile'; import { UserInput } from './UserInput'; import { CurrencyInput } from '../ui/currency-input'; import { CurrencyConversion } from '../Friend/CurrencyConversion'; -import { currencyConversion, getRatePrecision } from '~/utils/numbers'; -import { CURRENCY_CONVERSION_ICON } from '../ui/categoryIcons'; +import { currencyConversion } from '~/utils/numbers'; +import { CurrencyConversionIcon } from '../ui/categoryIcons'; +import { useSession } from 'next-auth/react'; export const AddOrEditExpensePage: React.FC<{ enableSendingInvites: boolean; @@ -73,6 +74,7 @@ export const AddOrEditExpensePage: React.FC<{ const addExpenseMutation = api.expense.addOrEditExpense.useMutation(); const updateProfile = api.user.updateUserDetail.useMutation(); + const { update } = useSession(); const onCurrencyPick = useCallback( (newCurrency: CurrencyCode) => { @@ -168,6 +170,15 @@ export const AddOrEditExpensePage: React.FC<{ navPromise() .then(() => resetState()) + .then(() => + update((session: any) => ({ + ...session, + user: { + ...(session?.user ?? {}), + currency, + }, + })), + ) .catch(console.error); } } @@ -206,6 +217,7 @@ export const AddOrEditExpensePage: React.FC<{ cronExpression, multipleTransactions, setSingleTransaction, + update, ]); const handleDescriptionChange = useCallback( @@ -261,7 +273,7 @@ export const AddOrEditExpensePage: React.FC<{ editingTargetCurrency={currency} > ); diff --git a/src/components/AddExpense/CurrencyPicker.tsx b/src/components/AddExpense/CurrencyPicker.tsx index f95b324d..1f7d3394 100644 --- a/src/components/AddExpense/CurrencyPicker.tsx +++ b/src/components/AddExpense/CurrencyPicker.tsx @@ -10,6 +10,7 @@ import { import { useTranslationWithUtils } from '~/hooks/useTranslationWithUtils'; import { GeneralPicker } from '../GeneralPicker'; import { Button } from '../ui/button'; +import { useCurrencyPreferenceStore } from '~/store/currencyPreferenceStore'; const FRANKFURTER_FILTERED_CURRENCIES = Object.fromEntries( Object.entries(CURRENCIES).filter(([code]) => FRANKFURTER_CURRENCIES.includes(code)), @@ -27,12 +28,14 @@ function CurrencyPickerInner({ showOnlyFrankfurter?: boolean; }) { const { t, getCurrencyName } = useTranslationWithUtils(['currencies']); + const { recentCurrencies, addToRecentCurrencies } = useCurrencyPreferenceStore(); const onSelect = useCallback( (currentValue: string) => { onCurrencyPick(parseCurrencyCode(currentValue)); + addToRecentCurrencies(parseCurrencyCode(currentValue)); }, - [onCurrencyPick], + [onCurrencyPick, addToRecentCurrencies], ); const trigger = useMemo( @@ -62,6 +65,20 @@ function CurrencyPickerInner({ }, [getCurrencyName], ); + const recentCurrencyObjects = useMemo( + () => recentCurrencies.map((code) => CURRENCIES[code]), + [recentCurrencies], + ); + const items = useMemo(() => { + const baseItems = showOnlyFrankfurter + ? Object.values(FRANKFURTER_FILTERED_CURRENCIES) + : Object.values(CURRENCIES); + const uniqueItems = [ + ...recentCurrencyObjects, + ...baseItems.filter((c) => !recentCurrencies.includes(c.code as CurrencyCode)), + ]; + return uniqueItems; + }, [showOnlyFrankfurter, recentCurrencyObjects, recentCurrencies]); return ( {Object.entries(balances).map(([friendId, perFriendBalances]) => { - const friend = userMap[+friendId]!.user; + const friend = userMap[Number(friendId)]!.user; return ( @@ -180,7 +180,7 @@ export const BalanceList: React.FC<{ groupId={groupBalances[0]!.groupId!} > diff --git a/src/components/Expense/ExpenseList.tsx b/src/components/Expense/ExpenseList.tsx index 7372f868..b6c1242a 100644 --- a/src/components/Expense/ExpenseList.tsx +++ b/src/components/Expense/ExpenseList.tsx @@ -5,11 +5,7 @@ import Link from 'next/link'; import { useRouter } from 'next/router'; import React from 'react'; import { toast } from 'sonner'; -import { - CURRENCY_CONVERSION_ICON, - CategoryIcon, - SETTLEUP_ICON, -} from '~/components/ui/categoryIcons'; +import { CategoryIcon, CurrencyConversionIcon, SettleupIcon } from '~/components/ui/categoryIcons'; import { useTranslationWithUtils } from '~/hooks/useTranslationWithUtils'; import { cn } from '~/lib/utils'; import type { ExpenseRouter } from '~/server/api/routers/expense'; @@ -155,7 +151,7 @@ const Settlement: ExpenseComponent = ({ e, userId }) => {
{toUIDate(e.expenseDate)}
- +

{displayName(e.paidByUser, userId)}{' '} @@ -186,7 +182,7 @@ const CurrencyConversion: ExpenseComponent = ({ e, userId }) => {

{toUIDate(e.expenseDate)}
- +

{getCurrencyHelpersCached(e.currency).toUIString(e.amount)} ➡️{' '} diff --git a/src/components/ui/categoryIcons.tsx b/src/components/ui/categoryIcons.tsx index d6c65db6..5aa2116d 100644 --- a/src/components/ui/categoryIcons.tsx +++ b/src/components/ui/categoryIcons.tsx @@ -101,17 +101,17 @@ export const CategoryIcons: Record = { hotel: Hotel, }; -export const CURRENCY_CONVERSION_ICON = DollarSign; +export const CurrencyConversionIcon = DollarSign; -export const SETTLEUP_ICON = HandCoins; +export const SettleupIcon = HandCoins; -export const DEFAULT_CATEGORY_ICON = CategoryIcons[DEFAULT_CATEGORY]; +export const DefaultCategoryIcon = CategoryIcons[DEFAULT_CATEGORY]; export const CategoryIcon: React.FC<{ category?: string; splitType?: SplitType } & LucideProps> = ({ category = DEFAULT_CATEGORY, ...props }) => { - const Icon = CategoryIcons[category as CategoryItem] ?? DEFAULT_CATEGORY_ICON; + const Icon = CategoryIcons[category as CategoryItem] ?? DefaultCategoryIcon; return ; }; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index a0cc1e69..176a0a62 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -120,8 +120,6 @@ const Auth: React.FC<{ Page: NextPageWithUser; pageProps: any }> = ({ Page, page useEffect(() => { if ('authenticated' === status && data.user) { - setCurrency(parseCurrencyCode(data.user.currency)); - if (!data.user.preferredLanguage) { // If user has no preferred language, set it to the current locale const currentLocale = router.locale ?? 'en'; diff --git a/src/pages/auth/VerificationStep.tsx b/src/pages/auth/VerificationStep.tsx index 708be6d7..f9d34c98 100644 --- a/src/pages/auth/VerificationStep.tsx +++ b/src/pages/auth/VerificationStep.tsx @@ -88,6 +88,7 @@ const OTPInput = ({ field }) => ( maxLength={5} pattern={REGEXP_ONLY_DIGITS_AND_CHARS} inputMode="text" + autoFocus {...field} > diff --git a/src/store/currencyPreferenceStore.ts b/src/store/currencyPreferenceStore.ts index 9a2024c3..ca663f23 100644 --- a/src/store/currencyPreferenceStore.ts +++ b/src/store/currencyPreferenceStore.ts @@ -8,7 +8,11 @@ const DEFAULT_KEY = 'global'; export type CurrencyPreference = CurrencyCode | typeof SHOW_ALL_VALUE; +const MAX_RECENT_CURRENCIES = 5; + interface CurrencyPreferenceState { + recentCurrencies: CurrencyCode[]; + addToRecentCurrencies: (currency: CurrencyCode) => void; preferences: Record; setPreference: (key?: number | string, currency?: string) => void; getPreference: (key?: number | string) => CurrencyPreference; @@ -18,7 +22,13 @@ interface CurrencyPreferenceState { export const useCurrencyPreferenceStore = create()( persist( (set, get) => ({ + recentCurrencies: [], preferences: {}, + addToRecentCurrencies: (currency) => + set((state) => { + const updatedRecent = [currency, ...state.recentCurrencies.filter((c) => c !== currency)]; + return { recentCurrencies: updatedRecent.slice(0, MAX_RECENT_CURRENCIES) }; + }), setPreference: (key = DEFAULT_KEY, currency = SHOW_ALL_VALUE) => set((state) => ({ preferences: { @@ -34,7 +44,7 @@ export const useCurrencyPreferenceStore = create()( }), }), { - name: 'currency-preferences', // sessionStorage key + name: 'currency-preferences', // SessionStorage key storage: createJSONStorage(() => sessionStorage), }, ),