Coder
3 years ago
27 changed files with 540 additions and 83 deletions
@ -0,0 +1,36 @@
@@ -0,0 +1,36 @@
|
||||
import { $size, useTheme } from '@/shared' |
||||
import { PartialTheme } from '@/shared/themes/interfaces' |
||||
import React from 'react' |
||||
import { View, StyleSheet } from 'react-native' |
||||
import { AttachmentsMenuItem } from '../components' |
||||
import { attachmentsMenuConfig } from '../configs' |
||||
|
||||
interface IProps { |
||||
isMenuOpen: boolean |
||||
onPressItem: (key: string) => void |
||||
} |
||||
|
||||
export const AttachmentsMenu = ({ isMenuOpen, onPressItem }: IProps) => { |
||||
const { styles } = useTheme(createStyles) |
||||
|
||||
if (!isMenuOpen) return null |
||||
|
||||
return ( |
||||
<View style={styles.menu}> |
||||
{attachmentsMenuConfig.map(it => ( |
||||
<AttachmentsMenuItem {...it} onPress={onPressItem} /> |
||||
))} |
||||
</View> |
||||
) |
||||
} |
||||
|
||||
const createStyles = (theme: PartialTheme) => |
||||
StyleSheet.create({ |
||||
menu: { |
||||
flexDirection: 'row', |
||||
justifyContent: 'flex-start', |
||||
marginLeft: $size(20), |
||||
marginTop: $size(10), |
||||
marginBottom: $size(20), |
||||
}, |
||||
}) |
@ -0,0 +1,21 @@
@@ -0,0 +1,21 @@
|
||||
import { $size, IconComponent } from '@/shared' |
||||
import React from 'react' |
||||
import { View } from 'react-native' |
||||
import { Actions } from 'react-native-gifted-chat' |
||||
|
||||
export const renderActions = (props: any) => ( |
||||
<Actions |
||||
{...props} |
||||
icon={() => ( |
||||
<View style={props.buttonContainerStyle}> |
||||
<IconComponent |
||||
style={props?.iconStyle} |
||||
color={props?.iconColor} |
||||
name={props.isMenuOpen ? 'x-1' : 'paperclip-1'} |
||||
size={props.isMenuOpen ? $size(16, 18) : $size(23, 25)} |
||||
/> |
||||
</View> |
||||
)} |
||||
cancelButtonIndex={1} |
||||
/> |
||||
) |
@ -0,0 +1,54 @@
@@ -0,0 +1,54 @@
|
||||
import { $size, IconComponent, useTheme } from '@/shared' |
||||
import { PartialTheme } from '@/shared/themes/interfaces' |
||||
import React from 'react' |
||||
import { StyleSheet, View, Text, TouchableOpacity } from 'react-native' |
||||
|
||||
interface IProps { |
||||
iconName: string |
||||
title: string |
||||
name: string |
||||
onPress: (name: string) => void |
||||
} |
||||
|
||||
export const AttachmentsMenuItem = ({ |
||||
iconName, |
||||
title, |
||||
name, |
||||
onPress, |
||||
}: IProps) => { |
||||
const { styles, theme } = useTheme(createStyles) |
||||
|
||||
return ( |
||||
<TouchableOpacity |
||||
style={styles.componentContainer} |
||||
onPress={() => onPress(name)}> |
||||
<View style={styles.iconContainer}> |
||||
<IconComponent |
||||
color={theme?.chats?.attachmentsMenu?.$icon} |
||||
name={iconName} |
||||
size={$size(28, 26)} |
||||
/> |
||||
</View> |
||||
<Text style={styles.title}>{title}</Text> |
||||
</TouchableOpacity> |
||||
) |
||||
} |
||||
|
||||
const createStyles = (theme: PartialTheme) => |
||||
StyleSheet.create({ |
||||
componentContainer: { |
||||
flexDirection: 'column', |
||||
alignItems: 'center', |
||||
marginRight: $size(30), |
||||
}, |
||||
iconContainer: { |
||||
backgroundColor: theme?.chats?.attachmentsMenu?.$iconBg, |
||||
padding: $size(16, 14), |
||||
borderRadius: 15, |
||||
}, |
||||
title: { |
||||
marginTop: $size(7, 5), |
||||
color: theme?.chats?.attachmentsMenu?.$title, |
||||
fontSize: $size(12), |
||||
}, |
||||
}) |
@ -0,0 +1,17 @@
@@ -0,0 +1,17 @@
|
||||
export const attachmentsMenuConfig = [ |
||||
{ |
||||
iconName: 'camera-1', |
||||
title: 'Камера', |
||||
name: 'camera' |
||||
}, |
||||
{ |
||||
iconName: 'image-1', |
||||
title: 'Галерея', |
||||
name: 'gallery' |
||||
}, |
||||
{ |
||||
iconName: 'filetext-1', |
||||
title: 'Файл', |
||||
name: 'file' |
||||
}, |
||||
] |
@ -1,4 +1,5 @@
@@ -1,4 +1,5 @@
|
||||
export * from './chat-card-buttons.config' |
||||
export * from './data-message' |
||||
export * from './member-card-btns.config' |
||||
export * from './chat-details-footer-btns.config' |
||||
export * from './chat-details-footer-btns.config' |
||||
export * from './attachments-menu.config' |
@ -0,0 +1,97 @@
@@ -0,0 +1,97 @@
|
||||
import { chatMessagesService } from '@/services/domain' |
||||
import { mediaService } from '@/services/system/media.service' |
||||
import _ from 'lodash' |
||||
import { Alert, Platform } from 'react-native' |
||||
import { Image } from 'react-native-image-crop-picker' |
||||
import {DocumentPickerResponse} from 'react-native-document-picker' |
||||
|
||||
const MAX_FILE_SIZE = 52428800 |
||||
|
||||
export const useSendFiles = () => { |
||||
const getName = image => { |
||||
if (Platform.OS === 'ios') return image.filename |
||||
|
||||
try { |
||||
return String(image.path).split('/').reverse()[0] |
||||
} catch (e) { |
||||
return 'avatar.jpg' |
||||
} |
||||
} |
||||
|
||||
const checkFileSizeAllowed = (files: (Image | DocumentPickerResponse)[]) => { |
||||
if (_.every(files, file => file.size <= MAX_FILE_SIZE)) return true |
||||
|
||||
Alert.alert('Не вдалось віправити файл. Розмір файла не повинен перевищувати 50 мб') |
||||
return false |
||||
} |
||||
|
||||
const handlePressGallery = async () => { |
||||
const selected = await mediaService.openMultiplePicker() |
||||
const allowed = checkFileSizeAllowed(selected) |
||||
if (!allowed) return null |
||||
|
||||
const prepared = selected.map(it => ({ |
||||
name: getName(it), |
||||
type: it.mime, |
||||
uri: |
||||
Platform.OS === 'android' |
||||
? it.path |
||||
: it.path.replace('file://', ''), |
||||
})) |
||||
return prepared |
||||
} |
||||
const handlePressFile = async () => { |
||||
const selected = await mediaService.openFilesPicker() |
||||
const allowed = checkFileSizeAllowed(selected) |
||||
if (!allowed) return null |
||||
|
||||
const prepared = selected.map(it => ({ |
||||
name: it.name, |
||||
type: it.type, |
||||
uri: it.uri, |
||||
})) |
||||
return prepared |
||||
} |
||||
|
||||
const handlePressCamera = async () => { |
||||
const selected: any = await mediaService.openCamera() |
||||
const allowed = checkFileSizeAllowed(selected) |
||||
if (!allowed) return null |
||||
|
||||
const prepared = { |
||||
name: getName(selected), |
||||
type: selected.mime, |
||||
uri: |
||||
Platform.OS === 'android' |
||||
? selected.path |
||||
: selected.path.replace('file://', ''), |
||||
} |
||||
return [prepared] |
||||
} |
||||
|
||||
const handlePressMenuItem = async (name: string, chatId: number) => { |
||||
const menuHandler = { |
||||
gallery: handlePressGallery, |
||||
camera: handlePressCamera, |
||||
file: handlePressFile, |
||||
} |
||||
|
||||
const apiRequest = { |
||||
gallery: chatMessagesService.sendImageMessage, |
||||
camera: chatMessagesService.sendImageMessage, |
||||
file: chatMessagesService.sendFileMessage, |
||||
} |
||||
const files = await menuHandler[name]() |
||||
|
||||
if (_.isEmpty(files)) return |
||||
|
||||
await apiRequest[name]({ |
||||
chatId, |
||||
files, |
||||
}) |
||||
} |
||||
|
||||
return { |
||||
handlePressMenuItem, |
||||
} |
||||
} |
@ -1,27 +1,56 @@
@@ -1,27 +1,56 @@
|
||||
import { getChatMessagesListReq, sendTextMessageReq } from "@/api" |
||||
import { IFetchChatMessages, ISendTextMessage } from "@/api/chats/requests.interface" |
||||
import { IFetchChatMessagesList } from "@/api/chats/responses.interfaces" |
||||
import { ApiResponse } from "@/api/http.types" |
||||
import { |
||||
getChatMessagesListReq, |
||||
sendFileMessageReq, |
||||
sendImageMessageReq, |
||||
sendTextMessageReq, |
||||
} from '@/api' |
||||
import { |
||||
IFetchChatMessages, |
||||
ISendFileMessage, |
||||
ISendTextMessage, |
||||
} from '@/api/chats/requests.interface' |
||||
import { IFetchChatMessagesList } from '@/api/chats/responses.interfaces' |
||||
import { ApiResponse } from '@/api/http.types' |
||||
|
||||
const fetchMessages = async (params: IFetchChatMessages): ApiResponse<IFetchChatMessagesList> => { |
||||
try { |
||||
const resp = await getChatMessagesListReq(params) |
||||
const fetchMessages = async ( |
||||
params: IFetchChatMessages, |
||||
): ApiResponse<IFetchChatMessagesList> => { |
||||
try { |
||||
const resp = await getChatMessagesListReq(params) |
||||
|
||||
return resp |
||||
} catch (err) { |
||||
console.log(err) |
||||
} |
||||
return resp |
||||
} catch (err) { |
||||
console.log(err) |
||||
} |
||||
} |
||||
|
||||
const sendTextMessage = async(data: ISendTextMessage) => { |
||||
try { |
||||
await sendTextMessageReq(data) |
||||
} catch (err) { |
||||
console.log('SEND TEXT MESSAGE ERROR', err) |
||||
const sendTextMessage = async (data: ISendTextMessage) => { |
||||
try { |
||||
await sendTextMessageReq(data) |
||||
} catch (err) { |
||||
console.log('SEND TEXT MESSAGE ERROR', err) |
||||
} |
||||
} |
||||
|
||||
const sendFileMessage = async (data: ISendFileMessage) => { |
||||
try { |
||||
await sendFileMessageReq(data) |
||||
} catch (err) { |
||||
console.log('SEND FILE MESSAGE ERROR', err) |
||||
} |
||||
} |
||||
|
||||
const sendImageMessage = async (data: ISendFileMessage) => { |
||||
try { |
||||
await sendImageMessageReq(data) |
||||
} catch (err) { |
||||
console.log('SEND IMAGE MESSAGE ERROR', err) |
||||
} |
||||
} |
||||
|
||||
export const chatMessagesService = { |
||||
fetchMessages, |
||||
sendTextMessage |
||||
} |
||||
fetchMessages, |
||||
sendTextMessage, |
||||
sendFileMessage, |
||||
sendImageMessage |
||||
} |
||||
|
@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
|
||||
export interface IFile { |
||||
uri: string |
||||
type: string |
||||
name: string |
||||
} |
Loading…
Reference in new issue