Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(mobile): add blob attachment and two-line transaction item #350

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions apps/mobile/app/(app)/(tabs)/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,12 @@ export default function HomeScreen() {
<TransactionItem transaction={transaction} />
)}
renderSectionHeader={({ section: { title, sum } }) => (
<View className="mx-6 flex-row justify-between border-muted-foreground/20 border-b bg-background py-2 pt-4 align-center">
<Text className="text-muted-foreground">{title}</Text>
<View className="mx-6 flex-row justify-between border-border border-b-[0.5px] bg-background py-2 pt-4 align-center">
<Text className="text-foreground">{title}</Text>
<AmountFormat
amount={sum}
className="font-semiBold text-md text-muted-foreground"
className="text-muted-foreground"
size="sm"
displayNegativeSign
displayPositiveSign
convertToDefaultCurrency
Expand Down
4 changes: 2 additions & 2 deletions apps/mobile/app/(app)/(tabs)/settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ export default function SettingsScreen() {
<Text className="!text-lg font-semiBold text-foreground">
{t(i18n)`Get 6pm Pro`}
</Text>
<Text className="!text-sm text-muted-foreground">
<Text className="!text-sm font-regular text-muted-foreground">
{t(i18n)`Unlocks full AI power and more!`}
</Text>
</View>
Expand Down Expand Up @@ -315,7 +315,7 @@ export default function SettingsScreen() {
className="!px-6 justify-start gap-6"
>
<LogOutIcon className="h-5 w-5 text-red-500" />
<Text className="font-normal text-red-500 group-active:text-red-500">
<Text className="font-regular text-red-500 group-active:text-red-500">
{t(i18n)`Sign out`}
</Text>
</Button>
Expand Down
7 changes: 7 additions & 0 deletions apps/mobile/app/(app)/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,13 @@ export default function AuthenticatedLayout() {
headerTitle: t(i18n)`New budget`,
}}
/>
<Stack.Screen
name="blob-viewer"
options={{
presentation: 'modal',
headerTitle: '',
}}
/>
</Stack>
</View>
)
Expand Down
18 changes: 18 additions & 0 deletions apps/mobile/app/(app)/blob-viewer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useLocalSearchParams } from 'expo-router'
import { Image, View } from 'react-native'

export default function BlobViewerScreen() {
const { blobObjectUrl } = useLocalSearchParams()
if (!blobObjectUrl) {
return null
}
return (
<View className="flex-1 bg-background">
<Image
source={{ uri: blobObjectUrl as string }}
className="h-full w-full"
resizeMode="contain"
/>
</View>
)
}
7 changes: 4 additions & 3 deletions apps/mobile/app/(app)/budget/[budgetId]/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -251,11 +251,12 @@ export default function BudgetDetailScreen() {
<TransactionItem transaction={transaction} />
)}
renderSectionHeader={({ section: { title, sum } }) => (
<View className="mx-6 flex-row justify-between border-muted-foreground/20 border-b bg-background py-2 pt-4 align-center">
<Text className="text-muted-foreground">{title}</Text>
<View className="mx-6 flex-row justify-between border-border border-b-[0.5px] bg-background py-2 pt-4 align-center">
<Text className="text-foreground">{title}</Text>
<AmountFormat
amount={sum}
className="font-semiBold text-md text-muted-foreground"
className="text-muted-foreground"
size="sm"
displayNegativeSign
displayPositiveSign
convertToDefaultCurrency
Expand Down
1 change: 1 addition & 0 deletions apps/mobile/app/(app)/transaction/[transactionId].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ export default function EditRecordScreen() {
onSubmit={handleUpdate}
onCancel={router.back}
onDelete={handleDelete}
blobAttachments={transaction?.blobAttachments}
/>
<PortalHost name="transaction-form" />
</View>
Expand Down
85 changes: 57 additions & 28 deletions apps/mobile/app/(app)/transaction/new-record.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,25 @@ import { useLingui } from '@lingui/react'
import { createId } from '@paralleldrive/cuid2'
import { PortalHost, useModalPortalRoot } from '@rn-primitives/portal'
import * as Haptics from 'expo-haptics'
import { useLocalSearchParams, useNavigation, useRouter } from 'expo-router'
import {
Link,
useLocalSearchParams,
useNavigation,
useRouter,
} from 'expo-router'
import { CameraIcon, KeyboardIcon, Trash2Icon } from 'lucide-react-native'
import { useEffect, useRef, useState } from 'react'
import { useForm } from 'react-hook-form'
import {
ActivityIndicator,
Alert,
Dimensions,
Image,
Keyboard,
ScrollView,
View,
} from 'react-native'
import { z } from 'zod'

const DEFAULT_CATEGORY_CONFIG = {
type: 'EXPENSE',
Expand All @@ -54,7 +61,13 @@ export default function NewRecordScreen() {
const { expenseCategories } = useCategoryList()

const params = useLocalSearchParams()
const parsedParams = zUpdateTransaction.parse(params)
const { blobObjectUrl, blobObjectId, ...parsedParams } = zUpdateTransaction
.extend({
blobObjectId: z.string().nullable().optional(),
blobObjectUrl: z.string().nullable().optional(),
})
.parse(params)

const defaultValues: TransactionFormValues = {
date: new Date(),
amount: 0,
Expand All @@ -80,30 +93,45 @@ export default function NewRecordScreen() {
// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
useEffect(() => {
navigation.setOptions({
headerTitle: () => (
<Tabs
value={page.toString()}
onValueChange={(value) => {
setPage(Number(value))
Keyboard.dismiss()
ref.current?.scrollTo({
y: 0,
x: value === '0' ? 0 : width,
animated: true,
})
}}
className="w-[160px]"
>
<TabsList className="bg-secondary">
<TabsTrigger value="0">
<KeyboardIcon className="!text-primary size-6" />
</TabsTrigger>
<TabsTrigger value="1">
<CameraIcon className="!text-primary size-6" />
</TabsTrigger>
</TabsList>
</Tabs>
),
headerTitle: () =>
blobObjectUrl ? (
<Link
href={{
pathname: '/blob-viewer',
params: {
blobObjectUrl,
},
}}
>
<Image
source={{ uri: blobObjectUrl }}
className="size-12 rounded-md border-4 border-secondary bg-muted"
/>
</Link>
) : (
<Tabs
value={page.toString()}
onValueChange={(value) => {
setPage(Number(value))
Keyboard.dismiss()
ref.current?.scrollTo({
y: 0,
x: value === '0' ? 0 : width,
animated: true,
})
}}
className="w-[160px]"
>
<TabsList className="bg-secondary">
<TabsTrigger value="0">
<KeyboardIcon className="!text-primary size-6" />
</TabsTrigger>
<TabsTrigger value="1">
<CameraIcon className="!text-primary size-6" />
</TabsTrigger>
</TabsList>
</Tabs>
),
headerRight: () =>
parsedParams?.id ? (
<Button
Expand All @@ -114,11 +142,11 @@ export default function NewRecordScreen() {
router.back()
}}
>
<Trash2Icon className="size-6 text-destructive" />
<Trash2Icon className="size-6 text-foreground" />
</Button>
) : null,
})
}, [page])
}, [page, blobObjectUrl])

const handleCreateTransaction = async (values: TransactionFormValues) => {
try {
Expand All @@ -134,6 +162,7 @@ export default function NewRecordScreen() {
data: {
...values,
amount: values.categoryId ? values.amount : -Math.abs(values.amount),
blobAttachmentIds: blobObjectId ? [blobObjectId] : undefined,
},
})
} catch (error) {
Expand Down
4 changes: 2 additions & 2 deletions apps/mobile/components/budget/budget-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@ export const BudgetItem: FC<BudgetItemProps> = ({ budget }) => {
>
<Pressable className="mx-6 mt-1 mb-3 justify-between gap-4 rounded-lg border border-border p-4">
<View className="flex-row items-center justify-between gap-6">
<View className="flex-1 gap-2">
<View className="flex-1 gap-3">
<Text
numberOfLines={1}
className="line-clamp-1 flex-1 font-semiBold text-lg"
className="line-clamp-1 flex-1 font-semiBold text-xl"
>
{budget.name}
</Text>
Expand Down
12 changes: 11 additions & 1 deletion apps/mobile/components/common/bottom-sheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import { useColorPalette } from '@/hooks/use-color-palette'
import {
BottomSheetBackdrop,
type BottomSheetBackdropProps,
type BottomSheetBackgroundProps,
BottomSheetModal,
type BottomSheetModalProps,
} from '@gorhom/bottom-sheet'
import type { BottomSheetModalMethods } from '@gorhom/bottom-sheet/lib/typescript/types'
import { forwardRef, useCallback } from 'react'
import { View } from 'react-native'
import { FullWindowOverlay } from 'react-native-screens'

export const BottomSheet = forwardRef<
Expand Down Expand Up @@ -34,12 +36,20 @@ export const BottomSheet = forwardRef<
[],
)

const backgroundComponent = useCallback(
(props: BottomSheetBackgroundProps) => (
<View className="overflow-hidden rounded-xl bg-background" {...props} />
),
[],
)

return (
<BottomSheetModal
ref={ref}
backgroundStyle={{ backgroundColor: getColor('--background') }}
handleIndicatorStyle={{ backgroundColor: getColor('--foreground') }}
backdropComponent={backdropComponent}
containerComponent={containerComponent}
backgroundComponent={backgroundComponent}
keyboardBehavior="extend"
enablePanDownToClose
enableDismissOnClose
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ type BurndownChartProps = {
}

export function BurndownChart({
totalBudget,
// totalBudget,
averagePerDay,
data = [],
anchorDay = new Date().getDate(),
Expand Down Expand Up @@ -113,7 +113,7 @@ export function BurndownChart({
axisSide: 'right',
labelPosition: 'inset',
labelColor: getColor('--foreground', { alpha: 0.5 }),
tickValues: [0, totalBudget / 2, totalBudget],
// tickValues: [0, totalBudget / 2, totalBudget],
lineWidth: 0,
},
]}
Expand Down
2 changes: 1 addition & 1 deletion apps/mobile/components/common/currency-sheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export function CurrencySheetList({ onSelect, value }: CurrencySheetListProps) {
<BottomSheetTextInput
placeholder={t(i18n)`Search currency...`}
placeholderClassName="text-muted-foreground font-regular"
className="mb-3 web:flex h-11 web:w-full rounded-md border border-input bg-background px-3 web:py-2 pl-11 font-regular native:text-base text-foreground text-sm native:leading-[1.25] web:ring-offset-background file:border-0 file:bg-transparent file:font-medium placeholder:text-muted-foreground web:focus-visible:outline-none web:focus-visible:ring-2 web:focus-visible:ring-ring web:focus-visible:ring-offset-2 lg:text-sm"
className="mb-3 web:flex h-11 web:w-full rounded-md border border-input bg-background px-3 web:py-2 pl-11 font-regular font-regular native:text-base text-foreground text-sm native:leading-[1.25] web:ring-offset-background file:border-0 file:bg-transparent file:font-medium placeholder:text-muted-foreground web:focus-visible:outline-none web:focus-visible:ring-2 web:focus-visible:ring-ring web:focus-visible:ring-offset-2 lg:text-sm"
value={searchValue}
onChangeText={setSearchValue}
/>
Expand Down
2 changes: 1 addition & 1 deletion apps/mobile/components/form-fields/currency-field.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export function CurrencyField({
className,
)}
>
<Text className="font-medium text-foreground text-sm">{value}</Text>
<Text className="font-regular text-foreground text-sm">{value}</Text>
</Button>
<BottomSheet ref={sheetRef} index={0} snapPoints={['50%', '87%']}>
<CurrencySheetList
Expand Down
4 changes: 2 additions & 2 deletions apps/mobile/components/home/category-chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,8 @@ export function CategoryChart({
style={{ opacity }}
/>
)}
<Text>{item.name}</Text>
<Text className={'font-normal'}>{item.percentage}%</Text>
<Text className="font-regular">{item.name}</Text>
<Text className="font-regular">{item.percentage}%</Text>
</Button>
)
}}
Expand Down
7 changes: 4 additions & 3 deletions apps/mobile/components/home/header.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { useUser } from '@clerk/clerk-expo'
import { useRouter } from 'expo-router'
import { Text, TouchableOpacity, View } from 'react-native'
import { TouchableOpacity, View } from 'react-native'
import { UserAvatar } from '../common/user-avatar'
import { Text } from '../ui/text'
import { type HomeFilter, SelectFilter } from './select-filter'
import { SelectWalletAccount } from './select-wallet-account'

Expand Down Expand Up @@ -30,8 +31,8 @@ export function HomeHeader({
>
<UserAvatar user={user!} />
</TouchableOpacity>
<View className="flex-1">
<Text className="line-clamp-1 font-medium text-muted-foreground text-sm">
<View className="flex-1 gap-1">
<Text className="line-clamp-1 font-semiBold text-muted-foreground text-sm">
{user?.fullName ?? user?.primaryEmailAddress?.emailAddress}
</Text>
<SelectWalletAccount
Expand Down
2 changes: 1 addition & 1 deletion apps/mobile/components/home/select-filter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export function SelectFilter({
/>
{value !== HomeFilter.All && (
<SelectValue
className={cn('font-medium text-primary-foreground')}
className={cn('font-semiBold text-primary-foreground')}
placeholder={t(i18n)`All Accounts`}
>
{value}
Expand Down
2 changes: 1 addition & 1 deletion apps/mobile/components/home/select-wallet-account.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export function SelectWalletAccount({
className="!border-none !border-transparent !py-0 !h-6 flex-row items-center gap-2 self-start px-0"
>
<SelectValue
className="font-medium text-foreground"
className="font-medium text-base text-foreground"
placeholder={t(i18n)`All Accounts`}
>
{value}
Expand Down
2 changes: 1 addition & 1 deletion apps/mobile/components/home/wallet-statistics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ export function WalletStatistics({
className="!border-0 h-auto native:h-auto flex-col items-center gap-3"
>
<View className="self-center">
<Text className="w-fit self-center text-center text-muted-foreground leading-tight">
<Text className="w-fit self-center text-center text-foreground leading-tight">
{options.find((option) => option.value === view)?.label}
</Text>
</View>
Expand Down
6 changes: 3 additions & 3 deletions apps/mobile/components/setting/profile-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -166,16 +166,16 @@ export function ProfileCard() {
fallbackClassName="bg-background"
className="h-16 w-16"
/>
<View className="flex-1 justify-center gap-1">
<Text className="line-clamp-1 font-medium">
<View className="flex-1 justify-center gap-1.5">
<Text className="line-clamp-1 font-semiBold">
{user?.fullName ?? user?.primaryEmailAddress?.emailAddress}
</Text>
<Badge
variant="default"
className="flex-row gap-1 self-start rounded-md"
>
{isPro && <CrownIcon className="size-4 text-primary-foreground" />}
<Text className="font-medium text-xs">
<Text className="font-medium text-sm">
{isWealth
? t(i18n)`Wealth`
: isGrowth
Expand Down
Loading
Loading