Browse Source

feat/preview-files (#13)

Reviewed-on: #13
Co-authored-by: YaroslavBerkuta <yaroslavberkuta@gmail.com>
Co-committed-by: YaroslavBerkuta <yaroslavberkuta@gmail.com>
pull/16/head
YaroslavBerkuta 9 months ago committed by Vitalik Yatsenko
parent
commit
3f734beaf8
  1. 36
      android/app/src/main/AndroidManifest.xml
  2. 7
      android/app/src/main/file_viewer_provider_paths.xml
  3. 2
      android/app/src/main/java/com/taskme2/MainApplication.java
  4. 2
      android/build.gradle
  5. 5
      android/settings.gradle
  6. 6
      ios/Podfile.lock
  7. 9
      package-lock.json
  8. 1
      package.json
  9. 1
      src/modules/root/index.tsx
  10. 2
      src/modules/root/smart-components/fancybox.smart-component.tsx
  11. 6
      src/modules/root/smart-components/index.ts
  12. 31
      src/services/system/fs.service.ts
  13. 8
      src/shared/components/elements/avatar.component.tsx
  14. 2
      src/shared/components/elements/index.ts
  15. 10
      src/shared/components/plugins/chat/chat-item-file.component.tsx
  16. 8
      src/shared/components/plugins/chat/chat-item-image.component.tsx
  17. 27
      src/shared/enums/file-type.enum.ts
  18. 4
      src/shared/helpers/fs.helpers.ts

36
android/app/src/main/AndroidManifest.xml

@ -57,5 +57,39 @@ @@ -57,5 +57,39 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:name="com.vinzscam.reactnativefileviewer.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_viewer_provider_paths"
/>
</provider>
</application>
</manifest>
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:mimeType="application/pdf" />
</intent>
</queries>
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:mimeType="text/plain" />
</intent>
</queries>
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:mimeType="application/msword" />
</intent>
</queries>
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:mimeType="application/vnd.openxmlformats-officedocument.wordprocessingml.document" />
</intent>
</queries>
</manifest>

7
android/app/src/main/file_viewer_provider_paths.xml

@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="files" path="/" />
<external-files-path name="external_files" path="" />
<external-path name="external" path="." />
<cache-path name="cache" path="/" />
</paths>

2
android/app/src/main/java/com/taskme2/MainApplication.java

@ -24,7 +24,7 @@ public class MainApplication extends Application implements ReactApplication { @@ -24,7 +24,7 @@ public class MainApplication extends Application implements ReactApplication {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage());
// packages.add();
return packages;
}

2
android/build.gradle

@ -9,6 +9,8 @@ buildscript { @@ -9,6 +9,8 @@ buildscript {
// We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP.
ndkVersion = "23.1.7779620"
pdfViewerVersion = "3.2.0-beta.1"
pdfViewerRepo = "com.github.mhiew"
}
repositories {
google()

5
android/settings.gradle

@ -4,4 +4,7 @@ include ':app' @@ -4,4 +4,7 @@ include ':app'
includeBuild('../node_modules/@react-native/gradle-plugin')
include ':react-native-config'
project(':react-native-config').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-config/android')
project(':react-native-config').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-config/android')
include ':react-native-file-viewer'
project(':react-native-file-viewer').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-file-viewer/android')

6
ios/Podfile.lock

@ -571,6 +571,8 @@ PODS: @@ -571,6 +571,8 @@ PODS:
- React-Core
- SDWebImage (~> 5.11.1)
- SDWebImageWebPCoder (~> 0.8.4)
- RNFileViewer (2.1.5):
- React-Core
- RNFS (2.20.0):
- React-Core
- RNGestureHandler (2.14.0):
@ -710,6 +712,7 @@ DEPENDENCIES: @@ -710,6 +712,7 @@ DEPENDENCIES:
- "RNCPicker (from `../node_modules/@react-native-picker/picker`)"
- RNDeviceInfo (from `../node_modules/react-native-device-info`)
- RNFastImage (from `../node_modules/react-native-fast-image`)
- RNFileViewer (from `../node_modules/react-native-file-viewer`)
- RNFS (from `../node_modules/react-native-fs`)
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
- RNImageCropPicker (from `../node_modules/react-native-image-crop-picker`)
@ -874,6 +877,8 @@ EXTERNAL SOURCES: @@ -874,6 +877,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-device-info"
RNFastImage:
:path: "../node_modules/react-native-fast-image"
RNFileViewer:
:path: "../node_modules/react-native-file-viewer"
RNFS:
:path: "../node_modules/react-native-fs"
RNGestureHandler:
@ -979,6 +984,7 @@ SPEC CHECKSUMS: @@ -979,6 +984,7 @@ SPEC CHECKSUMS:
RNCPicker: b18aaf30df596e9b1738e7c1f9ee55402a229dca
RNDeviceInfo: db5c64a060e66e5db3102d041ebe3ef307a85120
RNFastImage: 5c9c9fed9c076e521b3f509fe79e790418a544e8
RNFileViewer: ce7ca3ac370e18554d35d6355cffd7c30437c592
RNFS: 4ac0f0ea233904cb798630b3c077808c06931688
RNGestureHandler: 32a01c29ecc9bb0b5bf7bc0a33547f61b4dc2741
RNImageCropPicker: d9616a0cb9b72e8551ff94a7a5021fbd29050aa5

9
package-lock.json generated

@ -52,6 +52,7 @@ @@ -52,6 +52,7 @@
"react-native-exception-handler": "^2.10.10",
"react-native-expire-storage": "^0.0.3",
"react-native-fast-image": "^8.6.3",
"react-native-file-viewer": "^2.1.5",
"react-native-fs": "^2.20.0",
"react-native-gesture-handler": "^2.11.0",
"react-native-gifted-chat": "^2.4.0",
@ -15587,6 +15588,14 @@ @@ -15587,6 +15588,14 @@
"react-native": ">=0.60.0"
}
},
"node_modules/react-native-file-viewer": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/react-native-file-viewer/-/react-native-file-viewer-2.1.5.tgz",
"integrity": "sha512-MGC6sx9jsqHdefhVQ6o0akdsPGpkXgiIbpygb2Sg4g4bh7v6K1cardLV1NwGB9A6u1yICOSDT/MOC//9Ez6EUg==",
"peerDependencies": {
"react-native": ">=0.47"
}
},
"node_modules/react-native-flipper": {
"version": "0.164.0",
"resolved": "https://registry.npmjs.org/react-native-flipper/-/react-native-flipper-0.164.0.tgz",

1
package.json

@ -62,6 +62,7 @@ @@ -62,6 +62,7 @@
"react-native-exception-handler": "^2.10.10",
"react-native-expire-storage": "^0.0.3",
"react-native-fast-image": "^8.6.3",
"react-native-file-viewer": "^2.1.5",
"react-native-fs": "^2.20.0",
"react-native-gesture-handler": "^2.11.0",
"react-native-gifted-chat": "^2.4.0",

1
src/modules/root/index.tsx

@ -112,7 +112,6 @@ export const Navigation: FC = () => { @@ -112,7 +112,6 @@ export const Navigation: FC = () => {
<ActionSheet />
<FancyboxSmart />
<FullscreenVideoSmart />
{GlobalComponents}
</>
)

2
src/modules/root/smart-components/fancybox.smart-component.tsx

@ -13,7 +13,7 @@ export interface FancyboxProps { @@ -13,7 +13,7 @@ export interface FancyboxProps {
}
export const FancyboxSmart = () => {
const { styles, theme } = useTheme(createStyles)
const { styles } = useTheme(createStyles)
const [url, setUrl] = useState(null)
useEventsListener(

6
src/modules/root/smart-components/index.ts

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
export * from './confirm-modal.smart-component';
export * from './info-modal.smart-component';
export * from './confirm-modal.smart-component'
export * from './info-modal.smart-component'
export * from './date-picker.smart-component'
export * from './done-task.smart-component'
export * from './modal-picker-with-pagination.smart-component'
export * from './fancybox.smart-component'
export * from './fullscreen-video.smart-component'
export * from './fullscreen-video.smart-component'

31
src/services/system/fs.service.ts

@ -1,11 +1,12 @@ @@ -1,11 +1,12 @@
import { Alert, Platform } from 'react-native'
import { appEvents, IFile } from '@/shared'
import { appEvents, FileType, IFile } from '@/shared'
import {
createUniqueFileName,
getFileExtension,
getFileName,
getFileUri,
getNameFromFileUrl,
getUrlExtension,
isAndroid,
isImage,
} from '@/shared/helpers'
@ -15,6 +16,8 @@ import mime from 'mime' @@ -15,6 +16,8 @@ import mime from 'mime'
import { mediaPermissionsService } from './media-permissions.service'
import { saveToCameraRoll } from '@/services/system/camera-roll.service'
import Share from 'react-native-share'
import FileViewer from 'react-native-file-viewer'
import * as _ from 'lodash'
interface IWriteFileData {
content: string
fileName: string
@ -313,6 +316,31 @@ export const shareFileOutside = async ( @@ -313,6 +316,31 @@ export const shareFileOutside = async (
}
}
const validFileType = [FileType.DOC, FileType.DOCX, FileType.PDF]
const previewFile = (url: string) => {
try {
const extension = getUrlExtension(url)
if (!_.includes(validFileType, extension)) throw new Error()
const localFile = `${RNFS.DocumentDirectoryPath}/temporaryfile.${extension}`
const options = {
fromUrl: url,
toFile: localFile,
}
RNFS.downloadFile(options).promise.then(() =>
FileViewer.open(localFile),
)
} catch (error) {
appEvents.emit('openInfoModal', {
title: 'Сталась помилка!',
message: 'Цей тип файлу не підтримується!',
onPressOk: () => {},
})
}
}
export const fsService = {
getFileStat,
writeFile,
@ -325,4 +353,5 @@ export const fsService = { @@ -325,4 +353,5 @@ export const fsService = {
shareFile,
shareFileOutside,
createPath,
previewFile,
}

8
src/shared/components/elements/avatar.component.tsx

@ -1,13 +1,7 @@ @@ -1,13 +1,7 @@
import React, { FC } from 'react'
import { useTheme } from '@/shared/hooks/use-theme.hook'
import { PartialTheme } from '@/shared/themes/interfaces'
import {
StyleSheet,
TextStyle,
View,
ViewStyle,
Image,
} from 'react-native'
import { StyleSheet, TextStyle, View, ViewStyle, Image } from 'react-native'
import { Txt } from './txt.component'
interface AvatarProps {

2
src/shared/components/elements/index.ts

@ -10,4 +10,4 @@ export * from './not-found.component' @@ -10,4 +10,4 @@ export * from './not-found.component'
export * from './loading.component'
export * from './check-indicator.component'
export * from './remote-image.component'
export * from './selected-row-square-item.component'
export * from './selected-row-square-item.component'

10
src/shared/components/plugins/chat/chat-item-file.component.tsx

@ -1,4 +1,3 @@ @@ -1,4 +1,3 @@
import { fsService } from '@/services/system'
import { BYTES_IN_KILOBYTE } from '@/shared/consts'
import {
$size,
@ -14,6 +13,7 @@ import { StyleSheet, TouchableOpacity, View } from 'react-native' @@ -14,6 +13,7 @@ import { StyleSheet, TouchableOpacity, View } from 'react-native'
import { IconComponent, Txt } from '../../elements'
import { ChatItem } from './chat-item.component'
import { IChatMessage } from './interfaces'
import { fsService } from '@/services/system'
interface ChatItemFileProps extends IChatMessage {
onLongPress?: () => void
@ -35,18 +35,16 @@ export const ChatItemFile: FC<ChatItemFileProps> = props => { @@ -35,18 +35,16 @@ export const ChatItemFile: FC<ChatItemFileProps> = props => {
return `${convertToMegabytes(props.content.size)} MБ - `
}, [props.content.size])
const downloadFile = useCallback(() => {
const previewFile = useCallback(() => {
const content = props.content
fsService.shareFile(content?.fileUrl, content?.name)
fsService.previewFile(content?.fileUrl)
}, [props.content])
const iconName = getIconNameByExtension(props?.content?.fileUrl)
return (
<ChatItem {...props}>
<TouchableOpacity
style={styles.content}
onPress={() => downloadFile()}>
<TouchableOpacity style={styles.content} onPress={previewFile}>
<View style={styles.iconContainer}>
<IconComponent
color={theme?.chats?.message?.$fileIcon}

8
src/shared/components/plugins/chat/chat-item-image.component.tsx

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
import { appEvents } from '@/shared/events'
import { useTheme } from '@/shared/hooks'
import { PartialTheme } from '@/shared/themes/interfaces'
import React, { FC, useState } from 'react'
import React, { FC } from 'react'
import { StyleSheet, Dimensions, TouchableOpacity } from 'react-native'
import { RemoteImage } from '../../elements'
import { ChatItem } from './chat-item.component'
@ -17,12 +17,6 @@ interface ChatItemImageProps extends IChatMessage { @@ -17,12 +17,6 @@ interface ChatItemImageProps extends IChatMessage {
const width = (Dimensions.get('screen').width * 60) / 100
const calcHeight = (height: number, originWidth: number) => {
if (!height || !originWidth) return width
const persent = (height / originWidth) * 100
return (persent / 100) * width
}
export const ChatItemImage: FC<ChatItemImageProps> = props => {
const { styles } = useTheme(createStyles)
// const [height, setHeight] = useState(width)

27
src/shared/enums/file-type.enum.ts

@ -1,14 +1,15 @@ @@ -1,14 +1,15 @@
export enum FileType {
DOC = "doc",
GIF = "gif",
JPEG = "jpeg",
JPG = "jpg",
MP3 = "mp3",
MP4 = "mp4",
PDF = "pdf",
PNG = "png",
SVG = "svg",
TXT = "txt",
XLS = "xls",
ZIP = "zip"
}
DOC = 'doc',
GIF = 'gif',
JPEG = 'jpeg',
JPG = 'jpg',
MP3 = 'mp3',
MP4 = 'mp4',
PDF = 'pdf',
PNG = 'png',
SVG = 'svg',
TXT = 'txt',
XLS = 'xls',
ZIP = 'zip',
DOCX = 'docx',
}

4
src/shared/helpers/fs.helpers.ts

@ -269,3 +269,7 @@ export const getIconNameByExtension = (name: string) => { @@ -269,3 +269,7 @@ export const getIconNameByExtension = (name: string) => {
if (iconByType) return iconByType
return 'others'
}
export const getUrlExtension = (link: string) => {
return link.split(/[#?]/)[0].split('.').pop().trim()
}

Loading…
Cancel
Save