Skip to content

Commit 2cb19a0

Browse files
authored
Merge pull request #356 from GTBitsOfGood/staging
Merge to Prod
2 parents 6e7a30e + 838f4f1 commit 2cb19a0

File tree

13 files changed

+174
-38
lines changed

13 files changed

+174
-38
lines changed

email-notification/models.ts

+4
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,10 @@ const postSchema = new Schema<IPost | IDraftPost>({
162162
enum: Object.values(Breed),
163163
},
164164
],
165+
otherBreedDescription: {
166+
type: String,
167+
required: false,
168+
},
165169
gender: {
166170
type: String,
167171
default: null,

email-notification/types.ts

+2
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ export enum Breed {
7777
TerrierSmall = "terrierSmall", // Terrier (Small)
7878
Weimaraner = "weimaraner",
7979
Whippet = "whippet",
80+
Other = "other",
8081
}
8182

8283
export enum Gender {
@@ -143,6 +144,7 @@ export interface IPost {
143144
type: FosterType;
144145
size: Size;
145146
breed: Breed[];
147+
otherBreedDescription?: string;
146148
gender: Gender;
147149
age: Age;
148150
temperament: Temperament[];

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@
1313
"lint-staged": {
1414
"*.{js,jsx,ts,tsx,css,md}": "prettier --write"
1515
}
16-
}
16+
}

web/components/PetPostModal/EditPostModal.tsx

+35-32
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ const formSchema = z.object({
101101
breed: z
102102
.array(z.nativeEnum(Breed))
103103
.transform((val, ctx) => arrayEmptyValidation(val, ctx, "Breed")),
104+
otherBreedDescription: z.string().optional(),
104105
temperament: z.array(z.nativeEnum(Temperament)),
105106
medical: z.array(z.nativeEnum(Medical)),
106107
behavioral: z.array(z.nativeEnum(Behavioral)),
@@ -155,6 +156,7 @@ const EditPostModal: React.FC<{
155156
medical,
156157
gender,
157158
breed,
159+
otherBreedDescription,
158160
getsAlongWithOlderKids,
159161
getsAlongWithYoungKids,
160162
getsAlongWithLargeDogs,
@@ -172,6 +174,7 @@ const EditPostModal: React.FC<{
172174
type,
173175
size,
174176
breed,
177+
otherBreedDescription,
175178
draft,
176179
temperament,
177180
spayNeuterStatus,
@@ -490,42 +493,42 @@ const EditPostModal: React.FC<{
490493
onClick={
491494
isContentView
492495
? () => {
493-
setIsContentView(false);
494-
}
496+
setIsContentView(false);
497+
}
495498
: () => {
496-
//TODO: Wait for success to close.
497-
const validation = formSchema.safeParse(formState);
498-
if (validation.success) {
499-
setIsLoading(true);
500-
editPost(!formState.draft)
501-
.then(() => {
502-
onClose();
503-
setFileArr(fileArr);
504-
setIsContentView(true);
505-
dispatch({
506-
type: "clear",
507-
});
508-
})
509-
.finally(() => {
510-
setIsLoading(false);
499+
//TODO: Wait for success to close.
500+
const validation = formSchema.safeParse(formState);
501+
if (validation.success) {
502+
setIsLoading(true);
503+
editPost(!formState.draft)
504+
.then(() => {
505+
onClose();
506+
setFileArr(fileArr);
507+
setIsContentView(true);
508+
dispatch({
509+
type: "clear",
511510
});
512-
} else {
513-
toast.closeAll();
514-
toast({
515-
title: "Error",
516-
description: validation.error.issues
517-
.map((issue) => issue.message)
518-
.join("\r\n"),
519-
containerStyle: {
520-
whiteSpace: "pre-line",
521-
},
522-
status: "error",
523-
duration: 5000,
524-
isClosable: true,
525-
position: "top",
511+
})
512+
.finally(() => {
513+
setIsLoading(false);
526514
});
527-
}
515+
} else {
516+
toast.closeAll();
517+
toast({
518+
title: "Error",
519+
description: validation.error.issues
520+
.map((issue) => issue.message)
521+
.join("\r\n"),
522+
containerStyle: {
523+
whiteSpace: "pre-line",
524+
},
525+
status: "error",
526+
duration: 5000,
527+
isClosable: true,
528+
position: "top",
529+
});
528530
}
531+
}
529532
}
530533
>
531534
{isContentView ? "Next" : "Post"}

web/components/PostCreationModal/FileUpload/FileDropZone.tsx

+95-2
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,59 @@ function FileDropZone(props: PropsType) {
7171
lg: <FullDropZone />,
7272
});
7373

74+
const MAX_VIDEO_SIZE = 50 * 1024 * 1024; // 50MB
75+
const MAX_IMAGE_SIZE = 3 * 1024 * 1024; // 3MB
76+
77+
async function resizeImage(file: File): Promise<File> {
78+
return new Promise((resolve, reject) => {
79+
const reader = new FileReader();
80+
reader.readAsDataURL(file);
81+
reader.onload = () => {
82+
const img = new Image();
83+
img.src = reader.result as string;
84+
img.onload = () => {
85+
const canvas = document.createElement("canvas");
86+
const ctx = canvas.getContext("2d");
87+
if (!ctx) return reject(new Error("Canvas context not available"));
88+
89+
const MAX_WIDTH = 4096;
90+
const MAX_HEIGHT = 4096;
91+
let width = img.width;
92+
let height = img.height;
93+
94+
if (width > MAX_WIDTH || height > MAX_HEIGHT) {
95+
if (width > height) {
96+
height = (height * MAX_WIDTH) / width;
97+
width = MAX_WIDTH;
98+
} else {
99+
width = (width * MAX_HEIGHT) / height;
100+
height = MAX_HEIGHT;
101+
}
102+
}
103+
104+
canvas.width = width;
105+
canvas.height = height;
106+
ctx.drawImage(img, 0, 0, width, height);
107+
108+
canvas.toBlob(
109+
(blob) => {
110+
if (blob) {
111+
resolve(new File([blob], file.name, { type: "image/jpeg" }));
112+
} else {
113+
reject(new Error("Failed to resize image"));
114+
}
115+
},
116+
"image/jpeg",
117+
0.8
118+
);
119+
};
120+
};
121+
reader.onerror = (error) => reject(error);
122+
});
123+
}
124+
74125
const onDrop = useCallback(
75-
(acceptedFiles: File[], fileRejections: FileRejection[]) => {
126+
async (acceptedFiles: File[], fileRejections: FileRejection[]) => {
76127
if (fileRejections.length > 0) {
77128
toast({
78129
status: "error",
@@ -104,7 +155,49 @@ function FileDropZone(props: PropsType) {
104155
return;
105156
}
106157

107-
setFileArr([...fileArr, ...acceptedFiles]);
158+
let newFiles: File[] = [];
159+
160+
const processedFiles = await Promise.all(
161+
acceptedFiles.map(async (file) => {
162+
console.log(file.size);
163+
if (file.type.startsWith("video/")) {
164+
if (file.size > MAX_VIDEO_SIZE) {
165+
toast({
166+
status: "error",
167+
position: "top",
168+
title: "Error",
169+
description: `The video ${file.name} exceeds the 50MB limit.`,
170+
duration: 4000,
171+
isClosable: true,
172+
});
173+
return null;
174+
}
175+
return file;
176+
} else if (file.type.startsWith("image/")) {
177+
if (file.size > MAX_IMAGE_SIZE) {
178+
try {
179+
return await resizeImage(file);
180+
} catch (error) {
181+
console.error(`Failed to resize image ${file.name}`, error);
182+
toast({
183+
status: "error",
184+
position: "top",
185+
title: "Error",
186+
description: `Failed to resize image ${file.name}.`,
187+
duration: 4000,
188+
isClosable: true,
189+
});
190+
return null;
191+
}
192+
}
193+
return file;
194+
}
195+
return null;
196+
})
197+
);
198+
199+
newFiles = processedFiles.filter((file): file is File => file !== null);
200+
setFileArr([...fileArr, ...newFiles]);
108201
},
109202
[fileArr, setFileArr]
110203
);

web/components/PostCreationModal/Form/FormSlide.tsx

+17-1
Original file line numberDiff line numberDiff line change
@@ -193,15 +193,31 @@ export const FormSlide: React.FC<{
193193
value: k,
194194
label: v,
195195
}))}
196-
onChange={(e) =>
196+
onChange={(e) => {
197197
dispatchFormState({
198198
type: "setField",
199199
key: "breed",
200200
data: e!.map(({ value }) => value) as Breed[],
201201
})
202202
}
203+
}
204+
/>
205+
</FormControl>
206+
{formState.breed.includes(Breed.Other) ?
207+
<FormControl className="otherBreedForm" gridColumn="span 1">
208+
<FormLabel>Other Breed Description</FormLabel>
209+
<Textarea
210+
value={formState.otherBreedDescription}
211+
onChange={(e) =>
212+
dispatchFormState({
213+
type: "setField",
214+
key: "otherBreedDescription",
215+
data: e.target.value,
216+
})
217+
}
203218
/>
204219
</FormControl>
220+
: <></>}
205221
<FormControl className="temperamentForm" gridColumn="span 1">
206222
<FormLabel>Temperament</FormLabel>
207223
<Select

web/components/PostCreationModal/PostCreationModal.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ const formSchema = z.object({
103103
breed: z
104104
.array(z.nativeEnum(Breed))
105105
.transform((val, ctx) => arrayEmptyValidation(val, ctx, "Breed")),
106+
otherBreedDescription: z.string().optional(),
106107
temperament: z.array(z.nativeEnum(Temperament)),
107108
medical: z.array(z.nativeEnum(Medical)),
108109
behavioral: z.array(z.nativeEnum(Behavioral)),
@@ -160,6 +161,7 @@ const PostCreationModal: React.FC<{
160161
type: null,
161162
size: null,
162163
breed: [],
164+
otherBreedDescription: "",
163165
temperament: [],
164166
medical: [],
165167
behavioral: [],

web/db/models/Post.ts

+4
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ const postSchema = new Schema<IPost | IDraftPost>({
3636
enum: Object.values(Breed),
3737
},
3838
],
39+
otherBreedDescription: {
40+
type: String,
41+
required: false
42+
},
3943
gender: {
4044
type: String,
4145
default: null,

web/pages/onboarding.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ function Onboarding() {
171171
{ value: Breed.TerrierSmall, label: "Terrier (Small)" },
172172
{ value: Breed.Weimaraner, label: "Weimaraner" },
173173
{ value: Breed.Whippet, label: "Whippet" },
174+
{ value: Breed.Other, label: "Other" },
174175
],
175176
qtype: QType.Question,
176177
singleAnswer: false,
@@ -234,6 +235,7 @@ function Onboarding() {
234235
{ value: Breed.TerrierSmall, label: "Terrier (Small)" },
235236
{ value: Breed.Weimaraner, label: "Weimaraner" },
236237
{ value: Breed.Whippet, label: "Whippet" },
238+
{ value: Breed.Other, label: "Other" },
237239
],
238240
qtype: QType.Question,
239241
singleAnswer: false,

web/pages/post/[postId].tsx

+7-1
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ function PostPage({
157157
medical,
158158
gender,
159159
breed,
160+
otherBreedDescription,
160161
getsAlongWithOlderKids,
161162
getsAlongWithYoungKids,
162163
getsAlongWithLargeDogs,
@@ -364,7 +365,12 @@ function PostPage({
364365
</Text>
365366
<Text>
366367
<b>Breed: </b>
367-
{breed.map((breed) => breedLabels[breed]).join(", ")}
368+
{breed.map((breed) => {
369+
if (breed === "other" && otherBreedDescription) {
370+
return "Other (" + otherBreedDescription + ")";
371+
}
372+
return breedLabels[breed]
373+
}).join(", ")}
368374
</Text>
369375
<Text>
370376
<b>Size: </b>

web/server/routers/post.ts

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ const postSchema = z.object({
5454
type: z.nativeEnum(FosterType),
5555
size: z.nativeEnum(Size),
5656
breed: z.array(z.nativeEnum(Breed)),
57+
otherBreedDescription: z.string().optional(),
5758
gender: z.nativeEnum(Gender),
5859
age: z.nativeEnum(Age),
5960
draft: z.boolean(),

web/utils/types/post.ts

+3
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ export enum Breed {
115115
TerrierSmall = "terrierSmall", // Terrier (Small)
116116
Weimaraner = "weimaraner",
117117
Whippet = "whippet",
118+
Other = "other",
118119
}
119120

120121
export const breedLabels: Record<Breed, string> = {
@@ -168,6 +169,7 @@ export const breedLabels: Record<Breed, string> = {
168169
[Breed.TerrierSmall]: "Terrier (Small)", // Terrier (Small)
169170
[Breed.Weimaraner]: "Weimaraner",
170171
[Breed.Whippet]: "Whippet",
172+
[Breed.Other]: "Other",
171173
};
172174

173175
export enum Gender {
@@ -325,6 +327,7 @@ export interface IPost {
325327
type: FosterType;
326328
size: Size;
327329
breed: Breed[];
330+
otherBreedDescription?: string;
328331
gender: Gender;
329332
age: Age;
330333
temperament: Temperament[];

yarn.lock

+1-1
Original file line numberDiff line numberDiff line change
@@ -498,4 +498,4 @@ wrap-ansi@^7.0.0:
498498
yaml@^2.1.3:
499499
version "2.2.1"
500500
resolved "https://registry.npmjs.org/yaml/-/yaml-2.2.1.tgz"
501-
integrity sha512-e0WHiYql7+9wr4cWMx3TVQrNwejKaEe7/rHNmQmqRjazfOP5W8PB6Jpebb5o6fIapbz9o9+2ipcaTM2ZwDI6lw==
501+
integrity sha512-e0WHiYql7+9wr4cWMx3TVQrNwejKaEe7/rHNmQmqRjazfOP5W8PB6Jpebb5o6fIapbz9o9+2ipcaTM2ZwDI6lw==

0 commit comments

Comments
 (0)