Skip to content
This repository was archived by the owner on Nov 13, 2024. It is now read-only.

Commit c7a4eab

Browse files
piotr-suwalaSaletpubnub-release-bot
authored
Add file + text upload capabilities (#126)
* feat(lib): add file + text upload capabilities * feat(lib): rm a comment * feat(lib): change colors and fix tests * feat(lib): adjust margins for file preview text * PubNub SDK v0.31.0 release. --------- Co-authored-by: Przemysław Janowski <[email protected]> Co-authored-by: PubNub Release Bot <[email protected]>
1 parent 4925d34 commit c7a4eab

File tree

10 files changed

+123
-41
lines changed

10 files changed

+123
-41
lines changed

.pubnub.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
---
22
name: pubnub-react-chat-components
3-
version: v0.30.0
3+
version: v0.31.0
44
scm: github.com/pubnub/react-chat-components
55
schema: 1
66
files:
77
- lib/dist/index.js
88
- lib/dist/index.es.js
99
changelog:
10+
- date: 2023-10-04
11+
version: v0.31.0
12+
changes:
13+
- type: feature
14+
text: "Make it possible to send both text and file in one message."
1015
- date: 2023-07-20
1116
version: v0.30.0
1217
changes:

packages/common/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@pubnub/common-chat-components",
3-
"version": "0.30.0",
3+
"version": "0.31.0",
44
"main": "src/index.ts",
55
"license": "MIT",
66
"scripts": {

packages/common/src/message-input/message-input.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ export interface CommonMessageInputProps {
3636
onSend?: (value: MessagePayload | File | UriFileInput) => void;
3737
/** Option to provide an extra actions renderer to add custom action buttons to the input. */
3838
extraActionsRenderer?: () => JSX.Element;
39+
/** Callback to render custom file preview JSX Element */
40+
filePreviewRenderer?: (file: File | UriFileInput) => JSX.Element | null;
3941
}
4042

4143
/**
@@ -74,7 +76,7 @@ export const useMessageInputCore = (props: CommonMessageInputProps) => {
7476
if (!file && !isValidInputText()) return;
7577
let message = {
7678
id: uuid.v4(),
77-
text: file ? "" : text,
79+
text,
7880
type: file ? "" : "default",
7981
...(senderInfo && { sender: users.find((u) => u.id === pubnub.getUUID()) }),
8082
createdAt: new Date().toISOString(),

packages/react-native/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@pubnub/react-native-chat-components",
3-
"version": "0.30.0",
3+
"version": "0.31.0",
44
"description": "PubNub Chat Components is a development kit of React Native components that aims to help you to easily build Chat applications using PubNub infrastructure. It removes the complexicity of picking an adequate Chat engine, learning its APIs and dealing with its low-level internals. As the same time it allows you to create apps of various use cases, with different functionalities and customizable looks.",
55
"author": "PubNub <[email protected]>",
66
"main": "dist/commonjs/index",

packages/react-native/src/message-input/message-input.style.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const lightColors = {
1010
actionsSheetButtonIconTint: "#1F2937",
1111
actionsSheetLabelColor: "#1F2937",
1212
sheetContentHeaderText: "#000000",
13+
messagePreview: "#585858",
1314
};
1415

1516
const darkColors = {
@@ -21,10 +22,12 @@ const darkColors = {
2122
actionsSheetButtonIconTint: "#e4e5e5",
2223
actionsSheetLabelColor: "#e7eaec",
2324
sheetContentHeaderText: "#ffffff",
25+
messagePreview: "rgba(228, 228, 235, 0.8)",
2426
};
2527

2628
export interface MessageInputStyle {
2729
messageInputWrapper?: ViewStyle;
30+
messageInputContent?: ViewStyle;
2831
messageInput?: ViewStyle;
2932
messageInputPlaceholder?: TextStyle;
3033
messageInputFileLabel?: ViewStyle;
@@ -43,6 +46,8 @@ export interface MessageInputStyle {
4346
fileUploadModalSheetContentButton?: ViewStyle;
4447
fileUploadModalSheetContentButtonIcon?: ImageStyle;
4548
fileUploadModalSheetContentTextStyle?: ViewStyle;
49+
filePreviewContainer?: ViewStyle;
50+
filePreviewText?: TextStyle;
4651
}
4752

4853
export default (theme: Themes): MessageInputStyle => {
@@ -53,6 +58,9 @@ export default (theme: Themes): MessageInputStyle => {
5358
backgroundColor: colors.wrapperBackground,
5459
paddingHorizontal: 8,
5560
paddingVertical: 10,
61+
flexDirection: "column",
62+
},
63+
messageInputContent: {
5664
flexDirection: "row",
5765
alignItems: "center",
5866
},
@@ -143,5 +151,13 @@ export default (theme: Themes): MessageInputStyle => {
143151
fontSize: 16,
144152
lineHeight: 24,
145153
},
154+
filePreviewContainer: {
155+
marginBottom: 8,
156+
marginLeft: 10,
157+
},
158+
filePreviewText: {
159+
color: colors.messagePreview,
160+
fontSize: 14,
161+
},
146162
});
147163
};

packages/react-native/src/message-input/message-input.tsx

Lines changed: 57 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
import React, { FC, useState } from "react";
2-
import { View, Image, TouchableOpacity, TextInput, Animated, TextInputProps } from "react-native";
2+
import {
3+
View,
4+
Image,
5+
TouchableOpacity,
6+
TextInput,
7+
Animated,
8+
TextInputProps,
9+
Text,
10+
} from "react-native";
311
import { CommonMessageInputProps, useMessageInputCore } from "@pubnub/common-chat-components";
412
import { getDocumentAsync } from "expo-document-picker";
513
import { useStyle, useRotation } from "../helpers";
@@ -60,6 +68,7 @@ export const MessageInput: FC<MessageInputProps> = (props: MessageInputProps) =>
6068
sendMessageOnSubmitEditing,
6169
senderInfo,
6270
typingIndicator,
71+
filePreviewRenderer,
6372
...otherTextInputProps
6473
} = props;
6574

@@ -114,7 +123,6 @@ export const MessageInput: FC<MessageInputProps> = (props: MessageInputProps) =>
114123
const fileName =
115124
asset.fileName || asset.uri.substring(asset.uri.lastIndexOf("/") + 1, asset.uri.length);
116125
setFile({ mimeType: "image/*", name: fileName, uri: asset.uri });
117-
setText(fileName);
118126
} catch (e) {
119127
onError(e);
120128
}
@@ -131,15 +139,13 @@ export const MessageInput: FC<MessageInputProps> = (props: MessageInputProps) =>
131139
}
132140
setModalVisible(false);
133141
setFile({ mimeType: result.mimeType, name: result.name, uri: result.uri });
134-
setText(result.name);
135142
} catch (e) {
136143
onError(e);
137144
}
138145
};
139146

140147
const handleRemoveFile = () => {
141148
setFile(null);
142-
setText("");
143149
};
144150

145151
const handleInputChange = (newText: string) => {
@@ -237,36 +243,55 @@ export const MessageInput: FC<MessageInputProps> = (props: MessageInputProps) =>
237243
</>
238244
);
239245

246+
const renderFilePreview = () => {
247+
if (filePreviewRenderer) {
248+
return filePreviewRenderer(file);
249+
}
250+
251+
if (!file) {
252+
return null;
253+
}
254+
255+
return (
256+
<View style={style.filePreviewContainer}>
257+
<Text style={style.filePreviewText}>{file.name}</Text>
258+
</View>
259+
);
260+
};
261+
240262
return (
241263
<View style={style.messageInputWrapper}>
242-
{renderFileModal()}
243-
{!actionsAfterInput && renderActions()}
244-
<TextInput
245-
{...otherTextInputProps}
246-
testID="message-input"
247-
autoComplete="off"
248-
multiline={true}
249-
onChangeText={handleInputChange}
250-
placeholder={placeholder}
251-
style={style.messageInput}
252-
placeholderTextColor={style.messageInputPlaceholder.color}
253-
editable={!disabled && file == null}
254-
value={text}
255-
onKeyPress={handleKeyPress}
256-
/>
257-
{actionsAfterInput && renderActions()}
258-
{!disabled && (
259-
<View style={style.sendButton}>
260-
{loader ? (
261-
<Animated.Image
262-
style={[style.icon, { transform: [{ rotate }] }]}
263-
source={{ uri: SpinnerIcon }}
264-
/>
265-
) : (
266-
renderSendButton()
267-
)}
268-
</View>
269-
)}
264+
{renderFilePreview()}
265+
<View style={style.messageInputContent}>
266+
{renderFileModal()}
267+
{!actionsAfterInput && renderActions()}
268+
<TextInput
269+
{...otherTextInputProps}
270+
testID="message-input"
271+
autoComplete="off"
272+
multiline={true}
273+
onChangeText={handleInputChange}
274+
placeholder={placeholder}
275+
style={style.messageInput}
276+
placeholderTextColor={style.messageInputPlaceholder.color}
277+
editable={!disabled}
278+
value={text}
279+
onKeyPress={handleKeyPress}
280+
/>
281+
{actionsAfterInput && renderActions()}
282+
{!disabled && (
283+
<View style={style.sendButton}>
284+
{loader ? (
285+
<Animated.Image
286+
style={[style.icon, { transform: [{ rotate }] }]}
287+
source={{ uri: SpinnerIcon }}
288+
/>
289+
) : (
290+
renderSendButton()
291+
)}
292+
</View>
293+
)}
294+
</View>
270295
</View>
271296
);
272297
};

packages/react/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@pubnub/react-chat-components",
3-
"version": "0.30.0",
3+
"version": "0.31.0",
44
"description": "PubNub Chat Components is a development kit of React components that aims to help you to easily build Chat applications using PubNub infrastructure. It removes the complexicity of picking an adequate Chat engine, learning its APIs and dealing with its low-level internals. As the same time it allows you to create apps of various use cases, with different functionalities and customizable looks.",
55
"author": "PubNub <[email protected]>",
66
"main": "dist/index.js",

packages/react/src/message-input/message-input.scss

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
--msg-input__send__margin: 0 9px;
2525
--msg-input__send__minWidth: 0;
2626
--msg-input__send__padding: 0;
27+
--msg-input__file-preview-container__margin: 0 0 8px 10px;
2728
--msg-input--disabled__placeholder__color: rgba(var(--chat--light__color--2-rgb), 0.5);
2829
--msg-input__placeholder__color: var(--chat--light__color--2);
2930
--msg-input__textarea--focus__border: 1px solid transparent;
@@ -45,6 +46,8 @@
4546
--msg-input__textarea__resize: none;
4647
--msg-input__textarea__width: 100%;
4748
--msg-input__textarea__overflow: hidden;
49+
--msg__preview__color: var(--chat--light__color--1);
50+
--msg__preview__fontSize: 14px;
4851
}
4952

5053
/* ************************
@@ -109,6 +112,12 @@
109112
}
110113
}
111114

115+
&__file-preview-container {
116+
margin: var(--msg-input__file-preview-container__margin);
117+
color: var(--msg__preview__color);
118+
font-size: var(--msg__preview__fontSize);
119+
}
120+
112121
&__emoji-picker {
113122
bottom: var(--msg-input__emoji-picker__bottom);
114123
left: var(--msg-input__emoji-picker__left);
@@ -168,4 +177,5 @@
168177
--msg-input__textarea__background: var(--chat--dark__background--2);
169178
--msg-input--disabled__textarea__background: rgba(var(--chat--dark__background--2-rgb), 0.5);
170179
--msg-input__textarea__color: var(--chat--dark__color--1);
180+
--msg__preview__color: var(--chat--dark__color--1);
171181
}

packages/react/src/message-input/message-input.tsx

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ export const MessageInput: FC<MessageInputProps> = (props: MessageInputProps) =>
6464
sendButton,
6565
senderInfo,
6666
typingIndicator,
67+
filePreviewRenderer,
6768
...otherTextAreaProps
6869
} = props;
6970

@@ -140,7 +141,6 @@ export const MessageInput: FC<MessageInputProps> = (props: MessageInputProps) =>
140141
try {
141142
const file = event.target.files[0];
142143
setFile(file);
143-
setText(file.name);
144144
} catch (e) {
145145
onError(e);
146146
}
@@ -167,7 +167,7 @@ export const MessageInput: FC<MessageInputProps> = (props: MessageInputProps) =>
167167

168168
const handleRemoveFile = () => {
169169
autoSize();
170-
clearInput();
170+
setFile(null);
171171
if (fileRef.current) fileRef.current.value = "";
172172
};
173173

@@ -239,17 +239,34 @@ export const MessageInput: FC<MessageInputProps> = (props: MessageInputProps) =>
239239
</div>
240240
);
241241

242+
const renderFilePreview = () => {
243+
if (filePreviewRenderer) {
244+
return filePreviewRenderer(file);
245+
}
246+
247+
if (!file) {
248+
return null;
249+
}
250+
251+
return (
252+
<div className="pn-msg-input__file-preview-container" data-testid="file-preview-container">
253+
{file.name}
254+
</div>
255+
);
256+
};
257+
242258
return (
243259
<div
244260
className={`pn-msg-input pn-msg-input--${theme} ${disabled ? "pn-msg-input--disabled" : ""}`}
245261
>
262+
{renderFilePreview()}
246263
<div className="pn-msg-input__wrapper">
247264
{!actionsAfterInput && renderActions()}
248265
<textarea
249266
{...otherTextAreaProps}
250267
className="pn-msg-input__textarea"
251268
data-testid="message-input"
252-
disabled={disabled || !!file}
269+
disabled={disabled}
253270
onChange={handleInputChange}
254271
onKeyPress={handleKeyPress}
255272
placeholder={placeholder}

packages/react/test/message-input.test.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,11 @@ describe("Message Input", () => {
192192

193193
expect(fileInput.files[0]).toBe(file);
194194
expect(fileInput.files).toHaveLength(1);
195-
expect(input).toHaveValue("hello.png");
195+
expect(input).toHaveValue("");
196+
197+
const filePreviewContainer = screen.getByTestId("file-preview-container") as HTMLDivElement;
198+
199+
expect(filePreviewContainer.innerHTML).toBe("hello.png");
196200
});
197201

198202
test("clears the file", async () => {
@@ -205,6 +209,9 @@ describe("Message Input", () => {
205209
await userEvent.click(screen.getByTitle("Remove the file"));
206210

207211
expect(input).toHaveValue("");
212+
const filePreviewContainer = screen.queryByTestId("file-preview-container") as HTMLDivElement;
213+
214+
expect(filePreviewContainer).toBeNull();
208215
});
209216

210217
test("sends the file", async () => {

0 commit comments

Comments
 (0)