Browse Source

fix/preview-pdf (#19)

Reviewed-on: #19
Co-authored-by: YaroslavBerkuta <yaroslavberkuta@gmail.com>
Co-committed-by: YaroslavBerkuta <yaroslavberkuta@gmail.com>
pull/21/head
YaroslavBerkuta 9 months ago committed by Vitalik Yatsenko
parent
commit
1cdfc0fbc1
  1. 1
      android/app/build.gradle
  2. 3
      android/app/src/main/java/com/taskme2/MainApplication.java
  3. 5
      android/settings.gradle
  4. 13
      ios/Podfile.lock
  5. 19
      ios/taskme2.xcodeproj/project.pbxproj
  6. 32
      package-lock.json
  7. 2
      package.json
  8. 2
      src/modules/root/index.tsx
  9. 16
      src/modules/tasks/atoms/document-preview.atom.tsx
  10. 4
      src/services/system/fs.service.ts
  11. 2
      src/shared/components/layouts/index.ts
  12. 83
      src/shared/components/modals/preview-file-modal.smart-component.tsx
  13. 9
      src/shared/components/plugins/chat/chat-item-file.component.tsx
  14. 3
      src/shared/components/plugins/index.ts
  15. 17
      src/shared/components/plugins/webview.component.tsx
  16. 12
      src/shared/events/index.ts
  17. 6
      src/shared/helpers/fs.helpers.ts

1
android/app/build.gradle

@ -87,6 +87,7 @@ dependencies { @@ -87,6 +87,7 @@ dependencies {
implementation("com.facebook.react:react-android")
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.0.0")
implementation project(':react-native-view-pdf')
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}")
debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {

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

@ -9,6 +9,7 @@ import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint; @@ -9,6 +9,7 @@ import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
import com.facebook.react.defaults.DefaultReactNativeHost;
import com.facebook.soloader.SoLoader;
import java.util.List;
import com.rumax.reactnative.pdfviewer.PDFViewPackage;
public class MainApplication extends Application implements ReactApplication {
@ -24,7 +25,7 @@ public class MainApplication extends Application implements ReactApplication { @@ -24,7 +25,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();
packages.add(new PDFViewPackage());
return packages;
}

5
android/settings.gradle

@ -7,4 +7,7 @@ include ':react-native-config' @@ -7,4 +7,7 @@ include ':react-native-config'
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')
project(':react-native-file-viewer').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-file-viewer/android')
include ':react-native-view-pdf'
project(':react-native-view-pdf').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-view-pdf/android')

13
ios/Podfile.lock

@ -443,6 +443,9 @@ PODS: @@ -443,6 +443,9 @@ PODS:
- react-native-webrtc (111.0.6):
- JitsiWebRTC (~> 111.0.0)
- React-Core
- react-native-webview (13.8.1):
- RCT-Folly (= 2021.07.22.00)
- React-Core
- React-NativeModulesApple (0.72.10):
- hermes-engine
- React-callinvoker
@ -592,6 +595,8 @@ PODS: @@ -592,6 +595,8 @@ PODS:
- RNNotifee/NotifeeCore (= 7.8.2)
- RNNotifee/NotifeeCore (7.8.2):
- React-Core
- RNPDF (0.14.0):
- React
- RNPermissions (3.10.1):
- React-Core
- RNReanimated (3.6.1):
@ -687,6 +692,7 @@ DEPENDENCIES: @@ -687,6 +692,7 @@ DEPENDENCIES:
- react-native-sqlite-storage (from `../node_modules/react-native-sqlite-storage`)
- react-native-video (from `../node_modules/react-native-video`)
- react-native-webrtc (from `../node_modules/react-native-webrtc`)
- react-native-webview (from `../node_modules/react-native-webview`)
- React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`)
- React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`)
- React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`)
@ -717,6 +723,7 @@ DEPENDENCIES: @@ -717,6 +723,7 @@ DEPENDENCIES:
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
- RNImageCropPicker (from `../node_modules/react-native-image-crop-picker`)
- "RNNotifee (from `../node_modules/@notifee/react-native`)"
- RNPDF (from `../node_modules/react-native-view-pdf`)
- RNPermissions (from `../node_modules/react-native-permissions`)
- RNReanimated (from `../node_modules/react-native-reanimated`)
- RNScreens (from `../node_modules/react-native-screens`)
@ -827,6 +834,8 @@ EXTERNAL SOURCES: @@ -827,6 +834,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-video"
react-native-webrtc:
:path: "../node_modules/react-native-webrtc"
react-native-webview:
:path: "../node_modules/react-native-webview"
React-NativeModulesApple:
:path: "../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios"
React-perflogger:
@ -887,6 +896,8 @@ EXTERNAL SOURCES: @@ -887,6 +896,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-image-crop-picker"
RNNotifee:
:path: "../node_modules/@notifee/react-native"
RNPDF:
:path: "../node_modules/react-native-view-pdf"
RNPermissions:
:path: "../node_modules/react-native-permissions"
RNReanimated:
@ -959,6 +970,7 @@ SPEC CHECKSUMS: @@ -959,6 +970,7 @@ SPEC CHECKSUMS:
react-native-sqlite-storage: f6d515e1c446d1e6d026aa5352908a25d4de3261
react-native-video: c26780b224543c62d5e1b2a7244a5cd1b50e8253
react-native-webrtc: 255a1172fd31525b952b36aef7b8e9a41de325e5
react-native-webview: bdc091de8cf7f8397653e30182efcd9f772e03b3
React-NativeModulesApple: c3e696ff867e4bc212266cbdf7e862e48a0166fd
React-perflogger: 43287389ea08993c300897a46f95cfac04bb6c1a
React-RCTActionSheet: 923afe77f9bb89da7c1f98e2730bfc9dde0eed6d
@ -989,6 +1001,7 @@ SPEC CHECKSUMS: @@ -989,6 +1001,7 @@ SPEC CHECKSUMS:
RNGestureHandler: 32a01c29ecc9bb0b5bf7bc0a33547f61b4dc2741
RNImageCropPicker: d9616a0cb9b72e8551ff94a7a5021fbd29050aa5
RNNotifee: 8e2d3df3f0e9ce8f5d1fe4c967431138190b6175
RNPDF: 8526c7210ff375c29d2461c51945e2f5480f7b60
RNPermissions: 0a4aa6c2f46c2cd55fbfa095bcdbad20118ea46f
RNReanimated: fdbaa9c964bbab7fac50c90862b6cc5f041679b9
RNScreens: 3c5b9f4a9dcde752466854b6109b79c0e205dad3

19
ios/taskme2.xcodeproj/project.pbxproj

@ -710,7 +710,10 @@ @@ -710,7 +710,10 @@
"-DFOLLY_MOBILE=1",
"-DFOLLY_USE_LIBCPP=1",
);
OTHER_LDFLAGS = "$(inherited) ";
OTHER_LDFLAGS = (
"$(inherited)",
" ",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
@ -906,7 +909,7 @@ @@ -906,7 +909,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = taskme2/taskme2.entitlements;
CURRENT_PROJECT_VERSION = 12;
DEVELOPMENT_TEAM = VCUZPZ9254;
DEVELOPMENT_TEAM = HQ3J3TDPR2;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = taskme2/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Task me ;)";
@ -943,7 +946,7 @@ @@ -943,7 +946,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = taskme2/taskme2.entitlements;
CURRENT_PROJECT_VERSION = 12;
DEVELOPMENT_TEAM = VCUZPZ9254;
DEVELOPMENT_TEAM = HQ3J3TDPR2;
INFOPLIST_FILE = taskme2/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Task me ;)";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
@ -1037,7 +1040,10 @@ @@ -1037,7 +1040,10 @@
"-DFOLLY_MOBILE=1",
"-DFOLLY_USE_LIBCPP=1",
);
OTHER_LDFLAGS = "$(inherited) ";
OTHER_LDFLAGS = (
"$(inherited)",
" ",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
};
@ -1107,7 +1113,10 @@ @@ -1107,7 +1113,10 @@
"-DFOLLY_MOBILE=1",
"-DFOLLY_USE_LIBCPP=1",
);
OTHER_LDFLAGS = "$(inherited) ";
OTHER_LDFLAGS = (
"$(inherited)",
" ",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;

32
package-lock.json generated

@ -83,7 +83,9 @@ @@ -83,7 +83,9 @@
"react-native-vector-icons": "^10.0.0",
"react-native-video": "^5.2.1",
"react-native-video-controls": "^2.8.1",
"react-native-view-pdf": "^0.14.0",
"react-native-webrtc": "^111.0.1",
"react-native-webview": "^13.8.1",
"react-native-wheel-pick": "^1.2.2",
"react-redux": "^8.1.2",
"redux": "^4.2.1",
@ -16094,6 +16096,15 @@ @@ -16094,6 +16096,15 @@
"prop-types": "*"
}
},
"node_modules/react-native-view-pdf": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/react-native-view-pdf/-/react-native-view-pdf-0.14.0.tgz",
"integrity": "sha512-tW5zamLE0vYIgoEjASLOOCPmW3x1MfJM13GgKar5nPjltU6gWzgpviILmiqeuJB+H7aTCh/UrT81GoYlO0Oedg==",
"peerDependencies": {
"react": ">=16.8.6",
"react-native": ">=0.60"
}
},
"node_modules/react-native-webrtc": {
"version": "111.0.6",
"resolved": "https://registry.npmjs.org/react-native-webrtc/-/react-native-webrtc-111.0.6.tgz",
@ -16118,6 +16129,27 @@ @@ -16118,6 +16129,27 @@
"url": "https://github.com/sponsors/mysticatea"
}
},
"node_modules/react-native-webview": {
"version": "13.8.1",
"resolved": "https://registry.npmjs.org/react-native-webview/-/react-native-webview-13.8.1.tgz",
"integrity": "sha512-7Jqm1WzWJrOWraBAXQfKtr/Uo5Jw/IJHzC40jYLwgV/eVGmLJ9BpGKw6QVw7wpRkjmTZ2Typ4B1aHJLJJQFslA==",
"dependencies": {
"escape-string-regexp": "2.0.0",
"invariant": "2.2.4"
},
"peerDependencies": {
"react": "*",
"react-native": "*"
}
},
"node_modules/react-native-webview/node_modules/escape-string-regexp": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
"integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
"engines": {
"node": ">=8"
}
},
"node_modules/react-native-wheel-pick": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/react-native-wheel-pick/-/react-native-wheel-pick-1.2.2.tgz",

2
package.json

@ -93,7 +93,9 @@ @@ -93,7 +93,9 @@
"react-native-vector-icons": "^10.0.0",
"react-native-video": "^5.2.1",
"react-native-video-controls": "^2.8.1",
"react-native-view-pdf": "^0.14.0",
"react-native-webrtc": "^111.0.1",
"react-native-webview": "^13.8.1",
"react-native-wheel-pick": "^1.2.2",
"react-redux": "^8.1.2",
"redux": "^4.2.1",

2
src/modules/root/index.tsx

@ -34,6 +34,7 @@ import { SafeAreaView } from 'react-native-safe-area-context' @@ -34,6 +34,7 @@ import { SafeAreaView } from 'react-native-safe-area-context'
import { StyleSheet } from 'react-native'
import { PartialTheme } from '@/shared/themes/interfaces'
import { useAppBadge, useAppSocketListener, useNetConnect } from './hooks'
import { PreviewFileModal } from '@/shared/components/modals/preview-file-modal.smart-component'
export const Navigation: FC = () => {
const activeModule = useSelector(selectActiveNavigationModule)
@ -111,6 +112,7 @@ export const Navigation: FC = () => { @@ -111,6 +112,7 @@ export const Navigation: FC = () => {
<ActionSheet />
<FancyboxSmart />
<FullscreenVideoSmart />
<PreviewFileModal />
{GlobalComponents}
</>
)

16
src/modules/tasks/atoms/document-preview.atom.tsx

@ -1,15 +1,21 @@ @@ -1,15 +1,21 @@
import {
$size,
appEvents,
FileType,
IconComponent,
RemoteImage,
Txt,
useTheme,
} from '@/shared'
import { getIconNameByExtension, isImage, isVideo } from '@/shared/helpers'
import {
getIconNameByExtension,
getUrlExtension,
isImage,
isVideo,
} from '@/shared/helpers'
import { PartialTheme } from '@/shared/themes/interfaces'
import React, { FC, useState } from 'react'
import { Linking, StyleSheet, TouchableOpacity, View } from 'react-native'
import { StyleSheet, TouchableOpacity, View } from 'react-native'
import Video from 'react-native-video'
interface IProps {
@ -91,7 +97,11 @@ export const DocumentPreview: FC<IProps> = ({ url, allowDelete, onDelete }) => { @@ -91,7 +97,11 @@ export const DocumentPreview: FC<IProps> = ({ url, allowDelete, onDelete }) => {
url,
})
else if (isVideo(url)) appEvents.emit('openVideoFullscreen', { url })
else Linking.openURL(url)
else
appEvents.emit('previewFile', {
fileUrl: url,
mimeType: getUrlExtension(url) as FileType,
})
}
return (

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

@ -316,7 +316,7 @@ export const shareFileOutside = async ( @@ -316,7 +316,7 @@ export const shareFileOutside = async (
}
}
const validFileType = [FileType.DOC, FileType.DOCX, FileType.PDF]
const validFileType = [FileType.DOC, FileType.DOCX, FileType.PDF, FileType.TXT]
const previewFile = (url: string) => {
try {
@ -330,7 +330,7 @@ const previewFile = (url: string) => { @@ -330,7 +330,7 @@ const previewFile = (url: string) => {
toFile: localFile,
}
RNFS.downloadFile(options).promise.then(() =>
FileViewer.open(localFile),
FileViewer.open(localFile, { showOpenWithDialog: false }),
)
} catch (error) {
appEvents.emit('openInfoModal', {

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

@ -1,3 +1,3 @@ @@ -1,3 +1,3 @@
export * from './screen-layout-content.component'
export * from './screen-layout.component'
export * from './auth-layout.component'
export * from './auth-layout.component'

83
src/shared/components/modals/preview-file-modal.smart-component.tsx

@ -0,0 +1,83 @@ @@ -0,0 +1,83 @@
import { useEventsListener, useTheme } from '@/shared/hooks'
import React, { useMemo, useState } from 'react'
import { TouchableOpacity, StyleSheet, Platform } from 'react-native'
import { IconComponent } from '../elements'
import Modal from 'react-native-modal'
import { fsService } from '@/services/system'
import PDFView from 'react-native-view-pdf'
import { FileType } from '@/shared/enums'
export const PreviewFileModal = () => {
const [params, setParams] = useState(null)
const { styles } = useTheme(createStyles)
useEventsListener('previewFile', data => {
if (Platform.OS === 'ios') {
fsService.previewFile(data.fileUrl)
} else {
setParams(data)
}
})
const close = () => {
setParams(null)
}
const renderItem = useMemo(() => {
if (params?.mimeType === FileType.PDF) {
return (
<PDFView
fadeInDuration={250.0}
style={{ flex: 1 }}
resource={params?.fileUrl}
resourceType={'url'}
onLoad={() => console.log(`PDF rendered from url`)}
onError={error => console.log('Cannot render PDF', error)}
/>
)
}
return null
}, [params])
return (
<Modal
isVisible={Boolean(params)}
onBackdropPress={close}
coverScreen={true}
onModalHide={close}
backdropOpacity={1}
animationOutTiming={1}
style={styles.modal}
backdropTransitionOutTiming={1}
swipeDirection="down"
onSwipeComplete={close}>
<TouchableOpacity onPress={close} style={styles.closeBtn}>
<IconComponent
name="xcircle-1"
size={32}
color="#000"
onPress={close}
/>
</TouchableOpacity>
{renderItem}
</Modal>
)
}
const createStyles = () =>
StyleSheet.create({
modal: {
margin: 0,
width: '100%',
height: '100%',
},
image: {
width: '100%',
height: '90%',
},
closeBtn: {
position: 'absolute',
top: 50,
right: 5,
zIndex: 99999,
},
})

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

@ -4,6 +4,7 @@ import { @@ -4,6 +4,7 @@ import {
convertToKilobytes,
convertToMegabytes,
getIconNameByExtension,
getUrlExtension,
} from '@/shared/helpers'
import { useTheme } from '@/shared/hooks'
import { PartialTheme } from '@/shared/themes/interfaces'
@ -13,7 +14,8 @@ import { StyleSheet, TouchableOpacity, View } from 'react-native' @@ -13,7 +14,8 @@ 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'
import { appEvents } from '@/shared/events'
import { FileType } from '@/shared/enums'
interface ChatItemFileProps extends IChatMessage {
onLongPress?: () => void
@ -37,7 +39,10 @@ export const ChatItemFile: FC<ChatItemFileProps> = props => { @@ -37,7 +39,10 @@ export const ChatItemFile: FC<ChatItemFileProps> = props => {
const previewFile = useCallback(() => {
const content = props.content
fsService.previewFile(content?.fileUrl)
appEvents.emit('previewFile', {
fileUrl: content?.fileUrl,
mimeType: getUrlExtension(content?.name) as FileType,
})
}, [props.content])
const iconName = getIconNameByExtension(props?.content?.fileUrl)

3
src/shared/components/plugins/index.ts

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
export * from './masked-input.component'
export * from './image-crop-picker.component'
export * from './calendar.component'
export * from './accordion.component'
export * from './accordion.component'
export * from './webview.component'

17
src/shared/components/plugins/webview.component.tsx

@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
import React, { FC } from 'react'
import { WebView } from 'react-native-webview'
interface IProps {
url: string
}
export const WebviewPlugin: FC<IProps> = ({ url }) => {
return (
<WebView
source={{ uri: url }}
style={{ flex: 1 }}
originWhitelist={['file://*', '*']}
mixedContentMode="always"
/>
)
}

12
src/shared/events/index.ts

@ -1,6 +1,12 @@ @@ -1,6 +1,12 @@
import { RecordAudioModalSettings } from '@/modules/media/interfaces'
import { Events } from 'jet-tools'
import { ChatMemberRole, EntityType, TaskEvent, TaxonomyTypes } from '../enums'
import {
ChatMemberRole,
EntityType,
FileType,
TaskEvent,
TaxonomyTypes,
} from '../enums'
import {
IChatMessage,
IComment,
@ -133,6 +139,10 @@ export type AppEvents = { @@ -133,6 +139,10 @@ export type AppEvents = {
onSelect: (params: any) => void
currentIso: string
}
previewFile: {
fileUrl: string
mimeType: FileType
}
}
export type SocketEvents = {

6
src/shared/helpers/fs.helpers.ts

@ -273,3 +273,9 @@ export const getIconNameByExtension = (name: string) => { @@ -273,3 +273,9 @@ export const getIconNameByExtension = (name: string) => {
export const getUrlExtension = (link: string) => {
return link.split(/[#?]/)[0].split('.').pop().trim()
}
export const getFileNameFromUrl = (link: string) => {
const fileNameWithExtension = link.split('/').pop()
const fileName = fileNameWithExtension.split('.').slice(0, -1).join('.')
return fileName
}

Loading…
Cancel
Save