Skip to content

Commit

Permalink
Limit file upload size on the client (#84)
Browse files Browse the repository at this point in the history
  • Loading branch information
scastiel committed Feb 6, 2024
1 parent be0964d commit 0e6a2bd
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 1 deletion.
15 changes: 14 additions & 1 deletion src/app/groups/[groupId]/expenses/create-from-receipt-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {
import { ToastAction } from '@/components/ui/toast'
import { useToast } from '@/components/ui/use-toast'
import { useMediaQuery } from '@/lib/hooks'
import { formatCurrency, formatExpenseDate } from '@/lib/utils'
import { formatCurrency, formatExpenseDate, formatFileSize } from '@/lib/utils'
import { Category } from '@prisma/client'
import { ChevronRight, FileQuestion, Loader2, Receipt } from 'lucide-react'
import { getImageData, usePresignedUpload } from 'next-s3-upload'
Expand All @@ -40,6 +40,8 @@ type Props = {
categories: Category[]
}

const MAX_FILE_SIZE = 5 * 1024 ** 2

export function CreateFromReceiptButton({
groupId,
groupCurrency,
Expand All @@ -56,6 +58,17 @@ export function CreateFromReceiptButton({
const isDesktop = useMediaQuery('(min-width: 640px)')

const handleFileChange = async (file: File) => {
if (file.size > MAX_FILE_SIZE) {
toast({
title: 'The file is too big',
description: `The maximum file size you can upload is ${formatFileSize(
MAX_FILE_SIZE,
)}. Yours is ${formatFileSize(file.size)}.`,
variant: 'destructive',
})
return
}

const upload = async () => {
try {
setPending(true)
Expand Down
14 changes: 14 additions & 0 deletions src/components/expense-documents-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { ToastAction } from '@/components/ui/toast'
import { useToast } from '@/components/ui/use-toast'
import { randomId } from '@/lib/api'
import { ExpenseFormValues } from '@/lib/schemas'
import { formatFileSize } from '@/lib/utils'
import { Loader2, Plus, Trash, X } from 'lucide-react'
import { getImageData, usePresignedUpload } from 'next-s3-upload'
import Image from 'next/image'
Expand All @@ -27,12 +28,25 @@ type Props = {
updateDocuments: (documents: ExpenseFormValues['documents']) => void
}

const MAX_FILE_SIZE = 5 * 1024 ** 2

export function ExpenseDocumentsInput({ documents, updateDocuments }: Props) {
const [pending, setPending] = useState(false)
const { FileInput, openFileDialog, uploadToS3 } = usePresignedUpload() // use presigned uploads to addtionally support providers other than AWS
const { toast } = useToast()

const handleFileChange = async (file: File) => {
if (file.size > MAX_FILE_SIZE) {
toast({
title: 'The file is too big',
description: `The maximum file size you can upload is ${formatFileSize(
MAX_FILE_SIZE,
)}. Yours is ${formatFileSize(file.size)}.`,
variant: 'destructive',
})
return
}

const upload = async () => {
try {
setPending(true)
Expand Down
13 changes: 13 additions & 0 deletions src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,16 @@ export function formatCurrency(currency: string, amount: number) {
const formattedAmount = format.format(amount / 100)
return `${currency} ${formattedAmount}`
}

export function formatFileSize(size: number) {
const formatNumber = (num: number) =>
num.toLocaleString('en-US', {
minimumFractionDigits: 0,
maximumFractionDigits: 1,
})

if (size > 1024 ** 3) return `${formatNumber(size / 1024 ** 3)} GB`
if (size > 1024 ** 2) return `${formatNumber(size / 1024 ** 2)} MB`
if (size > 1024) return `${formatNumber(size / 1024)} kB`
return `${formatNumber(size)} B`
}

0 comments on commit 0e6a2bd

Please sign in to comment.