Преглед на файлове

example: revive React Native example (#4164)

Giacomo Cerquone преди 2 години
родител
ревизия
38dfb70d92

+ 8 - 0
.eslintrc.js

@@ -328,6 +328,14 @@ module.exports = {
         'examples/vue/**/*.js',
         'examples/vue3/**/*.js',
       ],
+      rules: {
+        'no-unused-vars': [
+          'error',
+          {
+            'varsIgnorePattern': 'React',
+          },
+        ],
+      },
       parserOptions: {
         sourceType: 'module',
       },

+ 2 - 1
examples/react-native-expo/.eslintrc.json

@@ -1,5 +1,6 @@
 {
   "rules": {
-    "react/react-in-jsx-scope": "off"
+    "react/react-in-jsx-scope": "off",
+    "no-use-before-define": "off"
   }
 }

+ 4 - 0
examples/react-native-expo/.expo-shared/assets.json

@@ -0,0 +1,4 @@
+{
+  "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true,
+  "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true
+}

+ 9 - 2
examples/react-native-expo/.gitignore

@@ -1,7 +1,14 @@
-node_modules/**/*
-.expo/*
+node_modules/
+.expo/
+dist/
 npm-debug.*
 *.jks
+*.p8
 *.p12
 *.key
 *.mobileprovision
+*.orig.*
+web-build/
+
+# macOS
+.DS_Store

+ 0 - 1
examples/react-native-expo/.watchmanconfig

@@ -1 +0,0 @@
-{}

+ 116 - 119
examples/react-native-expo/App.js

@@ -1,175 +1,172 @@
-// import * as Expo from 'expo'
-import React from 'react'
-import {
-  Text,
-  View,
-  AsyncStorage,
-  Image,
-} from 'react-native'
+import React, { useEffect, useState, useCallback } from 'react'
+import { Text, View, Image, StyleSheet } from 'react-native'
+import AsyncStorage from '@react-native-async-storage/async-storage'
 import Uppy from '@uppy/core'
 import Tus from '@uppy/tus'
-import UppyFilePicker from '@uppy/react-native'
+import FilePicker  from '@uppy/react-native'
+import  useUppy  from '@uppy/react/lib/useUppy'
 import FileList from './FileList'
 import PauseResumeButton from './PauseResumeButton'
 import ProgressBar from './ProgressBar'
 import SelectFiles from './SelectFilesButton'
 import getTusFileReader from './tusFileReader'
 
-export default class App extends React.Component {
-  constructor () {
-    super()
-
-    this.state = {
-      progress: 0,
-      total: 0,
-      file: null,
-      uploadURL: null,
-      isFilePickerVisible: false,
-      isPaused: false,
-      uploadStarted: false,
-      uploadComplete: false,
-      info: null,
-      totalProgress: 0,
-    }
-
-    this.isReactNative = (typeof navigator !== 'undefined'
-      && typeof navigator.product === 'string'
-      && navigator.product.toLowerCase() === 'reactnative')
-
-    this.showFilePicker = this.showFilePicker.bind(this)
-    this.hideFilePicker = this.hideFilePicker.bind(this)
-    this.togglePauseResume = this.togglePauseResume.bind(this)
+export default function App () {
+  const [state, _setState] = useState({
+    progress: 0,
+    total: 0,
+    file: null,
+    uploadURL: null,
+    isFilePickerVisible: false,
+    isPaused: false,
+    uploadStarted: false,
+    uploadComplete: false,
+    info: null,
+    totalProgress: 0,
+  })
+
+  const setState = useCallback((newState) => _setState((oldState) => ({ ...oldState, ...newState })), [])
+
+  const uppy = useUppy(() => {
+    return new Uppy({ autoProceed: true, debug: true })
+      .use(Tus, {
+        endpoint: 'https://tusd.tusdemo.net/files/',
+        urlStorage: AsyncStorage,
+        fileReader: getTusFileReader,
+        chunkSize: 10 * 1024 * 1024, // keep the chunk size small to avoid memory exhaustion
+      })
+  })
 
-    console.log('Is this React Native?', this.isReactNative)
-    this.uppy = new Uppy({ autoProceed: true, debug: true })
-    this.uppy.use(Tus, {
-      endpoint: 'https://tusd.tusdemo.net/files/',
-      urlStorage: AsyncStorage,
-      fileReader: getTusFileReader,
-      chunkSize: 10 * 1024 * 1024, // keep the chunk size small to avoid memory exhaustion
-    })
-    this.uppy.on('upload-progress', (file, progress) => {
-      this.setState({
+  useEffect(() => {
+    uppy.on('upload-progress', (file, progress) => {
+      setState({
         progress: progress.bytesUploaded,
         total: progress.bytesTotal,
-        totalProgress: this.uppy.state.totalProgress,
+        totalProgress: uppy.state.totalProgress,
         uploadStarted: true,
       })
     })
-    this.uppy.on('upload-success', () => {
+    uppy.on('upload-success', () => {
       // console.log(file.name, response)
     })
-    this.uppy.on('complete', (result) => {
-      this.setState({
-        status: 'Upload complete ✅',
+    uppy.on('complete', (result) => {
+      setState({
+        status: result.successful[0] ? 'Upload complete ✅' : 'Upload errored ❌',
         uploadURL: result.successful[0] ? result.successful[0].uploadURL : null,
         uploadComplete: true,
         uploadStarted: false,
       })
       console.log('Upload complete:', result)
     })
-
-    this.uppy.on('info-visible', () => {
-      const { info } = this.uppy.getState()
-      this.setState({
+    uppy.on('info-visible', () => {
+      const { info } = uppy.getState()
+      setState({
         info,
       })
       console.log('uppy-info:', info)
     })
-
-    this.uppy.on('info-hidden', () => {
-      this.setState({
+    uppy.on('info-hidden', () => {
+      setState({
         info: null,
       })
     })
-  }
+  }, [setState, uppy])
 
-  showFilePicker () {
-    this.setState({
+  const showFilePicker = () => {
+    setState({
       isFilePickerVisible: true,
       uploadStarted: false,
       uploadComplete: false,
     })
   }
 
-  hideFilePicker () {
-    this.setState({
+  const hideFilePicker = () => {
+    setState({
       isFilePickerVisible: false,
     })
   }
 
-  togglePauseResume () {
-    if (this.state.isPaused) {
-      this.uppy.resumeAll()
-      this.setState({
+  const togglePauseResume = () => {
+    if (state.isPaused) {
+      uppy.resumeAll()
+      setState({
         isPaused: false,
       })
     } else {
-      this.uppy.pauseAll()
-      this.setState({
+      uppy.pauseAll()
+      setState({
         isPaused: true,
       })
     }
   }
 
-  render () {
-    return (
-      <View style={{
-        paddingTop: 100,
-        paddingLeft: 50,
-        paddingRight: 50,
-        flex: 1,
-      }}
+  return (
+    <View
+      style={styles.root}
+    >
+      <Text
+        style={styles.title}
       >
-        <Text style={{
-          fontSize: 25,
-          marginBottom: 20,
-          textAlign: 'center',
-        }}
+        Uppy in React Native
+      </Text>
+      <View style={{ alignItems: 'center' }}>
+        <Image
+          style={styles.logo}
+          source={require('./assets/uppy-logo.png')}
+        />
+      </View>
+      <SelectFiles showFilePicker={showFilePicker} />
+
+      {state.info ? (
+        <Text
+          style={{
+            marginBottom: 10,
+            marginTop: 10,
+            color: '#b8006b',
+          }}
         >
-          Uppy in React Native
+          {state.info.message}
         </Text>
-        <View style={{ alignItems: 'center' }}>
-          <Image
-            style={{ width: 80, height: 78, marginBottom: 50 }}
-            source={require('./assets/uppy-logo.png')}
-          />
-        </View>
-        <SelectFiles showFilePicker={this.showFilePicker} />
-
-        {this.state.info ? (
-          <Text
-            style={{
-              marginBottom: 10,
-              marginTop: 10,
-              color: '#b8006b',
-            }}
-          >
-            {this.state.info.message}
-          </Text>
-        ) : null}
-
-        <ProgressBar progress={this.state.totalProgress} />
-
-        <PauseResumeButton
-          isPaused={this.state.isPaused}
-          onPress={this.togglePauseResume}
-          uploadStarted={this.state.uploadStarted}
-          uploadComplete={this.state.uploadComplete}
-        />
-
-        <UppyFilePicker
-          uppy={this.uppy}
-          show={this.state.isFilePickerVisible}
-          onRequestClose={this.hideFilePicker}
+      ) : null}
+
+      <ProgressBar progress={state.totalProgress} />
+
+      <PauseResumeButton
+        isPaused={state.isPaused}
+        onPress={togglePauseResume}
+        uploadStarted={state.uploadStarted}
+        uploadComplete={state.uploadComplete}
+      />
+
+      {uppy && (
+        <FilePicker
+          uppy={uppy}
+          show={state.isFilePickerVisible}
+          onRequestClose={hideFilePicker}
           companionUrl="http://localhost:3020"
         />
+      )}
 
-        <FileList uppy={this.uppy} />
+      {uppy && <FileList uppy={uppy} />}
 
-        {/* <Text>{this.state.status ? 'Status: ' + this.state.status : null}</Text>
-        <Text>{this.state.progress} of {this.state.total}</Text> */}
-      </View>
-    )
-  }
+      {state.status && <Text>Status: {state.status}</Text>}
+      <Text>{state.progress} of {state.total}</Text>
+    </View>
+  )
 }
+
+const styles = StyleSheet.create({
+  root: {
+    paddingTop: 100,
+    paddingBottom: 20,
+    paddingLeft: 50,
+    paddingRight: 50,
+    flex: 1,
+  },
+  title: {
+    fontSize: 25,
+    marginBottom: 20,
+    textAlign: 'center',
+  },
+  logo: { width: 80, height: 78, marginBottom: 50 },
+})

+ 18 - 15
examples/react-native-expo/FileList.js

@@ -1,36 +1,37 @@
-import React from 'react' // eslint-disable-line no-unused-vars
+import React from 'react'
 import { StyleSheet, View, FlatList, Text, Image } from 'react-native'
 
 import getFileTypeIcon from '@uppy/dashboard/lib/utils/getFileTypeIcon.js'
-import truncateString from '@uppy/dashboard/lib/utils/truncateString.js'
 import renderStringFromJSX from 'preact-render-to-string'
 
-// function truncateString (str) {
-//   const maxChars = 20
-//   if (str.length > maxChars) {
-//     return str.substring(0, 25) + '...'
-//   }
+const fileIcon = require('./assets/file-icon.png')
 
-//   return str
-// }
+const truncateString = (str) => {
+  const maxChars = 20
+  if (str.length > maxChars) {
+    return `${str.substring(0, 25)}...`
+  }
+
+  return str
+}
 
 function FileIcon () {
   return (
     <View style={styles.itemIconContainer}>
       <Image
         style={styles.itemIcon}
-        source={require('./assets/file-icon.png')}
+        source={fileIcon}
       />
     </View>
   )
 }
 
-function UppyDashboardFileIcon (props) {
-  const icon = renderStringFromJSX(getFileTypeIcon(props.type).icon)
+function UppyDashboardFileIcon ({ type }) {
+  const icon = renderStringFromJSX(getFileTypeIcon(type).icon)
   if (!icon) {
     return <FileIcon />
   }
-  const { color } = getFileTypeIcon(props.type)
+  const { color } = getFileTypeIcon(type)
   return (
     <View
       style={{
@@ -43,8 +44,8 @@ function UppyDashboardFileIcon (props) {
   )
 }
 
-export default function FileList (props) {
-  const uppyFiles = props.uppy.state.files
+export default function FileList ({ uppy }) {
+  const uppyFiles = uppy.store.state.files
   const uppyFilesArray = Object.keys(uppyFiles).map((id) => uppyFiles[id])
 
   return (
@@ -80,6 +81,8 @@ const styles = StyleSheet.create({
     marginBottom: 20,
     flex: 1,
     justifyContent: 'center',
+    alignItems:'center',
+    marginRight: -25,
   },
   item: {
     width: 100,

+ 5 - 5
examples/react-native-expo/PauseResumeButton.js

@@ -1,20 +1,20 @@
-import React from 'react' // eslint-disable-line no-unused-vars
+import React from 'react'
 import { StyleSheet, Text, TouchableHighlight } from 'react-native'
 
-export default function PauseResumeButton (props) {
-  if (!props.uploadStarted || props.uploadComplete) {
+export default function PauseResumeButton ({ uploadStarted, uploadComplete, isPaused, onPress }) {
+  if (!uploadStarted || uploadComplete) {
     return null
   }
 
   return (
     <TouchableHighlight
-      onPress={props.onPress}
+      onPress={onPress}
       style={styles.button}
     >
       <Text
         style={styles.text}
       >
-        {props.isPaused ? 'Resume' : 'Pause'}
+        {isPaused ? 'Resume' : 'Pause'}
       </Text>
     </TouchableHighlight>
   )

+ 24 - 20
examples/react-native-expo/ProgressBar.js

@@ -1,33 +1,37 @@
-import React from 'react' // eslint-disable-line no-unused-vars
-import { View, Text } from 'react-native'
+import React from 'react'
+import { View, Text, StyleSheet } from 'react-native'
 
-export default function ProgressBar (props) {
-  const { progress } = props
-
-  const colorGreen = '#0b8600'
-  const colorBlue = '#006bb7'
+const colorGreen = '#0b8600'
+const colorBlue = '#006bb7'
 
+export default function ProgressBar ({ progress }) {
   return (
-    <View style={{
-      marginTop: 15,
-      marginBottom: 15,
-    }}
-    >
+    <View style={styles.root}>
       <View
-        style={{
-          height: 5,
-          overflow: 'hidden',
-          backgroundColor: '#dee1e3',
-        }}
+        style={styles.wrapper}
       >
-        <View style={{
-          height: 5,
+        <View style={[styles.bar, {
           backgroundColor: progress === 100 ? colorGreen : colorBlue,
           width: `${progress}%`,
-        }}
+        }]}
         />
       </View>
       <Text>{progress ? `${progress}%` : null}</Text>
     </View>
   )
 }
+
+const styles = StyleSheet.create({
+  root: {
+    marginTop: 15,
+    marginBottom: 15,
+  },
+  wrapper:{
+    height: 5,
+    overflow: 'hidden',
+    backgroundColor: '#dee1e3',
+  },
+  bar: {
+    height: 5,
+  },
+})

+ 3 - 3
examples/react-native-expo/SelectFilesButton.js

@@ -1,10 +1,10 @@
-import React from 'react' // eslint-disable-line no-unused-vars
+import React from 'react'
 import { Text, TouchableHighlight, StyleSheet } from 'react-native'
 
-export default function SelectFiles (props) {
+export default function SelectFiles ({ showFilePicker }) {
   return (
     <TouchableHighlight
-      onPress={props.showFilePicker}
+      onPress={showFilePicker}
       style={styles.button}
     >
       <Text style={styles.text}>Select files</Text>

+ 12 - 9
examples/react-native-expo/app.json

@@ -1,13 +1,7 @@
 {
   "expo": {
-    "name": "Uppy RN Example",
-    "slug": "UppyRNExample",
-    "privacy": "public",
-    "sdkVersion": "32.0.0",
-    "platforms": [
-      "ios",
-      "android"
-    ],
+    "name": "react-native-expo",
+    "slug": "react-native-expo",
     "version": "1.0.0",
     "orientation": "portrait",
     "icon": "./assets/icon.png",
@@ -24,6 +18,15 @@
     ],
     "ios": {
       "supportsTablet": true
+    },
+    "android": {
+      "adaptiveIcon": {
+        "foregroundImage": "./assets/adaptive-icon.png",
+        "backgroundColor": "#FFFFFF"
+      }
+    },
+    "web": {
+      "favicon": "./assets/favicon.png"
     }
   }
-}
+}

BIN
examples/react-native-expo/assets/adaptive-icon.png


BIN
examples/react-native-expo/assets/favicon.png


+ 1 - 1
examples/react-native-expo/babel.config.cjs → examples/react-native-expo/babel.config.js

@@ -1,4 +1,4 @@
-module.exports = function babel (api) {
+module.exports = (api) => {
   api.cache(true)
   return {
     presets: ['babel-preset-expo'],

+ 8 - 0
examples/react-native-expo/index.js

@@ -0,0 +1,8 @@
+import { registerRootComponent } from 'expo'
+
+import App from './App'
+
+// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
+// It also ensures that whether you load the app in Expo Go or in a native build,
+// the environment is set up appropriately
+registerRootComponent(App)

+ 22 - 0
examples/react-native-expo/metro.config.js

@@ -0,0 +1,22 @@
+// Learn more https://docs.expo.dev/guides/monorepos
+const { getDefaultConfig } = require('expo/metro-config')
+const path = require('node:path')
+
+// Find the project and workspace directories
+const projectRoot = __dirname
+// This can be replaced with `find-yarn-workspace-root`
+const workspaceRoot = path.resolve(projectRoot, '../../')
+
+const config = getDefaultConfig(projectRoot)
+
+// 1. Watch all files within the monorepo
+config.watchFolders = [workspaceRoot]
+// 2. Let Metro know where to resolve packages and in what order
+config.resolver.nodeModulesPaths = [
+  path.resolve(projectRoot, 'node_modules'),
+  path.resolve(workspaceRoot, 'node_modules'),
+]
+// 3. Force Metro to resolve (sub)dependencies only from the `nodeModulesPaths`
+config.resolver.disableHierarchicalLookup = true
+
+module.exports = config

+ 22 - 13
examples/react-native-expo/package.json

@@ -1,27 +1,36 @@
 {
   "name": "@uppy-example/react-native-expo",
-  "version": "0.0.0",
+  "version": "1.0.0",
+  "main": "index.js",
+  "scripts": {
+    "start": "expo start",
+    "android": "expo start --android",
+    "ios": "expo start --ios",
+    "web": "expo start --web",
+    "eject": "expo eject"
+  },
   "dependencies": {
+    "@react-native-async-storage/async-storage": "~1.15.0",
     "@uppy/core": "workspace:*",
     "@uppy/dashboard": "workspace:*",
     "@uppy/instagram": "workspace:*",
+    "@uppy/react": "workspace:*",
     "@uppy/react-native": "workspace:*",
     "@uppy/tus": "workspace:*",
     "@uppy/url": "workspace:*",
     "@uppy/xhr-upload": "workspace:*",
-    "babel-preset-expo": "^5.0.0",
     "base64-js": "^1.3.0",
-    "expo": "^42.0.0",
+    "expo": "~43.0.2",
+    "expo-file-system": "~13.0.3",
+    "expo-status-bar": "~1.1.0",
     "preact-render-to-string": "^5.1.0",
-    "react": "^16.8.6",
-    "react-native": "~0.63.4"
+    "react": "17.0.1",
+    "react-dom": "17.0.1",
+    "react-native": "0.64.3",
+    "react-native-web": "0.17.1"
   },
-  "main": "node_modules/expo/AppEntry.js",
-  "private": true,
-  "scripts": {
-    "android": "expo start --android",
-    "eject": "expo eject",
-    "ios": "expo start --ios",
-    "start": "expo start"
-  }
+  "devDependencies": {
+    "@babel/core": "^7.12.9"
+  },
+  "private": true
 }

+ 6 - 5
examples/react-native-expo/readme.md

@@ -9,16 +9,17 @@
 To run this example, make sure you've correctly installed the **repository root**:
 
 ```bash
-npm install
-npm run build
+yarn install
+yarn run build
 ```
 
 That will also install the dependencies for this example.
 
-Then, again in the **repository root**, start this example by doing:
+Then, start this example by doing:
 
 ```bash
-npm run example react-native-expo
+cd examples/react-native-expo
+yarn start
 ```
 
-Then a tab will open in your browser with Expo UI, and you can choose to run the example in either an iOS or Android simulator, or right on your mobile device with an Expo app — might be easier, if you don’t want to install emulators.
+Then you'll see a menu within your terminal where you can chose where to open the app (Android, iOS, device etc.)

+ 5 - 10
examples/react-native-expo/tusFileReader.js

@@ -1,8 +1,8 @@
-import * as Expo from 'expo'
+import * as FileSystem from 'expo-file-system'
 import base64 from 'base64-js'
 
 export default function getTusFileReader (file, chunkSize, cb) {
-  Expo.FileSystem.getInfoAsync(file.uri, { size: true }).then((info) => {
+  FileSystem.getInfoAsync(file.uri, { size: true }).then((info) => {
     cb(null, new TusFileReader(file, info.size))
   }).catch(cb)
 }
@@ -14,18 +14,13 @@ class TusFileReader {
   }
 
   slice (start, end, cb) {
-    end = Math.min(end, this.size)
     const options = {
-      encoding: Expo.FileSystem.EncodingTypes.Base64,
-      length: end - start,
+      encoding: FileSystem.EncodingType.Base64,
+      length: Math.min(end, this.size) - start,
       position: start,
     }
-    Expo.FileSystem.readAsStringAsync(this.file.uri, options).then((data) => {
+    FileSystem.readAsStringAsync(this.file.uri, options).then((data) => {
       cb(null, base64.toByteArray(data))
     }).catch(cb)
   }
-
-  close () {
-
-  }
 }

+ 3 - 8
packages/@uppy/react-native/file-picker/selectImage.js

@@ -1,13 +1,8 @@
-// Using leading underscore so eslint compat plugin doesn't yell at us.
-import * as _Permissions from 'expo-permissions' // eslint-disable-line import/no-unresolved
-import * as ImagePicker from 'expo-image-picker' // eslint-disable-line import/no-unresolved
+import * as ImagePicker from 'expo-image-picker'
 
 function selectImageWithExpo (options) {
-  return _Permissions.askAsync(_Permissions.CAMERA_ROLL)
-    .then((isAllowed) => (isAllowed ? ImagePicker.launchImageLibraryAsync(options)
-      : Promise.reject(new Error('Permissions denied'))))
-    .then((result) => (!result.cancelled ? result
-      : Promise.reject(new Error('Operation cancelled'))))
+  // No permissions request is necessary for launching the image library
+  return ImagePicker.launchImageLibraryAsync(options)
 }
 
 export default selectImageWithExpo

+ 6 - 6
packages/@uppy/react-native/file-picker/takePicture.js

@@ -1,12 +1,12 @@
-// Using leading underscore so eslint compat plugin doesn't yell at us.
-import * as _Permissions from 'expo-permissions' // eslint-disable-line import/no-unresolved
-import * as ImagePicker from 'expo-image-picker' // eslint-disable-line import/no-unresolved
+import * as ImagePicker from 'expo-image-picker'
 
 function takePictureWithExpo () {
-  return _Permissions.askAsync(_Permissions.CAMERA)
-    .then((isAllowed) => (isAllowed ? ImagePicker.launchCameraAsync({ allowsEditing: true })
+  return ImagePicker.getCameraPermissionsAsync()
+    .then(({ granted }) => (granted
+      ? ImagePicker.launchCameraAsync({ allowsEditing: true })
       : Promise.reject(new Error('Permissions denied'))))
-    .then((result) => (!result.cancelled ? result
+    .then((result) => (!result.cancelled
+      ? result
       : Promise.reject(new Error('Operation cancelled'))))
 }
 

+ 6 - 6
packages/@uppy/react-native/package.json

@@ -21,16 +21,16 @@
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   "dependencies": {
+    "@uppy/core": "workspace:*",
     "@uppy/instagram": "workspace:^",
-    "@uppy/url": "workspace:^"
+    "@uppy/url": "workspace:^",
+    "expo-document-picker": "^10.3.0",
+    "expo-image-picker": "^13.3.1",
+    "react-native-super-grid": "*"
   },
   "peerDependencies": {
     "expo": ">=33.0.0",
-    "expo-document-picker": ">=6.0.0",
-    "expo-image-picker": ">=6.0.0",
-    "expo-permissions": ">=6.0.0",
     "react": "^16.0.0 || ^17.0.0 || ^18.0.0",
-    "react-native": "*",
-    "react-native-super-grid": "*"
+    "react-native": "*"
   }
 }

Файловите разлики са ограничени, защото са твърде много
+ 403 - 99
yarn.lock


Някои файлове не бяха показани, защото твърде много файлове са промени