diff --git a/mobile/src/App.js b/mobile/src/App.js
index 44a1066..ff9a8b8 100644
--- a/mobile/src/App.js
+++ b/mobile/src/App.js
@@ -65,7 +65,7 @@ export default class App extends React.Component {
path="/workItemList"
component={WorkItemList}
/>
-
+
diff --git a/mobile/src/Auth/DefaultRoute.js b/mobile/src/Auth/DefaultRoute.js
index 43027b5..469e25c 100644
--- a/mobile/src/Auth/DefaultRoute.js
+++ b/mobile/src/Auth/DefaultRoute.js
@@ -1,7 +1,14 @@
import React, { Fragment, Component } from "react"
import { Route, Redirect } from "react-router-native"
+import PropTypes from "prop-types"
-export const DefaultRoute = () => {
- // NOTE: When working on the app, change this to the page you are working on
- return } />
+export class DefaultRoute extends Component {
+ static propTypes = {
+ redirect: PropTypes.string,
+ }
+
+ render() {
+ // NOTE: When working on the app, change this to the page you are working on
+ return } />
+ }
}
diff --git a/mobile/src/Modal/ProgressModal.js b/mobile/src/Modal/ProgressModal.js
new file mode 100644
index 0000000..585a34e
--- /dev/null
+++ b/mobile/src/Modal/ProgressModal.js
@@ -0,0 +1,56 @@
+import React, { Component } from "react"
+import Modal from "react-native-modal"
+import PropTypes from "prop-types"
+import { View, Text, ActivityIndicator, TouchableOpacity } from "react-native"
+import { ProgressBar } from "../ui"
+import autobind from "autobind-decorator"
+
+export class ProgressModal extends Component {
+ static propTypes = {
+ open: PropTypes.bool,
+ message: PropTypes.string.isRequired,
+ percent: PropTypes.number,
+ onCancel: PropTypes.func,
+ }
+
+ render() {
+ const { open, message, percent } = this.props
+
+ return (
+
+
+
+
+ {message}
+
+
+ Cancel
+
+
+
+ )
+ }
+}
diff --git a/mobile/src/Modal/WaitModal.js b/mobile/src/Modal/WaitModal.js
index ecfa387..856ebf8 100644
--- a/mobile/src/Modal/WaitModal.js
+++ b/mobile/src/Modal/WaitModal.js
@@ -2,6 +2,7 @@ import React, { Component } from "react"
import Modal from "react-native-modal"
import PropTypes from "prop-types"
import { View, Text, ActivityIndicator } from "react-native"
+import { BubbleLoader } from "../ui"
export class WaitModal extends Component {
static propTypes = {
@@ -18,9 +19,10 @@ export class WaitModal extends Component {
style={{
flexDirection: "column",
justifyContent: "center",
+ alignItems: "center",
backgroundColor: "transparent",
}}>
-
+
@@ -327,10 +357,17 @@ export class WorkItem extends React.Component {
binder={binder}
onUploadStarted={this.handleUploadStarted}
onUploadEnded={this.handleUploadEnded}
+ onUploadProgress={this.handleUploadProgress}
/>
{isIphoneX ? : null}
+
{
if (onUploadEnded) {
onUploadEnded(true, uploadData)
diff --git a/mobile/src/ui/BubbleLoader.js b/mobile/src/ui/BubbleLoader.js
new file mode 100644
index 0000000..3776080
--- /dev/null
+++ b/mobile/src/ui/BubbleLoader.js
@@ -0,0 +1,77 @@
+import React from "react"
+import { View, Animated, Easing } from "react-native"
+import PropTypes from "prop-types"
+
+export class BubbleLoader extends React.Component {
+ static propTypes = {
+ size: PropTypes.number,
+ }
+
+ static defaultProps = {
+ size: 50,
+ }
+
+ constructor(props) {
+ super(props)
+ this.animatedScales = [0, 1, 2].map((i) => new Animated.Value(1.0))
+
+ Animated.loop(
+ Animated.parallel(
+ this.animatedScales.map((value, i) =>
+ Animated.sequence([
+ Animated.timing(value, {
+ toValue: 0.2,
+ duration: 800,
+ delay: 200 * i,
+ easing: Easing.out(Easing.sin),
+ useNativeDriver: true,
+ }),
+ Animated.timing(value, {
+ toValue: 1.0,
+ duration: 800,
+ easing: Easing.out(Easing.sin),
+ useNativeDriver: true,
+ }),
+ Animated.delay(200 * (2 - i)),
+ ])
+ )
+ )
+ ).start()
+ }
+
+ render() {
+ const { size } = this.props
+ const spacing = size / 5
+
+ return (
+
+ {[0, 1, 2].map((i) => (
+
+ ))}
+
+ )
+ }
+}
diff --git a/mobile/src/ui/ProgressBar.js b/mobile/src/ui/ProgressBar.js
new file mode 100644
index 0000000..577a848
--- /dev/null
+++ b/mobile/src/ui/ProgressBar.js
@@ -0,0 +1,86 @@
+import React from "react"
+import {
+ AppRegistry,
+ StyleSheet,
+ Text,
+ View,
+ Easing,
+ Animated,
+} from "react-native"
+import PropTypes from "prop-types"
+
+var style = StyleSheet.create({
+ container: {
+ width: "100%",
+ flexDirection: "row",
+ overflow: "hidden",
+ borderWidth: 2,
+ borderColor: "#555555",
+ borderRadius: 8,
+ marginBottom: 5,
+ backgroundColor: "#FFFFFF",
+ },
+ bar: {
+ flexDirection: "row",
+ justifyContent: "flex-end",
+ alignItems: "center",
+ backgroundColor: "#3BB0FD",
+ },
+ value: {
+ position: "absolute",
+ right: 0,
+ paddingRight: 5,
+ backgroundColor: "#00000000",
+ color: "#FFFFFF",
+ },
+})
+
+export class ProgressBar extends React.Component {
+ static propInfo = {
+ height: PropTypes.number,
+ value: PropTypes.number,
+ }
+
+ static defaultProps = {
+ value: 0,
+ }
+
+ constructor(props) {
+ super(props)
+ this.state = {
+ animatedValue: new Animated.Value(props.value % 101),
+ }
+ }
+
+ componentWillReceiveProps(nextProps) {
+ if (this.props.value !== nextProps.value) {
+ Animated.timing(this.state.animatedValue, {
+ toValue: nextProps.value % 101,
+ easing: Easing.out(Easing.ease),
+ duration: 500,
+ }).start()
+ }
+ }
+
+ render() {
+ const { height } = this.props
+ const width = this.state.animatedValue.interpolate({
+ inputRange: [0, 100],
+ outputRange: ["0%", "100%"],
+ })
+
+ return (
+
+
+ {this.props.value}%
+
+
+ )
+ }
+}
diff --git a/mobile/src/ui/index.js b/mobile/src/ui/index.js
index 9805903..8e8c0dd 100644
--- a/mobile/src/ui/index.js
+++ b/mobile/src/ui/index.js
@@ -1,6 +1,8 @@
export { Icon } from "./Icon"
export { Header } from "./Header"
export { OptionStrip } from "./OptionStrip"
+export { BubbleLoader } from "./BubbleLoader"
+export { ProgressBar } from "./ProgressBar"
export { BoundSwitch } from "./BoundSwitch"
export { BoundInput } from "./BoundInput"
export { FormStaticInput } from "./FormStaticInput"
diff --git a/server/src/api/routes/AuthRoutes.js b/server/src/api/routes/AuthRoutes.js
index c7e8f4e..b082b37 100644
--- a/server/src/api/routes/AuthRoutes.js
+++ b/server/src/api/routes/AuthRoutes.js
@@ -15,6 +15,7 @@ export class AuthRoutes {
constructor(container) {
const app = container.app
+ this.log = container.log
this.mq = container.mq
this.db = container.db
this.maxEmailTokenAgeInHours = config.get("email.maxEmailTokenAgeInHours")