diff --git a/components/camera-controls/camera-controls.component.jsx b/components/camera-controls/camera-controls.component.jsx new file mode 100644 index 0000000..95db222 --- /dev/null +++ b/components/camera-controls/camera-controls.component.jsx @@ -0,0 +1,102 @@ +// src/toolbar.component.js file +import { Ionicons } from "@expo/vector-icons"; +import { Camera } from "expo-camera"; +import React from "react"; +import { + StyleSheet, + TouchableOpacity, + TouchableWithoutFeedback, + View, +} from "react-native"; + +const styles = StyleSheet.create({ + alignCenter: { + flex: 1, + alignItems: "center", + justifyContent: "center", + }, + bottomToolbar: { + marginTop: "auto", + height: 100, + display: "flex", + justifyContent: "space-evenly", + alignItems: "center", + flexDirection: "row", + }, + captureBtn: { + width: 60, + height: 60, + borderWidth: 2, + borderRadius: 60, + borderColor: "#FFFFFF", + }, + captureBtnActive: { + width: 80, + height: 80, + }, + captureBtnInternal: { + width: 76, + height: 76, + borderWidth: 2, + borderRadius: 76, + backgroundColor: "red", + borderColor: "transparent", + }, +}); + +const { FlashMode: CameraFlashModes, Type: CameraTypes } = Camera.Constants; + +export default function CameraControls({ + capturing = false, + cameraType = CameraTypes.back, + flashMode = CameraFlashModes.off, + setFlashMode, + setCameraType, + onCaptureIn, + onCaptureOut, + onLongCapture, + onShortCapture, +}) { + return ( + + + setFlashMode( + flashMode === CameraFlashModes.on + ? CameraFlashModes.off + : CameraFlashModes.on + ) + } + > + + + + + + {capturing && } + + + + + setCameraType( + cameraType === CameraTypes.back + ? CameraTypes.front + : CameraTypes.back + ) + } + > + + + + ); +} diff --git a/components/job-list-item/job-list-item.component.jsx b/components/job-list-item/job-list-item.component.jsx index 0e0c780..f4ee995 100644 --- a/components/job-list-item/job-list-item.component.jsx +++ b/components/job-list-item/job-list-item.component.jsx @@ -9,6 +9,7 @@ import Swipeable from "react-native-gesture-handler/Swipeable"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; import { setCameraJob, setCameraJobId } from "../../redux/app/app.actions"; +import Dinero from "dinero.js"; import styles from "../styles"; const mapStateToProps = createStructuredSelector({ @@ -71,7 +72,11 @@ export function JobListItem({ setCameraJob, setCameraJobId, item }) { {item.ins_co_nm || ""} - {item.clm_total || ""} + + {Dinero({ + amount: Math.round(item.clm_total * 100), + }).toFormat() || ""} + diff --git a/components/screen-camera/screen-camera.component.jsx b/components/screen-camera/screen-camera.component.jsx index 7ae1a04..40d3634 100644 --- a/components/screen-camera/screen-camera.component.jsx +++ b/components/screen-camera/screen-camera.component.jsx @@ -1,12 +1,7 @@ -import { - FontAwesome, - Ionicons, - MaterialCommunityIcons, -} from "@expo/vector-icons"; +import { Ionicons } from "@expo/vector-icons"; import { useNavigation } from "@react-navigation/native"; import { Camera } from "expo-camera"; import * as FileSystem from "expo-file-system"; -import * as Permissions from "expo-permissions"; import React, { useEffect, useRef, useState } from "react"; import { SafeAreaView, Text, TouchableOpacity, View } from "react-native"; import { connect } from "react-redux"; @@ -16,6 +11,7 @@ import { selectCurrentCameraJobId, } from "../../redux/app/app.selectors"; import { addPhoto } from "../../redux/photos/photos.actions"; +import CameraControls from "../camera-controls/camera-controls.component"; const mapStateToProps = createStructuredSelector({ cameraJobId: selectCurrentCameraJobId, @@ -28,29 +24,29 @@ const mapDispatchToProps = (dispatch) => ({ export function ScreenCamera({ cameraJobId, cameraJob, addPhoto }) { const navigation = useNavigation(); const [hasPermission, setHasPermission] = useState(null); - const [rollPermision, setRollPermission] = useState(null); - const [type, setType] = useState(Camera.Constants.Type.back); + const [state, setState] = useState({ + flashMode: Camera.Constants.FlashMode.off, + capturing: null, + cameraType: Camera.Constants.Type.back, + }); const cameraRef = useRef(null); useEffect(() => { (async () => { const { status } = await Camera.requestPermissionsAsync(); setHasPermission(status === "granted"); - // camera roll - const { cam_roll } = await Permissions.askAsync(Permissions.CAMERA_ROLL); - setRollPermission(cam_roll === "granted"); })(); }, []); - const handleCameraType = () => { - setType( - type === Camera.Constants.Type.back - ? Camera.Constants.Type.front - : Camera.Constants.Type.back - ); + const setFlashMode = (flashMode) => setState({ ...state, flashMode }); + const setCameraType = (cameraType) => setState({ ...state, cameraType }); + const handleCaptureIn = () => setState({ ...state, capturing: true }); + + const handleCaptureOut = () => { + if (state.capturing) cameraRef.current.stopRecording(); }; - const handleTakePicture = async () => { + const handleShortCapture = async () => { console.log("Taking the picture!"); if (cameraRef.current) { const options = { @@ -60,6 +56,7 @@ export function ScreenCamera({ cameraJobId, cameraJob, addPhoto }) { }; let photo = await cameraRef.current.takePictureAsync(options); + setState({ ...state, capturing: false }); console.log("ScreenCamera -> photo", photo); const filename = photo.uri.substring(photo.uri.lastIndexOf("/") + 1); @@ -70,24 +67,60 @@ export function ScreenCamera({ cameraJobId, cameraJob, addPhoto }) { to: newUri, }); - addPhoto({ ...photo, id: filename, uri: newUri, jobId: cameraJobId }); + addPhoto({ + ...photo, + id: filename, + uri: newUri, + jobId: cameraJobId, + video: false, + }); + } + }; + + const handleLongCapture = async () => { + console.log("Taking a video!"); + if (cameraRef.current) { + let video = await cameraRef.current.recordAsync(); + setState({ ...state, capturing: false }); + + const filename = video.uri.substring(video.uri.lastIndexOf("/") + 1); + const newUri = FileSystem.documentDirectory + "photos/" + filename; + + await FileSystem.copyAsync({ + from: video.uri, + to: newUri, + }); + + addPhoto({ + ...video, + id: filename, + uri: newUri, + jobId: cameraJobId, + video: true, + }); } }; if (hasPermission === null) { return ; } + if (hasPermission === false) { return No access to camera; } + + const { hasCameraPermission, flashMode, cameraType, capturing } = state; + return ( - + {cameraJobId} - { + navigation.push("MediaCache"); + }} style={{ - flex: 1, - flexDirection: "row", - justifyContent: "space-between", - margin: 20, + alignSelf: "flex-start", + alignItems: "center", + fontSize: 20, + fontWeight: "bold", }} > - - - - - - - { - navigation.push("MediaCache"); - }} - style={{ - alignSelf: "flex-end", - alignItems: "center", - backgroundColor: "transparent", - }} - > - - - + + + + diff --git a/components/screen-media-cache/screen-media-cache.component.jsx b/components/screen-media-cache/screen-media-cache.component.jsx index 5d0056e..3d2f827 100644 --- a/components/screen-media-cache/screen-media-cache.component.jsx +++ b/components/screen-media-cache/screen-media-cache.component.jsx @@ -1,11 +1,11 @@ import { Button, Text as NBText, Thumbnail, View } from "native-base"; import React from "react"; -import { SafeAreaView, Text } from "react-native"; - +import { FlatList, SafeAreaView, Text } from "react-native"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; import { removeAllPhotos } from "../../redux/photos/photos.actions"; import { selectPhotos } from "../../redux/photos/photos.selectors"; + const mapStateToProps = createStructuredSelector({ photos: selectPhotos, }); @@ -22,22 +22,19 @@ export function ScreenMediaCache({ photos, removeAllPhotos }) { Delete all {photos.length} - - The View - {photos.map((i, idx) => ( + + item.id} + renderItem={(object) => ( - {i.uri} - + {object.item.uri} + - ))} - + )} + //ItemSeparatorComponent={FlatListItemSeparator} + /> ); } diff --git a/package.json b/package.json index 87d56eb..fa388fd 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "react-dom": "16.13.1", "react-i18next": "^11.7.3", "react-native": "https://github.com/expo/react-native/archive/sdk-39.0.3.tar.gz", + "react-native-easy-grid": "^0.2.2", "react-native-gesture-handler": "~1.7.0", "react-native-indicators": "^0.17.0", "react-native-reanimated": "~1.13.0", diff --git a/redux/photos/photos.sagas.js b/redux/photos/photos.sagas.js index eb17913..e6e6a5d 100644 --- a/redux/photos/photos.sagas.js +++ b/redux/photos/photos.sagas.js @@ -1,5 +1,6 @@ import { all, call, takeLatest } from "redux-saga/effects"; import PhotosActionTypes from "./photos.types"; +import * as FileSystem from "expo-file-system"; export function* onRemoveAllPhotos() { yield takeLatest(PhotosActionTypes.REMOVE_ALL_PHOTOS, removeAllPhotosAction);