diff --git a/apps/vite/src/app.tsx b/apps/vite/src/app.tsx
index 44b332bb..9ce7e2c5 100644
--- a/apps/vite/src/app.tsx
+++ b/apps/vite/src/app.tsx
@@ -84,12 +84,17 @@ function App() {
onMembersPress={() => setShowMembers(show => !show)}
/>
-
-
-
-
-
-
+
diff --git a/apps/vite/styles/app.css b/apps/vite/styles/app.css
index 3bcc1fd1..944fc433 100644
--- a/apps/vite/styles/app.css
+++ b/apps/vite/styles/app.css
@@ -41,16 +41,20 @@ body,
#content {
overflow: auto;
- padding: 72px 8px 132px 8px;
+ padding: 40px 0px 0px 0px;
height: 100vh;
margin-top: -56px;
}
+#messages {
+ padding: 32px 8px;
+}
+
#composer {
- position: absolute;
+ position: sticky;
bottom: 0;
left: 0;
- right: 0;
+ z-index: 100;
}
#members {
diff --git a/packages/components/hooks/index.ts b/packages/components/hooks/index.ts
index c5497c4e..0b801443 100644
--- a/packages/components/hooks/index.ts
+++ b/packages/components/hooks/index.ts
@@ -1,2 +1,3 @@
export { useBlur } from './use-blur'
+export { useImageUpload } from './use-image-uploader'
export { useThrottle } from './use-throttle'
diff --git a/packages/components/hooks/use-image-uploader.ts b/packages/components/hooks/use-image-uploader.ts
new file mode 100644
index 00000000..14070901
--- /dev/null
+++ b/packages/components/hooks/use-image-uploader.ts
@@ -0,0 +1,73 @@
+import { useRef, useState } from 'react'
+
+interface UseImageUploadReturn {
+ imagesData: string[]
+ handleImageUpload: (event: React.ChangeEvent) => void
+ handleImageRemove: (index: number) => void
+ imageUploaderInputRef: React.RefObject
+ isDisabled: boolean
+}
+const ALLOWED_EXTENSIONS = /(\.jpg|\.jpeg|\.png)$/i
+const IMAGES_LIMIT = 6
+
+const useImageUpload = (): UseImageUploadReturn => {
+ const [imagesData, setImagesData] = useState([])
+ const imageUploaderInputRef = useRef(null)
+
+ const handleImageUpload = async (
+ event: React.ChangeEvent
+ ) => {
+ const files = event.target.files
+
+ if (!files) {
+ return
+ }
+
+ const filteredFiles = [...files].filter(file =>
+ ALLOWED_EXTENSIONS.test(file.name)
+ )
+
+ // Show alert if some files have unsupported formats
+ if (files.length > filteredFiles.length) {
+ return alert(
+ `Some files have unsupported formats. Only .jpg, .jpeg and .png formats are supported.`
+ )
+ }
+
+ if (files.length > IMAGES_LIMIT || imagesData.length > IMAGES_LIMIT) {
+ return alert(
+ `You can upload only ${IMAGES_LIMIT} images. Please remove some files and try again.`
+ )
+ }
+
+ const newImagesData: string[] = await Promise.all(
+ filteredFiles.map(async file => {
+ const reader = new FileReader()
+ reader.readAsDataURL(file)
+ return new Promise(resolve => {
+ reader.onloadend = () => {
+ resolve(reader.result as string)
+ }
+ })
+ })
+ )
+
+ setImagesData(prevState => [...prevState, ...newImagesData])
+ }
+
+ const handleImageRemove = (index: number) => {
+ // Reset input value to trigger onChange event
+ imageUploaderInputRef.current!.value = ''
+ setImagesData(prevState => prevState.filter((_, i) => i !== index))
+ }
+
+ return {
+ imagesData,
+ handleImageUpload,
+ handleImageRemove,
+ imageUploaderInputRef,
+ isDisabled: imagesData.length >= IMAGES_LIMIT,
+ }
+}
+
+export { useImageUpload }
diff --git a/packages/components/src/composer/composer.tsx b/packages/components/src/composer/composer.tsx
index 5cad4911..0b9b7f73 100644
--- a/packages/components/src/composer/composer.tsx
+++ b/packages/components/src/composer/composer.tsx
@@ -1,15 +1,20 @@
import { useState } from 'react'
+import { useImageUpload } from '@status-im/components/hooks'
import {
+ ArrowUpIcon,
AudioIcon,
+ ClearIcon,
FormatIcon,
ImageIcon,
ReactionIcon,
} from '@status-im/icons/20'
import { BlurView } from 'expo-blur'
-import { Stack, XStack, YStack } from 'tamagui'
+import { AnimatePresence, Stack, XStack, YStack } from 'tamagui'
+import { Button } from '../button'
import { IconButton } from '../icon-button'
+import { Image } from '../image'
import { Input } from '../input'
import { Reply } from '../reply'
@@ -22,26 +27,36 @@ const Composer = (props: Props) => {
const { isBlurred, reply } = props
const [isFocused, setIsFocused] = useState(false)
+ const [text, setText] = useState('')
- const iconButtonBlurred = isBlurred && !isFocused
+ const {
+ imagesData,
+ handleImageUpload,
+ handleImageRemove,
+ imageUploaderInputRef,
+ isDisabled: isImageUploadDisabled,
+ } = useImageUpload()
+
+ const iconButtonBlurred = isBlurred && !isFocused && imagesData.length === 0
return (
{
blurred={isBlurred}
onBlur={() => setIsFocused(false)}
onFocus={() => setIsFocused(true)}
+ onChangeText={setText}
/>
-
+
+
+ {imagesData.length > 0 && (
+
+ {imagesData.map((imageData, index) => (
+
+
+ handleImageRemove(index)}
+ cursor="pointer"
+ justifyContent="center"
+ alignItems="center"
+ >
+
+
+
+
+
+
+ ))}
+
+ )}
+
{
backgroundColor="transparent"
>
- }
- blurred={iconButtonBlurred}
- />
+
}
@@ -94,11 +182,25 @@ const Composer = (props: Props) => {
blurred={iconButtonBlurred}
/>
- }
- blurred={iconButtonBlurred}
- />
+ {text || imagesData.length > 0 ? (
+ // TODO fix styles for circular button. Also the color is different from the design and we have layout shift because of the size.
+ }
+ height={32}
+ size={32}
+ width={32}
+ borderRadius={32}
+ justifyContent="center"
+ alignItems="center"
+ type="positive"
+ />
+ ) : (
+ }
+ blurred={iconButtonBlurred}
+ />
+ )}