diff --git a/src/lib/Network.js b/src/lib/Network.js index 7ac7e4914fec2..45ee45e545417 100644 --- a/src/lib/Network.js +++ b/src/lib/Network.js @@ -34,7 +34,13 @@ function request(command, data, type = 'post') { console.info('[API] Error', responseData); }) // eslint-disable-next-line no-unused-vars - .catch(() => isAppOffline = true); + .catch(() => { + isAppOffline = true; + + // Throw a new error to prevent any other `then()` in the promise chain from being triggered (until another + // catch() happens + throw new Error('API is offline'); + }); } // Holds a queue of all the write requests that need to happen @@ -61,30 +67,35 @@ function delayedWrite(command, data) { /** * Process the write queue by looping through the queue and attempting to make the requests */ -// function processWriteQueue() { -// if (isAppOffline) { -// // Make a simple request to see if we're online again -// request('Get', null, 'get') -// .then(() => isAppOffline = false); -// return; -// } -// -// if (delayedWriteQueue.length === 0) { -// return; -// } -// -// _.each(delayedWriteQueue, (delayedWriteRequest) => { -// request(delayedWriteRequest.command, delayedWriteRequest.data) -// .then(delayedWriteRequest.callback) -// .catch(() => { -// // If the request failed, we need to put the request object back into the queue -// delayedWriteQueue.push(delayedWriteRequest); -// }); -// }); -// } +function processWriteQueue() { + if (isAppOffline) { + // Make a simple request to see if we're online again + request('Get', null) + .then(() => isAppOffline = false); + return; + } + + if (delayedWriteQueue.length === 0) { + return; + } + + for (let i = 0; i < delayedWriteQueue.length; i++) { + // Take the request object out of the queue and make the request + const delayedWriteRequest = delayedWriteQueue.shift(); + + request(delayedWriteRequest.command, delayedWriteRequest.data) + .then(delayedWriteRequest.callback) + .catch(() => { + // If the request failed, we need to put the request object back into the queue + delayedWriteQueue.push(delayedWriteRequest); + }); + } +} -// TODO: Figure out setInterval // Process our write queue very often -// setInterval(processWriteQueue, 1000); +setInterval(processWriteQueue, 1000); -export {request, delayedWrite}; +export { + request, + delayedWrite, +}; diff --git a/src/lib/Pusher/pusher.js b/src/lib/Pusher/pusher.js index d257fdf0b4e90..e09145bb98025 100644 --- a/src/lib/Pusher/pusher.js +++ b/src/lib/Pusher/pusher.js @@ -24,7 +24,8 @@ function init(appKey, params) { }); // If we want to pass params in our requests to api.php we'll need to add it to socket.config.auth.params - // as per the documentation (https://pusher.com/docs/channels/using_channels/connection#channels-options-parameter). + // as per the documentation + // (https://pusher.com/docs/channels/using_channels/connection#channels-options-parameter). // Any param mentioned here will show up in $_REQUEST when we call "Push_Authenticate". Params passed here need // to pass our inputRules to show up in the request. if (params) { @@ -92,8 +93,9 @@ function bindEventToChannel(channel, eventName, eventCallback = () => {}, isChun return; } - // If we are chunking the requests, we need to construct a rolling list of all packets that have come through Pusher. - // If we've completed one of these full packets, we'll combine the data and act on the event that it's assigned to. + // If we are chunking the requests, we need to construct a rolling list of all packets that have come through + // Pusher. If we've completed one of these full packets, we'll combine the data and act on the event that it's + // assigned to. // If we haven't seen this eventID yet, initialize it into our rolling list of packets. if (!chunkedDataEvents[eventData.id]) { @@ -109,13 +111,17 @@ function bindEventToChannel(channel, eventName, eventCallback = () => {}, isChun chunkedEvent.receivedFinal = true; } - // Only call the event callback if we've received the last packet and we don't have any holes in the complete packet. + // Only call the event callback if we've received the last packet and we don't have any holes in the complete + // packet. if (chunkedEvent.receivedFinal && chunkedEvent.chunks.length === Object.keys(chunkedEvent.chunks).length) { eventCallback(JSON.parse(chunkedEvent.chunks.join(''))); try { eventCallback(JSON.parse(chunkedEvent.chunks.join(''))); } catch (err) { - console.error('[Pusher] Unable to parse chunked JSON response from Pusher', 0, {error: err, eventData: chunkedEvent.chunks.join('')}); + console.error('[Pusher] Unable to parse chunked JSON response from Pusher', 0, { + error: err, + eventData: chunkedEvent.chunks.join('') + }); } delete chunkedDataEvents[eventData.id]; @@ -131,7 +137,8 @@ function bindEventToChannel(channel, eventName, eventCallback = () => {}, isChun * @param {String} channelName * @param {String} eventName * @param {Function} [eventCallback] - * @param {Boolean} [isChunked] This parameters tells us whether or not we expect the result to come in individual pieces/chunks (because it exceeds + * @param {Boolean} [isChunked] This parameters tells us whether or not we expect the result to come in individual + * pieces/chunks (because it exceeds * the 10kB limit that pusher has). * * @return {Promise} @@ -142,7 +149,8 @@ function subscribe(channelName, eventName, eventCallback = () => {}, isChunked = return new Promise((resolve, reject) => { // We cannot call subscribe() before init(). Prevent any attempt to do this on dev. if (!socket) { - throw new Error('[Pusher] instance not found. Pusher.subscribe() most likely has been called before Pusher.init()'); + throw new Error(`[Pusher] instance not found. Pusher.subscribe() + most likely has been called before Pusher.init()`); } console.debug('[Pusher] Attempting to subscribe to channel', true, {channelName, eventName}); @@ -157,7 +165,10 @@ function subscribe(channelName, eventName, eventCallback = () => {}, isChunked = channel.bind('pusher:subscription_error', (status) => { if (status === 403) { - console.debug('[Pusher] Issue authenticating with Pusher during subscribe attempt.', 0, {channelName, status}); + console.debug('[Pusher] Issue authenticating with Pusher during subscribe attempt.', 0, { + channelName, + status + }); } reject(status); @@ -200,7 +211,8 @@ function unsubscribe(channelName, eventName = '') { const channel = getChannel(channelName); if (!channel) { - console.debug('[Pusher] Attempted to unsubscribe or unbind from a channel, but Pusher-JS has no knowledge of it', 0, {channelName, eventName}); + console.debug(`[Pusher] Attempted to unsubscribe or unbind from a channel, + but Pusher-JS has no knowledge of it`, 0, {channelName, eventName}); return; } @@ -210,7 +222,8 @@ function unsubscribe(channelName, eventName = '') { } else { if (!channel.subscribed) { // eslint-disable-next-line no-console - console.warn('[Pusher] Attempted to unsubscribe from channel, but we are not subscribed to begin with', 0, {channelName}); + console.warn(`[Pusher] Attempted to unsubscribe from channel, + but we are not subscribed to begin with`, 0, {channelName}); return; } diff --git a/src/page/HomePage/Report/ReportHistoryCompose.js b/src/page/HomePage/Report/ReportHistoryCompose.js new file mode 100644 index 0000000000000..8233b357744e2 --- /dev/null +++ b/src/page/HomePage/Report/ReportHistoryCompose.js @@ -0,0 +1,102 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import {View, TextInput, Button} from 'react-native'; +import styles from '../../../style/StyleSheet'; + +const propTypes = { + // A method to call when the form is submitted + onSubmit: PropTypes.func.isRequired, +}; + +class ReportHistoryCompose extends React.Component { + constructor(props) { + super(props); + + this.updateComment = this.updateComment.bind(this); + this.submitForm = this.submitForm.bind(this); + this.triggerSubmitShortcut = this.triggerSubmitShortcut.bind(this); + + this.state = { + comment: '', + }; + } + + componentDidMount() { + this.textInput.focus(); + } + + componentDidUpdate() { + this.textInput.focus(); + } + + /** + * Update the value of the comment input in the state + * + * @param {string} newComment + */ + updateComment(newComment) { + this.setState({ + comment: newComment, + }); + } + + /** + * Listens for the keyboard shortcut and submits + * the form when we have enter + * + * @param {Object} e + */ + triggerSubmitShortcut(e) { + if (e && e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + this.submitForm(); + } + } + + /** + * Add a new comment to this chat + * + * @param {SyntheticEvent} [e] + */ + submitForm(e) { + if (e) { + e.preventDefault(); + } + + // Don't submit empty commentes + // @TODO show an error in the UI + if (!this.state.comment) { + return; + } + + this.props.onSubmit(this.state.comment); + this.setState({ + comment: '', + }); + } + + render() { + return ( + + this.textInput = el} + multiline + textAlignVertical + numberOfLines={3} + minHeight={60} + maxHeight={60} + onChangeText={this.updateComment} + onKeyPress={this.triggerSubmitShortcut} + style={[styles.textInput]} + value={this.state.comment} + /> + +