Skip to content

Commit 00f183b

Browse files
committed
upload file done
1 parent 6b7c798 commit 00f183b

File tree

5 files changed

+147
-101
lines changed

5 files changed

+147
-101
lines changed

src/App.tsx

Lines changed: 45 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,48 +2,11 @@ import React from "react";
22
import ChatBot, { Flow } from "react-chatbotify";
33
import RcbPlugin from "./factory/RcbPluginFactory";
44
import { InputValidatorBlock } from "./types/InputValidatorBlock";
5+
import { validateFile } from "./utils/validateFile";
56

67
const App = () => {
7-
// Initialize the plugin
88
const plugins = [RcbPlugin()];
99

10-
const handleUpload = (params: { files?: FileList }) => {
11-
const files = params.files;
12-
13-
if (!files || files.length === 0) {
14-
return { success: false, promptContent: "No file selected." };
15-
}
16-
17-
const file = files[0];
18-
const maxSize = 5 * 1024 * 1024; // 5MB
19-
20-
// Debugging log for file details
21-
console.log("Uploaded file details:", {
22-
name: file.name,
23-
type: file.type,
24-
size: file.size,
25-
});
26-
27-
// Adjusted MIME type checking
28-
if (!file.type.match(/^image\/(jpeg|jpg|png)$/)) {
29-
return {
30-
success: false,
31-
promptContent: "Only JPEG and PNG files are allowed.",
32-
};
33-
}
34-
35-
if (file.size > maxSize) {
36-
return {
37-
success: false,
38-
promptContent: "File size must be less than 5MB.",
39-
};
40-
}
41-
42-
// If all checks pass
43-
console.log("File validation passed:", file.name);
44-
return { success: true };
45-
};
46-
4710
const flow: Flow = {
4811
start: {
4912
message: "Hey there! Please enter your age.",
@@ -61,21 +24,61 @@ const App = () => {
6124
};
6225
},
6326
} as InputValidatorBlock,
27+
6428
age_validation: {
6529
message: "Great! Now please upload a profile picture (JPEG or PNG).",
66-
file: (params) => handleUpload(params),
6730
path: "file_upload_validation",
68-
// Removed validateInput
69-
},
31+
validateInput: (userInput?: string) => {
32+
console.log("validateInput called with userInput:", userInput);
33+
34+
// Allow empty input or file names with allowed extensions
35+
if (
36+
!userInput ||
37+
/\.(jpg|jpeg|png)$/i.test(userInput.trim())
38+
) {
39+
return { success: true };
40+
}
41+
42+
// Disallow other text inputs
43+
return {
44+
success: false,
45+
promptContent: "Please upload a file.",
46+
promptDuration: 3000,
47+
promptType: "error",
48+
};
49+
},
50+
validateFile: (file?: File) => {
51+
return validateFile(file); // Validate file input
52+
},
53+
file: async ({ files }) => {
54+
console.log("Files received:", files);
55+
56+
if (files && files[0]) {
57+
const validationResult = validateFile(files[0]);
58+
if (!validationResult.success) {
59+
console.error(validationResult.promptContent);
60+
return;
61+
}
62+
console.log("File uploaded successfully:", files[0]);
63+
} else {
64+
console.error("No file provided.");
65+
}
66+
},
67+
} as InputValidatorBlock,
68+
7069
file_upload_validation: {
71-
message: "Thank you! Your profile picture has been uploaded successfully.",
70+
message:
71+
"Thank you! Your profile picture has been uploaded successfully.",
7272
path: "end",
7373
},
74+
7475
end: {
7576
message: "This is the end of the flow. Thank you!",
7677
},
7778
};
7879

80+
81+
7982
return <ChatBot id="chatbot-id" plugins={plugins} flow={flow} />;
8083
};
8184

src/core/useRcbPlugin.ts

Lines changed: 30 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -85,45 +85,42 @@ const useRcbPlugin = (pluginConfig?: PluginConfig) => {
8585
*
8686
* @param event Event emitted when user uploads a file.
8787
*/
88+
// useRcbPlugin.ts
89+
8890
const handleUserUploadFile = (event: Event): void => {
8991
const rcbEvent = event as RcbUserUploadFileEvent;
90-
const file: File = rcbEvent.data.files[0];
91-
92-
// Get validator and if no validator, return
93-
const validator = getValidator(rcbEvent, getBotId(), getFlow());
94-
if (!validator) {
95-
return;
92+
const file: File | undefined = rcbEvent.data?.files?.[0];
93+
94+
if (!file) {
95+
console.error("No file uploaded.");
96+
event.preventDefault();
97+
return;
9698
}
97-
98-
// Perform validation
99-
const validationResult = validator(file) as ValidationResult;
100-
if (!validationResult?.success) {
101-
event.preventDefault();
99+
100+
const validator = getValidator<File>(
101+
rcbEvent,
102+
getBotId(),
103+
getFlow(),
104+
"validateFileInput"
105+
);
106+
107+
if (!validator) {
108+
console.error("Validator not found for file input.");
109+
return;
102110
}
103-
104-
// Show prompt if necessary
111+
112+
const validationResult = validator(file);
113+
114+
if (!validationResult.success) {
115+
console.error("Validation failed:", validationResult);
105116
if (validationResult.promptContent) {
106-
// Preserve original styles if this is the first plugin toast
107-
if (numPluginToasts === 0) {
108-
originalStyles.current = structuredClone(styles);
109-
}
110-
const promptStyles = getPromptStyles(
111-
validationResult,
112-
mergedPluginConfig
113-
);
114-
115-
// Update styles with prompt styles
116-
updateStyles(promptStyles);
117-
118-
// Show prompt toast to user
119-
showToast(
120-
validationResult.promptContent,
121-
validationResult.promptDuration ?? 3000
122-
);
123-
124-
// Increase number of plugin toasts by 1
125-
setNumPluginToasts((prev) => prev + 1);
117+
showToast(validationResult.promptContent, validationResult.promptDuration ?? 3000);
118+
}
119+
event.preventDefault();
120+
return;
126121
}
122+
123+
console.log("Validation successful:", validationResult);
127124
};
128125

129126
/**

src/types/InputValidatorBlock.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@
33
import { Block } from "react-chatbotify";
44
import { ValidationResult } from "./ValidationResult";
55

6-
/**
7-
* Extends the Block from React ChatBotify to support validateInput attribute.
8-
*/
9-
export type InputValidatorBlock = Block & {
10-
validateInput: (userInput?: string | File) => ValidationResult;
11-
};
6+
export type InputValidatorBlock = Omit<Block, "file"> & {
7+
file?: (params: { files?: FileList }) => void | Promise<void>; // Updated
8+
validateInput?: (userInput?: string) => ValidationResult;
9+
validateFileInput?: (file?: File) => ValidationResult;
10+
};

src/utils/getValidator.ts

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import { Flow, RcbUserSubmitTextEvent, RcbUserUploadFileEvent } from "react-chatbotify";
44
import { InputValidatorBlock } from "../types/InputValidatorBlock";
5+
import { ValidationResult } from "../types/ValidationResult";
56

67
/**
78
* Union type for user events that can be validated.
@@ -16,35 +17,31 @@ type RcbUserEvent = RcbUserSubmitTextEvent | RcbUserUploadFileEvent;
1617
* @param currFlow The current flow object.
1718
* @returns The validator function if it exists, otherwise undefined.
1819
*/
19-
export const getValidator = (
20+
export const getValidator = <T = string | File>(
2021
event: RcbUserEvent,
2122
currBotId: string | null,
22-
currFlow: Flow
23-
) => {
23+
currFlow: Flow,
24+
validatorType: "validateInput" | "validateFileInput" = "validateInput"
25+
): ((input: T) => ValidationResult) | undefined => {
2426
if (!event.detail) {
25-
return;
27+
return;
2628
}
27-
29+
2830
const { botId, currPath } = event.detail;
29-
31+
3032
if (currBotId !== botId) {
31-
return;
33+
return;
3234
}
33-
35+
3436
if (!currPath) {
35-
return;
37+
return;
3638
}
37-
39+
3840
const currBlock = currFlow[currPath] as InputValidatorBlock;
3941
if (!currBlock) {
40-
return;
42+
return;
4143
}
42-
43-
const validator = currBlock.validateInput;
44-
const isValidatorFunction = validator && typeof validator === "function";
45-
if (!isValidatorFunction) {
46-
return;
47-
}
48-
49-
return validator;
50-
};
44+
45+
const validator = currBlock[validatorType] as ((input: T) => ValidationResult) | undefined;
46+
return typeof validator === "function" ? validator : undefined;
47+
};

src/utils/validateFile.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { ValidationResult } from "../types/ValidationResult";
2+
3+
/**
4+
* Validates the uploaded file.
5+
* Ensures the file is of allowed type and size, and rejects non-file inputs.
6+
*/
7+
export const validateFile = (file?: File): ValidationResult => {
8+
const allowedTypes = ["image/jpeg", "image/png"];
9+
const maxSizeInBytes = 5 * 1024 * 1024; // 5MB
10+
11+
// Check if no file is provided
12+
if (!file) {
13+
return {
14+
success: false,
15+
promptContent: "No file uploaded.",
16+
promptDuration: 3000,
17+
promptType: "error",
18+
};
19+
}
20+
21+
// Check if the input is not a File object (e.g., text input or invalid type)
22+
if (!(file instanceof File)) {
23+
return {
24+
success: false,
25+
promptContent: "Invalid input. Please upload a valid file.",
26+
promptDuration: 3000,
27+
promptType: "error",
28+
};
29+
}
30+
31+
// Validate file type
32+
if (!allowedTypes.includes(file.type)) {
33+
return {
34+
success: false,
35+
promptContent: "Only JPEG or PNG files are allowed.",
36+
promptType: "error",
37+
};
38+
}
39+
40+
// Validate file size
41+
if (file.size > maxSizeInBytes) {
42+
return {
43+
success: false,
44+
promptContent: "File size must be less than 5MB.",
45+
promptType: "error",
46+
};
47+
}
48+
49+
return { success: true };
50+
};

0 commit comments

Comments
 (0)