Skip to content
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
13 changes: 6 additions & 7 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@ ARG NODE_VERSION=22.16.0

FROM node:${NODE_VERSION}-alpine${ALPINE_VERSION} AS base

ARG APP_VERSION
ARG GIT_SHA

ENV SKIP_ENV_VALIDATION="true"
ENV DOCKER_OUTPUT=1
ENV NEXT_TELEMETRY_DISABLED=1
ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0
ENV APP_VERSION=${APP_VERSION}
ENV GIT_SHA=${GIT_SHA}

ENV NEXT_PUBLIC_APP_VERSION=${APP_VERSION}
ENV NEXT_PUBLIC_GIT_SHA=${GIT_SHA}

RUN apk update && apk add --no-cache libc6-compat

Expand All @@ -24,9 +28,6 @@ RUN pnpm build

FROM node:${NODE_VERSION}-alpine${ALPINE_VERSION} AS release

ARG APP_VERSION
ARG GIT_SHA

ENV NODE_ENV=production
ENV DOCKER_OUTPUT=1

Expand All @@ -43,8 +44,6 @@ COPY --from=base /app/prisma/migrations ./prisma/migrations

# set this so it throws error where starting server
ENV SKIP_ENV_VALIDATION="false"
ENV APP_VERSION=${APP_VERSION}
ENV GIT_SHA=${GIT_SHA}

COPY ./start.sh ./start.sh

Expand Down
12 changes: 7 additions & 5 deletions src/components/Account/DebugInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const DebugInfo: React.FC<React.PropsWithChildren> = ({ children }) => {
}
};

if (env.NEXT_PUBLIC_VERSION) {
if (env.NEXT_PUBLIC_APP_VERSION) {
void fetchLatestVersion();
}
}, []);
Expand All @@ -46,8 +46,8 @@ export const DebugInfo: React.FC<React.PropsWithChildren> = ({ children }) => {
if (env.NEXT_PUBLIC_GIT_SHA) {
debugInfo.push(`${t('account.debug_info_details.git')}: ${env.NEXT_PUBLIC_GIT_SHA}`);
}
if (env.NEXT_PUBLIC_VERSION) {
debugInfo.push(`${t('account.debug_info_details.version')} ${env.NEXT_PUBLIC_VERSION}`);
if (env.NEXT_PUBLIC_APP_VERSION) {
debugInfo.push(`${t('account.debug_info_details.version')} ${env.NEXT_PUBLIC_APP_VERSION}`);
}
try {
void navigator.clipboard.writeText(debugInfo.join('\n'));
Expand Down Expand Up @@ -78,10 +78,12 @@ export const DebugInfo: React.FC<React.PropsWithChildren> = ({ children }) => {
/>
<DebugInfoRow
label={t('account.debug_info_details.version')}
value={env.NEXT_PUBLIC_VERSION}
value={env.NEXT_PUBLIC_APP_VERSION}
className="mt-4"
/>
{newVersion && env.NEXT_PUBLIC_VERSION && newVersion !== env.NEXT_PUBLIC_VERSION ? (
{newVersion &&
env.NEXT_PUBLIC_APP_VERSION &&
newVersion !== env.NEXT_PUBLIC_APP_VERSION ? (
<p className="mt-4 text-sm text-yellow-600">
{t('account.debug_info_details.new_version_available')}: {newVersion}
</p>
Expand Down
13 changes: 7 additions & 6 deletions src/components/Account/UpdateDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { zodResolver } from '@hookform/resolvers/zod';
import { Camera, Pencil, X } from 'lucide-react';
import Cropper, { type Area } from 'react-easy-crop';
import React, { useCallback, useMemo, useRef, useState } from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import { useForm } from 'react-hook-form';
import { type TFunction, useTranslation } from 'next-i18next';
import { toast } from 'sonner';
import { z } from 'zod';

import { env } from '~/env';
import { prepareImageForUpload, uploadImage, validateUploadSize } from '~/utils/imageUpload';

import { AppDrawer } from '../ui/drawer';
Expand All @@ -17,6 +16,7 @@ import { Input } from '../ui/input';
import { Label } from '../ui/label';
import { Slider } from '../ui/slider';
import { Button } from '../ui/button';
import { useAppStore } from '~/store/appStore';

const createImage = async (url: string) => {
const image = new Image();
Expand Down Expand Up @@ -91,6 +91,7 @@ export const UpdateName: React.FC<{
const [crop, setCrop] = useState({ x: 0, y: 0 });
const [zoom, setZoom] = useState(1);
const [croppedAreaPixels, setCroppedAreaPixels] = useState<Area | null>(null);
const maxUploadFileSizeMB = useAppStore((s) => s.maxUploadFileSizeMB);

const { t } = useTranslation();

Expand Down Expand Up @@ -151,14 +152,14 @@ export const UpdateName: React.FC<{
let croppedFile = new File([croppedBlob], 'avatar.jpg', { type: 'image/jpeg' });

try {
croppedFile = await prepareImageForUpload(croppedFile);
croppedFile = await prepareImageForUpload(croppedFile, maxUploadFileSizeMB);
} catch (error) {
console.error('Compression failed:', error);
toast.error(t('errors.image_compression_failed'));
}

if (!validateUploadSize(croppedFile)) {
toast.error(t('errors.less_than', { size: env.NEXT_PUBLIC_UPLOAD_MAX_FILE_SIZE_MB }));
if (!validateUploadSize(croppedFile, maxUploadFileSizeMB)) {
toast.error(t('errors.less_than', { size: maxUploadFileSizeMB }));
return;
}

Expand All @@ -183,7 +184,7 @@ export const UpdateName: React.FC<{
setZoom(1);
setCroppedAreaPixels(null);
})();
}, [croppedAreaPixels, detailForm, imageSrc, onNameSubmit, t]);
}, [croppedAreaPixels, detailForm, imageSrc, maxUploadFileSizeMB, onNameSubmit, t]);

const handleFileChange = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
Expand Down
19 changes: 3 additions & 16 deletions src/components/AddExpense/CurrencyPicker.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,20 @@
import { memo, useCallback, useMemo } from 'react';

import {
CURRENCIES,
type CurrencyCode,
FRANKFURTER_CURRENCIES,
parseCurrencyCode,
} from '~/lib/currency';
import { CURRENCIES, type CurrencyCode, parseCurrencyCode } from '~/lib/currency';

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)),
);

function CurrencyPickerInner({
className,
currentCurrency = 'USD',
onCurrencyPick,
showOnlyFrankfurter = false,
}: {
className?: string;
currentCurrency: CurrencyCode;
onCurrencyPick: (currency: CurrencyCode) => void;
showOnlyFrankfurter?: boolean;
}) {
const { t, getCurrencyName } = useTranslationWithUtils(['currencies']);
const { recentCurrencies, addToRecentCurrencies } = useCurrencyPreferenceStore();
Expand Down Expand Up @@ -70,15 +59,13 @@ function CurrencyPickerInner({
[recentCurrencies],
);
const items = useMemo(() => {
const baseItems = showOnlyFrankfurter
? Object.values(FRANKFURTER_FILTERED_CURRENCIES)
: Object.values(CURRENCIES);
const baseItems = Object.values(CURRENCIES);
const uniqueItems = [
...recentCurrencyObjects,
...baseItems.filter((c) => !recentCurrencies.includes(c.code as CurrencyCode)),
];
return uniqueItems;
}, [showOnlyFrankfurter, recentCurrencyObjects, recentCurrencies]);
}, [recentCurrencyObjects, recentCurrencies]);

return (
<GeneralPicker
Expand Down
11 changes: 6 additions & 5 deletions src/components/AddExpense/UploadFile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@ import React, { useState } from 'react';
import { toast } from 'sonner';
import { useTranslation } from 'next-i18next';

import { env } from '~/env';
import { useAddExpenseStore } from '~/store/addStore';
import { prepareImageForUpload, uploadImage, validateUploadSize } from '~/utils/imageUpload';

import { Input } from '../ui/input';
import { Label } from '../ui/label';
import { useAppStore } from '~/store/appStore';

export const UploadFile: React.FC = () => {
const { t } = useTranslation();
const [file, setFile] = useState<File | null>(null);
const maxUploadFileSizeMB = useAppStore((s) => s.maxUploadFileSizeMB);
const fileKey = useAddExpenseStore((s) => s.fileKey);
const { setFileUploading, setFileKey } = useAddExpenseStore((s) => s.actions);

Expand All @@ -28,14 +29,14 @@ export const UploadFile: React.FC = () => {

try {
try {
file = await prepareImageForUpload(file);
file = await prepareImageForUpload(file, maxUploadFileSizeMB);
} catch (error) {
console.error('Compression failed:', error);
toast.error(t('errors.image_compression_failed'));
}

if (!validateUploadSize(file)) {
toast.error(t('errors.less_than', { size: env.NEXT_PUBLIC_UPLOAD_MAX_FILE_SIZE_MB }));
if (!validateUploadSize(file, maxUploadFileSizeMB)) {
toast.error(t('errors.less_than', { size: maxUploadFileSizeMB }));
return;
}

Expand All @@ -54,7 +55,7 @@ export const UploadFile: React.FC = () => {
setFileUploading(false);
}
},
[setFileUploading, setFileKey, t],
[setFileUploading, setFileKey, maxUploadFileSizeMB, t],
);

return (
Expand Down
3 changes: 0 additions & 3 deletions src/components/Friend/CurrencyConversion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { api } from '~/utils/api';
import { MAX_RATE_PRECISION, currencyConversion, getRatePrecision } from '~/utils/numbers';

import { toast } from 'sonner';
import { env } from '~/env';
import { type CurrencyCode, isCurrencyCode } from '~/lib/currency';
import { useAddExpenseStore } from '~/store/addStore';
import { CurrencyPicker } from '../AddExpense/CurrencyPicker';
Expand Down Expand Up @@ -216,8 +215,6 @@ export const CurrencyConversion: React.FC<{
className="mx-auto"
currentCurrency={targetCurrency}
onCurrencyPick={onChangeTargetCurrency}
// Client env vars with pages router only work after next build :/
showOnlyFrankfurter={env.NEXT_PUBLIC_FRANKFURTER_USED}
/>
)}
</div>
Expand Down
14 changes: 5 additions & 9 deletions src/components/group/AddMembers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { api } from '~/utils/api';

import { EntityAvatar } from '../ui/avatar';
import { Input } from '../ui/input';
import { env } from '~/env';

const AddMembers: React.FC<{
enableSendingInvites: boolean;
Expand All @@ -34,13 +33,10 @@ const AddMembers: React.FC<{
return null;
}

const groupUserMap = group.groupUsers.reduce(
(acc, gu) => {
acc[gu.userId] = true;
return acc;
},
{} as Record<string, boolean>,
);
const groupUserMap = group.groupUsers.reduce<Record<string, boolean>>((acc, gu) => {
acc[gu.userId] = true;
return acc;
}, {});

const filteredUsers = friendsQuery.data?.filter(
(friend) =>
Expand Down Expand Up @@ -126,7 +122,7 @@ const AddMembers: React.FC<{
onChange={(e) => setInputValue(e.target.value)}
/>
<div>
{enableSendingInvites && !env.NEXT_PUBLIC_IS_CLOUD_DEPLOYMENT ? (
{enableSendingInvites ? (
<div className="mt-1 text-orange-600">
{t('group_details.no_members.add_members_details.warning')}
</div>
Expand Down
16 changes: 4 additions & 12 deletions src/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,10 @@ export const env = createEnv({
/**
* Specify your client-side environment variables schema here. This way you can ensure the app
* isn't built with invalid env vars. To expose them to the client, prefix them with
* `NEXT_PUBLIC_`.
* `NEXT_PUBLIC_`. Note that these are only evaluated at build time!
*/
client: {
NEXT_PUBLIC_FRANKFURTER_USED: z.boolean().default(false),
NEXT_PUBLIC_IS_CLOUD_DEPLOYMENT: z.boolean().default(false),
NEXT_PUBLIC_UPLOAD_MAX_FILE_SIZE_MB: z.coerce.number().int().positive().default(10),
NEXT_PUBLIC_VERSION: z.string().optional(),
NEXT_PUBLIC_APP_VERSION: z.string().optional(),
NEXT_PUBLIC_GIT_SHA: z.string().optional(),
},

Expand Down Expand Up @@ -143,16 +140,11 @@ export const env = createEnv({
OIDC_CLIENT_SECRET: process.env.OIDC_CLIENT_SECRET,
OIDC_WELL_KNOWN_URL: process.env.OIDC_WELL_KNOWN_URL,
OIDC_ALLOW_DANGEROUS_EMAIL_LINKING: Boolean(process.env.OIDC_ALLOW_DANGEROUS_EMAIL_LINKING),
NEXT_PUBLIC_FRANKFURTER_USED: process.env.CURRENCY_RATE_PROVIDER === 'frankfurter',
NEXT_PUBLIC_IS_CLOUD_DEPLOYMENT: process.env.NEXTAUTH_URL?.includes('splitpro.app') ?? false,
NEXT_PUBLIC_UPLOAD_MAX_FILE_SIZE_MB: process.env.UPLOAD_MAX_FILE_SIZE_MB
? Number(process.env.UPLOAD_MAX_FILE_SIZE_MB)
: 10,
UPLOAD_MAX_FILE_SIZE_MB: process.env.UPLOAD_MAX_FILE_SIZE_MB
? Number(process.env.UPLOAD_MAX_FILE_SIZE_MB)
: 10,
NEXT_PUBLIC_VERSION: process.env.APP_VERSION,
NEXT_PUBLIC_GIT_SHA: process.env.GIT_SHA,
NEXT_PUBLIC_APP_VERSION: process.env.NEXT_PUBLIC_APP_VERSION,
NEXT_PUBLIC_GIT_SHA: process.env.NEXT_PUBLIC_GIT_SHA,
},
/**
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially
Expand Down
35 changes: 0 additions & 35 deletions src/lib/currency.ts
Original file line number Diff line number Diff line change
Expand Up @@ -840,38 +840,3 @@ export const isCurrencyCode = (value: string): value is CurrencyCode => value in

export const parseCurrencyCode = (code: string): CurrencyCode =>
isCurrencyCode(code) ? code : 'USD';

// Check with https://api.frankfurter.dev/v1/currencies
export const FRANKFURTER_CURRENCIES = [
'AUD',
'BGN',
'BRL',
'CAD',
'CHF',
'CNY',
'CZK',
'DKK',
'EUR',
'GBP',
'HKD',
'HUF',
'IDR',
'ILS',
'INR',
'ISK',
'JPY',
'KRW',
'MXN',
'MYR',
'NOK',
'NZD',
'PHP',
'PLN',
'RON',
'SEK',
'SGD',
'THB',
'TRY',
'USD',
'ZAR',
];
7 changes: 5 additions & 2 deletions src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@ import '~/styles/globals.css';
const poppins = Poppins({ weight: ['200', '300', '400', '500', '600', '700'], subsets: ['latin'] });
const toastOptions = { duration: 1500 };

const MyApp: AppType<{ session: Session | null; baseUrl: string }> = ({
const MyApp: AppType<{ session: Session | null; baseUrl: string; maxUploadFileSizeMB: number }> = ({
Component,
pageProps: { session, baseUrl, ...pageProps },
pageProps: { session, baseUrl, maxUploadFileSizeMB, ...pageProps },
}) => {
const { t, ready } = useTranslation();
const { setMaxUploadFileSizeMB } = useAppStore((s) => s.actions);
setMaxUploadFileSizeMB(maxUploadFileSizeMB);

if (!ready) {
return (
Expand Down Expand Up @@ -181,6 +183,7 @@ const Auth: React.FC<{ Page: NextPageWithUser; pageProps: any }> = ({ Page, page
export const getServerSideProps = () => ({
props: {
baseUrl: env.NEXTAUTH_URL,
maxUploadFileSizeMB: env.UPLOAD_MAX_FILE_SIZE_MB,
},
});

Expand Down
Loading
Loading