Browse Source

Merge branch 'files-config' into 'master'

Files config

See merge request jetup/rws/rws-appication!359
merge-requests/360/merge
Coder 2 years ago
parent
commit
41994f2824
  1. 2
      ios/Podfile
  2. 10
      src/api/configs/requests.ts
  3. 14
      src/api/configs/responses.interfaces.ts
  4. 3
      src/api/index.ts
  5. 23
      src/modules/chats/hooks/use-send-files.hook.ts
  6. 2
      src/modules/chats/screens/group-chat-detail.screen.tsx
  7. 274
      src/modules/tasks/config/task-attachments-menu.config.ts
  8. 5
      src/modules/tasks/smart-components/task-attachments-collapse.smart-component.tsx
  9. 6
      src/services/domain/auth.service.ts
  10. 20
      src/services/domain/configs.service.ts
  11. 3
      src/services/domain/index.ts
  12. 20
      src/shared/components/plugins/image-crop-picker.component.tsx
  13. 16
      src/shared/helpers/configs.helpers.ts
  14. 171
      src/shared/helpers/fs.helpers.ts
  15. 1
      src/shared/helpers/index.ts
  16. 10
      src/shared/interfaces/configs.interfaces.ts
  17. 3
      src/shared/interfaces/index.ts
  18. 25
      src/store/shared/reducer.ts
  19. 2
      src/store/shared/selectors.ts
  20. 12
      src/store/shared/types.ts

2
ios/Podfile

@ -4,7 +4,7 @@ require_relative '../node_modules/@react-native-community/cli-platform-ios/nativ @@ -4,7 +4,7 @@ require_relative '../node_modules/@react-native-community/cli-platform-ios/nativ
platform :ios, '12.1'
target 'taskme' do
pod 'ffmpeg-kit-react-native', :subspecs => ['full'], :podspec => '../node_modules/ffmpeg-kit-react-native/ffmpeg-kit-react-native.podspec'
pod 'ffmpeg-kit-react-native', :subspecs => ['audio'], :podspec => '../node_modules/ffmpeg-kit-react-native/ffmpeg-kit-react-native.podspec'
config = use_native_modules!
pod 'react-native-sqlite-storage', :path => '../node_modules/react-native-sqlite-storage'

10
src/api/configs/requests.ts

@ -0,0 +1,10 @@ @@ -0,0 +1,10 @@
import http from "../http.service";
import * as Res from "./responses.interfaces";
const fetchFilesLimitsConfig = () => {
return http.get<Res.IGetFilesConfigRes>("configs/files-config", {}, 'common/');
};
export const configsApi = {
fetchFilesLimitsConfig
};

14
src/api/configs/responses.interfaces.ts

@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
interface IFilesConfigResp {
avatarWidth?: string;
avatarHeight?: string;
avatarSize?: string;
avatarTypes?: string;
taskFilesSize?: string;
taskFilesTypes?: string;
chatFilesSize?: string;
chatVideosSize?: string;
}
export interface IGetFilesConfigRes {
config: IFilesConfigResp;
}

3
src/api/index.ts

@ -9,4 +9,5 @@ export * from './chats/requests' @@ -9,4 +9,5 @@ export * from './chats/requests'
export * from './notifications/requests'
export * from './chats-members/requests'
export * from './chat-messages/requests'
export * from './tasks-documents/requests'
export * from './tasks-documents/requests'
export * from './configs/requests'

23
src/modules/chats/hooks/use-send-files.hook.ts

@ -6,14 +6,14 @@ import { appEvents, IChatMessage } from '@/shared' @@ -6,14 +6,14 @@ import { appEvents, IChatMessage } from '@/shared'
import {
alertFileSizeExceeded,
checkFileSize,
MAX_FILE_SIZE,
MAX_VIDEO_SIZE,
} from '@/shared/helpers'
import { chatMessagesService } from '@/services/domain'
import {
ISendFileMessage,
ISendFileMessages,
} from '@/api/chat-messages/requests.interfaces'
import { getFilesConfig } from '@/store/shared'
import { useSelector } from 'react-redux'
interface IProps {
replyToMessage: IChatMessage
@ -22,16 +22,17 @@ interface IProps { @@ -22,16 +22,17 @@ interface IProps {
export const useSendFiles = ({ replyToMessage, onSend }: IProps) => {
const [isSending, setSending] = useState<boolean>(false)
const { chatFilesSize, chatVideosSize } = useSelector(getFilesConfig)
const handlePressGallery = async (chatId: number) => {
try {
setSending(true)
const items = await mediaService.openMultiplePicker()
if (_.isEmpty(items)) return
const { allowed, exceeded } = checkFileSize(items)
const { allowed, exceeded } = checkFileSize(items, chatFilesSize, chatVideosSize)
if (!_.isEmpty(exceeded)) alertFileSizeExceeded(exceeded)
if (!_.isEmpty(exceeded)) alertFileSizeExceeded(exceeded, chatFilesSize, chatVideosSize)
if (_.isEmpty(allowed)) return
onSend()
@ -66,9 +67,9 @@ export const useSendFiles = ({ replyToMessage, onSend }: IProps) => { @@ -66,9 +67,9 @@ export const useSendFiles = ({ replyToMessage, onSend }: IProps) => {
uri: it.uri,
}))
const { allowed, exceeded } = checkFileSize(prepared)
const { allowed, exceeded } = checkFileSize(prepared, chatFilesSize, chatVideosSize)
if (!_.isEmpty(exceeded)) alertFileSizeExceeded(exceeded)
if (!_.isEmpty(exceeded)) alertFileSizeExceeded(exceeded, chatFilesSize, chatVideosSize)
if (_.isEmpty(allowed)) return
onSend()
@ -104,8 +105,8 @@ export const useSendFiles = ({ replyToMessage, onSend }: IProps) => { @@ -104,8 +105,8 @@ export const useSendFiles = ({ replyToMessage, onSend }: IProps) => {
if (!photo) return
if (photo.size > MAX_FILE_SIZE) {
alertFileSizeExceeded([photo])
if (photo.size > chatFilesSize * 1000) {
alertFileSizeExceeded([photo], chatFilesSize, chatVideosSize)
return
}
@ -127,8 +128,8 @@ export const useSendFiles = ({ replyToMessage, onSend }: IProps) => { @@ -127,8 +128,8 @@ export const useSendFiles = ({ replyToMessage, onSend }: IProps) => {
const takeVideo = async (chatId: number) => {
const video = await mediaService.launchDeviceCamera()
if (video.size > MAX_VIDEO_SIZE) {
alertFileSizeExceeded([video])
if (video.size > chatVideosSize * 1000) {
alertFileSizeExceeded([video], chatFilesSize, chatVideosSize)
return
}

2
src/modules/chats/screens/group-chat-detail.screen.tsx

@ -53,7 +53,7 @@ export const GroupChatDetailScreen: FC<IProps> = ({ navigation, route }) => { @@ -53,7 +53,7 @@ export const GroupChatDetailScreen: FC<IProps> = ({ navigation, route }) => {
)
const listChatMembersLayout = () => {
if (!heightList) {
if (chatInfo?.type === ChatType.Group && !heightList) {
return (
<View
style={{

274
src/modules/tasks/config/task-attachments-menu.config.ts

@ -1,133 +1,163 @@ @@ -1,133 +1,163 @@
import { TaskAttachmentsActionMode } from './../enum/task-attachments.enums'
import {
alertFileSizeExceeded,
checkFileSize,
MAX_FILE_SIZE,
alertNotAllowedFiles,
checkTaskFiles,
} from '@/shared/helpers'
import { mediaService } from '@/services/system'
import _ from 'lodash'
import { appEvents, IFile } from '@/shared'
import { SetStateAction } from 'react'
import { taskDocumentsService } from '@/services/domain'
import { Alert } from 'react-native'
export const getAttachmentsMenusOptions = (params: {
taskId: number | string
setFiles: (docs: SetStateAction<Array<IFile>>) => void
mode: TaskAttachmentsActionMode
taskId: number | string
setFiles: (docs: SetStateAction<Array<IFile>>) => void
mode: TaskAttachmentsActionMode
sizeLimit: number
types: string
}) =>
Object.values({
camera: {
name: 'Фото з камери',
onPress: () => {
setTimeout(async () => {
const photo = await mediaService.openCamera()
if (photo.size > MAX_FILE_SIZE) {
alertFileSizeExceeded([photo])
return
}
if (params.mode === TaskAttachmentsActionMode.DETAIL) {
await taskDocumentsService.add({
taskId: params.taskId,
files: [photo],
})
appEvents.emit('reloadTaskDocs', {
taskId: params.taskId,
})
appEvents.emit('onFirstDocument', { taskId: params.taskId })
return
}
params.setFiles(prevState => [
...prevState,
{ ...photo, id: Math.random() },
])
}, 200)
},
},
gallery: {
name: 'Фото з галереї',
onPress: () => {
setTimeout(async () => {
const images = await mediaService.openMultiplePicker()
const { allowed, exceeded } = checkFileSize(images)
if (!_.isEmpty(exceeded)) alertFileSizeExceeded(exceeded)
if (_.isEmpty(allowed)) return
if (params.mode === TaskAttachmentsActionMode.DETAIL) {
await taskDocumentsService.add({
taskId: params.taskId,
files: allowed,
})
appEvents.emit('reloadTaskDocs', {
taskId: params.taskId,
})
appEvents.emit('onFirstDocument', { taskId: params.taskId })
return
}
params.setFiles(prevState => [
...prevState,
...images.map(it => ({ ...it, id: Math.random() })),
])
}, 200)
},
},
file: {
name: 'Файл',
onPress: () => {
setTimeout(async () => {
try {
const files = await mediaService.openFilesPicker()
if (_.isEmpty(files)) return
const preparedFiles = files.map(it => ({
name: it.name,
size: it.size,
type: it.type,
uri: it.uri,
}))
const { allowed, exceeded } =
checkFileSize(preparedFiles)
if (!_.isEmpty(exceeded))
alertFileSizeExceeded(exceeded)
if (_.isEmpty(allowed)) return
if (params.mode === TaskAttachmentsActionMode.DETAIL) {
await taskDocumentsService.add({
taskId: params.taskId,
files: allowed,
})
appEvents.emit('reloadTaskDocs', {
taskId: params.taskId,
})
appEvents.emit('onFirstDocument', { taskId: params.taskId })
return
}
params.setFiles(prevState => [
...prevState,
...allowed.map(it => ({
...it,
id: Math.random(),
})),
])
} catch (err) {
console.log({ 'mediaService.openFilesPicker': err })
}
}, 200)
},
},
})
Object.values({
camera: {
name: 'Фото з камери',
onPress: () => {
setTimeout(async () => {
const photo = await mediaService.openCamera()
if (photo.size > params.sizeLimit * 1000) {
Alert.alert(
`Не вдалось додати фото`,
`Розмір файла перевищує допустимий розмір ${
params.sizeLimit / 1000
}Мб`,
)
return
}
if (params.mode === TaskAttachmentsActionMode.DETAIL) {
await taskDocumentsService.add({
taskId: params.taskId,
files: [photo],
})
appEvents.emit('reloadTaskDocs', {
taskId: params.taskId,
})
appEvents.emit('onFirstDocument', {
taskId: params.taskId,
})
return
}
params.setFiles(prevState => [
...prevState,
{ ...photo, id: Math.random() },
])
}, 200)
},
},
gallery: {
name: 'Фото з галереї',
onPress: () => {
setTimeout(async () => {
const images = await mediaService.openMultiplePicker()
const { allowed, notAllowed } = checkTaskFiles(
images,
params.sizeLimit,
params.types,
)
if (!_.isEmpty(notAllowed))
alertNotAllowedFiles(
notAllowed,
params.sizeLimit,
params.types,
)
if (_.isEmpty(allowed)) return
if (params.mode === TaskAttachmentsActionMode.DETAIL) {
await taskDocumentsService.add({
taskId: params.taskId,
files: allowed,
})
appEvents.emit('reloadTaskDocs', {
taskId: params.taskId,
})
appEvents.emit('onFirstDocument', {
taskId: params.taskId,
})
return
}
params.setFiles(prevState => [
...prevState,
...images.map(it => ({ ...it, id: Math.random() })),
])
}, 200)
},
},
file: {
name: 'Файл',
onPress: () => {
setTimeout(async () => {
try {
const files = await mediaService.openFilesPicker()
if (_.isEmpty(files)) return
const preparedFiles = files.map(it => ({
name: it.name,
size: it.size,
type: it.type,
uri: it.uri,
}))
const { allowed, notAllowed } = checkTaskFiles(
preparedFiles,
params.sizeLimit,
params.types,
)
if (!_.isEmpty(notAllowed))
alertNotAllowedFiles(
notAllowed,
params.sizeLimit,
params.types,
)
if (_.isEmpty(allowed)) return
if (params.mode === TaskAttachmentsActionMode.DETAIL) {
await taskDocumentsService.add({
taskId: params.taskId,
files: allowed,
})
appEvents.emit('reloadTaskDocs', {
taskId: params.taskId,
})
appEvents.emit('onFirstDocument', {
taskId: params.taskId,
})
return
}
params.setFiles(prevState => [
...prevState,
...allowed.map(it => ({
...it,
id: Math.random(),
})),
])
} catch (err) {
console.log({ 'mediaService.openFilesPicker': err })
}
}, 200)
},
},
})

5
src/modules/tasks/smart-components/task-attachments-collapse.smart-component.tsx

@ -6,6 +6,8 @@ import { TextStyle } from 'react-native' @@ -6,6 +6,8 @@ import { TextStyle } from 'react-native'
import { TaskAttachmentsActionMode } from '../enum'
import { ShortTaskDocument } from '../interfaces'
import { transformDocToShortDoc, transformFileToShortDoc } from '../transforms'
import { getFilesConfig } from '@/store/shared'
import { useSelector } from 'react-redux'
interface TaskAttachmentsCollapseSmartProps {
taskId: number | string
@ -28,6 +30,7 @@ export const TaskAttachmentsCollapseSmart: FC< @@ -28,6 +30,7 @@ export const TaskAttachmentsCollapseSmart: FC<
files: (number | string)[]
}>(null)
const [files, setFiles] = useState<IFile[]>([])
const { taskFilesSize, taskFilesTypes } = useSelector(getFilesConfig)
useEventsListener('onTaskCreated', () =>
setTimeout(() => {
@ -42,6 +45,8 @@ export const TaskAttachmentsCollapseSmart: FC< @@ -42,6 +45,8 @@ export const TaskAttachmentsCollapseSmart: FC<
taskId,
setFiles,
mode,
sizeLimit: taskFilesSize,
types: taskFilesTypes,
})
appEvents.emit('openActionSheet', { items: menuOptions })
}

6
src/services/domain/auth.service.ts

@ -34,6 +34,7 @@ import { IGetAccountCacheInfoResponse } from '@/api/account/responses.interfaces @@ -34,6 +34,7 @@ import { IGetAccountCacheInfoResponse } from '@/api/account/responses.interfaces
import { appEvents } from '@/shared'
import { accountManagerInstance as accManager } from '@/managers'
import { getErrorCode } from '@/shared/helpers'
import { configsService } from './configs.service'
let phoneNumber: string
@ -77,7 +78,7 @@ export const autoAuth = async () => { @@ -77,7 +78,7 @@ export const autoAuth = async () => {
loadNotImportantData(cacheInfo)
//run synchronization
// ***** run synchronization *****
synchService.runSynchronization()
NavigationService.setModule(NavigationModuleKey.User)
@ -108,7 +109,8 @@ const loadNotImportantData = async ( @@ -108,7 +109,8 @@ const loadNotImportantData = async (
await contactsService.fetchBirthdayCountToday().catch(() => {})
await tasksService.getUnreadTasksCount().catch(() => {})
await chatsService.getUnreadMessagesCount().catch(() => {})
await notificationService.initAndSaveDevice().catch(() => {})
await notificationService.initAndSaveDevice().catch(() => { })
await configsService.loadFilesLimitsConfig();
}
const onReconnect = async () => {

20
src/services/domain/configs.service.ts

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
import { configsApi } from "@/api";
import { transformFilesLimitsConfig } from "@/shared/helpers";
import { SetFilesConfig } from "@/store/shared";
import { simpleDispatch } from "@/store/store-helpers";
const loadFilesLimitsConfig = async () => {
try {
const { data } = await configsApi.fetchFilesLimitsConfig();
if (data) {
const config = transformFilesLimitsConfig(data.config);
simpleDispatch(new SetFilesConfig({ config }));
}
} catch (e) {
console.log("Error on load files limits config", e);
}
};
export const configsService = {
loadFilesLimitsConfig
};

3
src/services/domain/index.ts

@ -11,4 +11,5 @@ export * from './chats.service' @@ -11,4 +11,5 @@ export * from './chats.service'
export * from './chat-members.service'
export * from './chat-messages.service'
export * from './actions-queue.service'
export * from './remove.service'
export * from './remove.service'
export * from './configs.service'

20
src/shared/components/plugins/image-crop-picker.component.tsx

@ -1,8 +1,10 @@ @@ -1,8 +1,10 @@
import React, { FC, useCallback, useState } from 'react'
import React, { FC, useState } from 'react'
import { mediaService } from '@/services/system/media.service'
import { useActionSheet } from '@expo/react-native-action-sheet'
import { TouchableOpacity, ViewStyle } from 'react-native'
import { appEvents } from '@/shared/events'
import { useSelector } from 'react-redux'
import { getFilesConfig } from '@/store/shared'
import { checkAvatarFile } from '@/shared/helpers'
interface IImageCropPickerProps {
children: (preview: string, onPressRemove: () => void) => React.ReactNode
@ -13,6 +15,8 @@ interface IImageCropPickerProps { @@ -13,6 +15,8 @@ interface IImageCropPickerProps {
export const ImageCropPicker: FC<IImageCropPickerProps> = props => {
const [preview, setPreview] = useState(null)
const { avatarWidth, avatarHeight, avatarSize, avatarTypes } =
useSelector(getFilesConfig)
const onPress = () => {
appEvents.emit('openActionSheet', {
@ -30,20 +34,26 @@ export const ImageCropPicker: FC<IImageCropPickerProps> = props => { @@ -30,20 +34,26 @@ export const ImageCropPicker: FC<IImageCropPickerProps> = props => {
}
const loadFromCamera = async () => {
const image = await mediaService.openCamera()
const image = await mediaService.openCamera({
width: avatarWidth,
height: avatarHeight,
})
onSuccessUpload(image)
}
const loadPicker = async () => {
const image: any = await mediaService.openCropPicker({
width: 300,
height: 400,
width: avatarWidth,
height: avatarHeight,
cropperCircleOverlay: true,
})
onSuccessUpload(image)
}
const onSuccessUpload = async image => {
const isAllowed = checkAvatarFile(image, avatarSize, avatarTypes)
if (!isAllowed) return
setPreview(image.uri)
props.onChange(image)
}

16
src/shared/helpers/configs.helpers.ts

@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
import _ from "lodash";
import { IFilesConfig } from "../interfaces";
export const transformFilesLimitsConfig = (
data: Partial<Record<keyof IFilesConfig, string>>
) => {
const config: Partial<IFilesConfig> = {};
if (_.isEmpty(data)) return config;
Object.keys(data).map(it => {
if (!_.isNaN(Number(data[it]))) config[it] = Number(data[it]);
else config[it] = data[it].toLowerCase();
});
return config;
};

171
src/shared/helpers/fs.helpers.ts

@ -3,27 +3,27 @@ import { Alert, Platform } from 'react-native' @@ -3,27 +3,27 @@ import { Alert, Platform } from 'react-native'
import { FileType } from '../enums'
import { IFile } from '../interfaces'
export const MAX_FILE_SIZE = 50000000
export const MAX_VIDEO_SIZE = 100000000
export const MAX_FILE_SIZE = 50000000 // in bytes
export const MAX_VIDEO_SIZE = 100000000 // in bytes
const FILE_TAIL_REG_EXP = /^\(\d+\)$/
const DIGITS_REG_EXP = /\d+/
const M4A_EXT = 'm4a'
const MOV_EXT = 'mov'
const iconByFileType = {
[FileType.DOC]: 'doc',
[FileType.JPG]: 'jpg',
[FileType.GIF]: 'gif',
[FileType.JPEG]: 'jpeg',
[FileType.MP3]: 'mp3',
[FileType.MP4]: 'mp4',
[FileType.PDF]: 'pdf',
[FileType.PNG]: 'png',
[FileType.SVG]: 'svg',
[FileType.TXT]: 'txt',
[FileType.XLS]: 'xls',
[FileType.ZIP]: 'zip'
};
[FileType.DOC]: 'doc',
[FileType.JPG]: 'jpg',
[FileType.GIF]: 'gif',
[FileType.JPEG]: 'jpeg',
[FileType.MP3]: 'mp3',
[FileType.MP4]: 'mp4',
[FileType.PDF]: 'pdf',
[FileType.PNG]: 'png',
[FileType.SVG]: 'svg',
[FileType.TXT]: 'txt',
[FileType.XLS]: 'xls',
[FileType.ZIP]: 'zip',
}
export const getFileName = (file: any) => {
// Alert.alert('FILe', JSON.stringify(file))
@ -97,14 +97,18 @@ export const prepareFormData = (file: IFile, formData: FormData) => { @@ -97,14 +97,18 @@ export const prepareFormData = (file: IFile, formData: FormData) => {
formData.append('filesData', JSON.stringify(filesData))
}
export const checkFileSize = (files: IFile[]) => {
export const checkFileSize = (
files: IFile[],
fileLimit: number,
videoLimit: number,
) => {
const allowed = []
const exceeded = []
files.map(file => {
if (
(file.type.includes('video') && file.size > MAX_VIDEO_SIZE) ||
(!file.type.includes('video') && file.size > MAX_FILE_SIZE)
(file.type.includes('video') && file.size > videoLimit * 1000) ||
(!file.type.includes('video') && file.size > fileLimit * 1000)
)
exceeded.push(file)
else allowed.push(file)
@ -116,17 +120,100 @@ export const checkFileSize = (files: IFile[]) => { @@ -116,17 +120,100 @@ export const checkFileSize = (files: IFile[]) => {
}
}
export const alertFileSizeExceeded = (files: IFile[]) => {
export const alertFileSizeExceeded = (
files: IFile[],
fileLimit: number,
videoLimit: number,
) => {
const names = files.map(file => file.name)
const filesText = files.length > 1 ? 'файли' : 'файл'
const message =
files.length > 1
? `Розміри файлів ${names.join(', ')} перевищують допустимий`
: `Розмір файла ${names[0]} перевищує допустимий`
? `Розміри файлів ${names.join(
', ',
)} перевищують допустимі.\nДозволяється завантажувати файли розміром не більше ${
videoLimit / 1000
}Мб для відео, ${fileLimit / 1000}Мб для інших типів`
: `Розмір файла ${
names[0]
} перевищує допустимий.\nДозволяється завантажувати файли розміром не більше ${
videoLimit / 1000
}Мб для відео, ${fileLimit / 1000}Мб для інших типів`
Alert.alert(`Не вдалось віправити ${filesText}`, message)
}
export const checkTaskFiles = (
files: IFile[],
limit: number,
types: string,
) => {
const allowed = []
const notAllowed = []
files.map(file => {
const extByUrl = getFileExtension(file.uri)
const extByName = getFileExtension(file.name)
if (
file.size > limit * 1000 ||
(!_.includes(types, extByUrl?.toLowerCase()) &&
!_.includes(types, extByName?.toLowerCase()))
)
notAllowed.push(file)
else allowed.push(file)
})
return {
allowed,
notAllowed,
}
}
export const alertNotAllowedFiles = (
files: IFile[],
limit: number,
types: string,
) => {
const names = files.map(file => file.name)
const filesText = files.length > 1 ? 'файли' : 'файл'
const messageStart =
files.length > 1
? `Файли ${names.join(', ')} мають неприпустимий розмір або тип`
: `Файл ${names[0]} має неприпустимий розмір або тип`
const messageEnd = `Дозволяється завантажувати файли розміром не більше ${
limit / 1000
}Мб\n
Допустимі розширення: ${types.split(',').join(', ')}`
Alert.alert(
`Не вдалось додати ${filesText}`,
`${messageStart}\n
${messageEnd}`,
)
}
export const checkAvatarFile = (file: IFile, limit: number, types: string) => {
const extByUrl = getFileExtension(file.uri)
const extByName = getFileExtension(file.name)
if (
(!_.includes(types, extByUrl?.toLowerCase()) &&
!_.includes(types, extByName?.toLowerCase())) ||
file.size > limit * 1000
) {
Alert.alert(
'Не вдалось завантажити файл',
`Розмір зображення не повинен перевищувати ${limit}Кб \nДозволені типи: ${types
.split(',')
.join(', ')}`,
)
return false
}
return true
}
export const createUniqueFileName = (fileName: string) => {
const arrFromName = fileName.split('.')
const ext = arrFromName[arrFromName.length - 1]
@ -151,34 +238,34 @@ export const createUniqueFileName = (fileName: string) => { @@ -151,34 +238,34 @@ export const createUniqueFileName = (fileName: string) => {
}
export const getNameFromFileUrl = (url: string) => {
if (!url) return "";
const splitUrl = url.split("/");
if (!url) return ''
const splitUrl = url.split('/')
if (splitUrl.length === 1) return url;
const dirtyName = splitUrl[splitUrl.length - 1];
const clearName = dirtyName.split(".");
clearName.pop();
if (splitUrl.length === 1) return url
const dirtyName = splitUrl[splitUrl.length - 1]
const clearName = dirtyName.split('.')
clearName.pop()
if (clearName.length > 0) return clearName.join(".");
if (clearName.length > 0) return clearName.join('.')
return dirtyName;
};
return dirtyName
}
export const getFileType = (name: string) => {
if (!name) return null;
if (!name) return null
const extension = getFileExtension(name);
if (_.isEmpty(extension)) return null;
const extension = getFileExtension(name)
if (_.isEmpty(extension)) return null
if (Object.values(FileType).includes(extension.toLowerCase() as FileType))
return extension.toLowerCase() as FileType;
if (Object.values(FileType).includes(extension.toLowerCase() as FileType))
return extension.toLowerCase() as FileType
return null;
};
return null
}
export const getIconNameByExtension = (name: string) => {
const fileType = getFileType(name);
const iconByType = iconByFileType[fileType];
if (iconByType) return iconByType;
return 'others';
};
const fileType = getFileType(name)
const iconByType = iconByFileType[fileType]
if (iconByType) return iconByType
return 'others'
}

1
src/shared/helpers/index.ts

@ -19,3 +19,4 @@ export * from './touch-event.helpers' @@ -19,3 +19,4 @@ export * from './touch-event.helpers'
export * from './transforms.helpers'
export * from './url.helpers'
export * from './versions.helper'
export * from './configs.helpers'

10
src/shared/interfaces/configs.interfaces.ts

@ -0,0 +1,10 @@ @@ -0,0 +1,10 @@
export interface IFilesConfig {
avatarWidth: number;
avatarHeight: number;
avatarSize: number;
avatarTypes: string;
taskFilesSize: number;
taskFilesTypes: string;
chatFilesSize: number;
chatVideosSize: number;
}

3
src/shared/interfaces/index.ts

@ -11,4 +11,5 @@ export * from './factories.interfaces' @@ -11,4 +11,5 @@ export * from './factories.interfaces'
export * from './options.interfaces'
export * from './notification.interfaces'
export * from './chats.interfaces'
export * from './media.interfaces'
export * from './media.interfaces'
export * from './configs.interfaces'

25
src/store/shared/reducer.ts

@ -1,12 +1,25 @@ @@ -1,12 +1,25 @@
import { TAuthActions } from './types'
import { createReducer } from '@bitalikrty/redux-create-reducer'
import { NavigationModuleKey } from '@/shared/enums'
import { IFilesConfig } from '@/shared'
export interface ISharedState {
activeNavigationModule: NavigationModuleKey
prevModule: NavigationModuleKey
isForbidden: ConstrainBoolean
isNetConnect: boolean
filesConfig: IFilesConfig
}
const filesConfigInitState = {
avatarWidth: 200, // in px
avatarHeight: 200, // in px
avatarSize: 500, // in kb
avatarTypes: 'jpg,svg,png,webp',
taskFilesSize: 100000, // in kb
taskFilesTypes: 'jpeg,jpg,png,svg,webp,tiff,pdf,txt,doc,docx,xls,xlsx',
chatFilesSize: 50000, // in kb
chatVideosSize: 100000, // in kb
}
const initialState: ISharedState = {
@ -14,6 +27,7 @@ const initialState: ISharedState = { @@ -14,6 +27,7 @@ const initialState: ISharedState = {
prevModule: null,
isForbidden: false,
isNetConnect: true,
filesConfig: filesConfigInitState,
}
export const sharedReducer = createReducer<ISharedState, TAuthActions>(
@ -47,6 +61,17 @@ export const sharedReducer = createReducer<ISharedState, TAuthActions>( @@ -47,6 +61,17 @@ export const sharedReducer = createReducer<ISharedState, TAuthActions>(
prevModule: null,
isForbidden: false,
isNetConnect: true,
filesConfig: filesConfigInitState,
}
},
SET_FILES_CONFIG: (state, action) => {
return {
...state,
filesConfig: {
...state.filesConfig,
...action.payload.config,
},
}
},
},

2
src/store/shared/selectors.ts

@ -9,3 +9,5 @@ export const selectPrevActiveModule = (store: RootState) => @@ -9,3 +9,5 @@ export const selectPrevActiveModule = (store: RootState) =>
export const selectIsForbidden = (store: RootState) => store.shared.isForbidden
export const selectNetConnect = (store: RootState) => store.shared.isNetConnect
export const getFilesConfig = (state: RootState) => state.shared.filesConfig;

12
src/store/shared/types.ts

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
import { NavigationModuleKey } from '@/shared'
import { IFilesConfig, NavigationModuleKey } from '@/shared'
import { Action } from 'redux'
export class SetNavigationModule implements Action {
@ -29,8 +29,18 @@ export class Reset { @@ -29,8 +29,18 @@ export class Reset {
readonly type = 'RESET'
}
export class SetFilesConfig {
readonly type = "SET_FILES_CONFIG";
constructor(
public readonly payload: {
config: Partial<IFilesConfig>;
}
) {}
}
export type TAuthActions =
| SetNavigationModule
| SetIsForbidden
| Reset
| SetNetConnect
| SetFilesConfig

Loading…
Cancel
Save