@plusscommunities/pluss-maintenance-app 6.1.1-beta.0 → 7.0.0-auth.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/dist/module/actions/JobActions.js +44 -1
  2. package/dist/module/actions/JobActions.js.map +1 -1
  3. package/dist/module/actions/types.js +3 -0
  4. package/dist/module/actions/types.js.map +1 -1
  5. package/dist/module/apis/index.js +3 -1
  6. package/dist/module/apis/index.js.map +1 -1
  7. package/dist/module/apis/{generalActions.js → maintenanceActions.js} +44 -45
  8. package/dist/module/apis/maintenanceActions.js.map +1 -0
  9. package/dist/module/components/FilterPopupMenu.js +78 -26
  10. package/dist/module/components/FilterPopupMenu.js.map +1 -1
  11. package/dist/module/components/MaintenanceList.js +70 -45
  12. package/dist/module/components/MaintenanceList.js.map +1 -1
  13. package/dist/module/components/MaintenanceListItem.js +54 -42
  14. package/dist/module/components/MaintenanceListItem.js.map +1 -1
  15. package/dist/module/components/MaintenanceWidgetItem.js +16 -20
  16. package/dist/module/components/MaintenanceWidgetItem.js.map +1 -1
  17. package/dist/module/components/PrioritySelectorPopup.js +82 -0
  18. package/dist/module/components/PrioritySelectorPopup.js.map +1 -0
  19. package/dist/module/components/StatusSelectorPopup.js +9 -13
  20. package/dist/module/components/StatusSelectorPopup.js.map +1 -1
  21. package/dist/module/components/WidgetSmall.js +14 -10
  22. package/dist/module/components/WidgetSmall.js.map +1 -1
  23. package/dist/module/feature.config.js +3 -3
  24. package/dist/module/feature.config.js.map +1 -1
  25. package/dist/module/helper.js +39 -17
  26. package/dist/module/helper.js.map +1 -1
  27. package/dist/module/reducers/JobsReducer.js +33 -3
  28. package/dist/module/reducers/JobsReducer.js.map +1 -1
  29. package/dist/module/screens/JobTypePicker.js +3 -3
  30. package/dist/module/screens/JobTypePicker.js.map +1 -1
  31. package/dist/module/screens/MaintenancePage.js +2 -2
  32. package/dist/module/screens/RequestDetail.js +340 -88
  33. package/dist/module/screens/RequestDetail.js.map +1 -1
  34. package/dist/module/screens/RequestNotes.js +437 -26
  35. package/dist/module/screens/RequestNotes.js.map +1 -1
  36. package/dist/module/screens/ServiceRequest.js +596 -93
  37. package/dist/module/screens/ServiceRequest.js.map +1 -1
  38. package/dist/module/values.config.a.js +9 -1
  39. package/dist/module/values.config.a.js.map +1 -1
  40. package/dist/module/values.config.default.js +15 -1
  41. package/dist/module/values.config.default.js.map +1 -1
  42. package/dist/module/values.config.forms.js +42 -0
  43. package/dist/module/values.config.forms.js.map +1 -0
  44. package/dist/module/values.config.js +15 -1
  45. package/dist/module/values.config.js.map +1 -1
  46. package/package.json +16 -12
  47. package/src/actions/JobActions.js +53 -1
  48. package/src/actions/types.js +4 -0
  49. package/src/apis/index.js +5 -1
  50. package/src/apis/{generalActions.js → maintenanceActions.js} +51 -43
  51. package/src/components/FilterPopupMenu.js +75 -24
  52. package/src/components/MaintenanceList.js +64 -33
  53. package/src/components/MaintenanceListItem.js +53 -31
  54. package/src/components/MaintenanceWidgetItem.js +18 -14
  55. package/src/components/PrioritySelectorPopup.js +79 -0
  56. package/src/components/StatusSelectorPopup.js +8 -13
  57. package/src/components/WidgetSmall.js +10 -8
  58. package/src/feature.config.js +15 -13
  59. package/src/helper.js +51 -13
  60. package/src/reducers/JobsReducer.js +27 -2
  61. package/src/screens/JobTypePicker.js +1 -1
  62. package/src/screens/RequestDetail.js +324 -75
  63. package/src/screens/RequestNotes.js +434 -33
  64. package/src/screens/ServiceRequest.js +642 -157
  65. package/src/values.config.a.js +8 -0
  66. package/src/values.config.default.js +14 -0
  67. package/src/values.config.forms.js +42 -0
  68. package/src/values.config.js +14 -0
  69. package/dist/module/apis/generalActions.js.map +0 -1
  70. package/dist/module/values.config.b.js +0 -28
  71. package/dist/module/values.config.b.js.map +0 -1
  72. package/dist/module/values.config.c.js +0 -28
  73. package/dist/module/values.config.c.js.map +0 -1
  74. package/dist/module/values.config.d.js +0 -28
  75. package/dist/module/values.config.d.js.map +0 -1
  76. package/src/values.config.b.js +0 -28
  77. package/src/values.config.c.js +0 -28
  78. package/src/values.config.d.js +0 -28
@@ -1,16 +1,31 @@
1
1
  import React, { Component } from 'react';
2
- import { ScrollView, View, Text, TouchableOpacity, Modal, KeyboardAvoidingView } from 'react-native';
2
+ import {
3
+ ScrollView,
4
+ View,
5
+ Text,
6
+ TouchableOpacity,
7
+ Modal,
8
+ KeyboardAvoidingView,
9
+ Platform,
10
+ FlatList,
11
+ Dimensions,
12
+ ImageBackground,
13
+ Keyboard,
14
+ } from 'react-native';
3
15
  import { connect } from 'react-redux';
4
- import { Icon } from 'react-native-elements';
16
+ import { Icon } from '@rneui/themed';
5
17
  import _ from 'lodash';
6
18
  import moment from 'moment';
7
19
  import { jobAdded } from '../actions';
8
- import { generalActions } from '../apis';
20
+ import { maintenanceActions } from '../apis';
9
21
  import { getBottomSpace } from 'react-native-iphone-x-helper';
10
22
  import { Services } from '../feature.config';
11
23
  import { Components, Colours, Helper } from '../core.config';
12
24
  import { values } from '../values.config';
13
25
 
26
+ const SCREEN_WIDTH = Dimensions.get('window').width;
27
+ const PHOTO_SIZE = (SCREEN_WIDTH - 64) / 3;
28
+
14
29
  class RequestNotes extends Component {
15
30
  constructor(props) {
16
31
  super(props);
@@ -21,7 +36,13 @@ class RequestNotes extends Component {
21
36
  noteAttachments: [],
22
37
  noteInput: '',
23
38
  addNoteOpen: false,
39
+ noteImages: [{ add: true }],
40
+ uploadingImage: false,
41
+ galleryOpen: false,
42
+ galleryImages: [],
43
+ galleryIndex: 0,
24
44
  };
45
+ this.checkThumb = null;
25
46
  }
26
47
 
27
48
  componentDidMount() {
@@ -34,6 +55,10 @@ class RequestNotes extends Component {
34
55
  }
35
56
  }
36
57
 
58
+ componentWillUnmount() {
59
+ clearInterval(this.checkThumb);
60
+ }
61
+
37
62
  updateJobState() {
38
63
  const job = _.find(this.props.jobs, j => j.id === this.props.job.id) || this.props.job;
39
64
  this.setState({ job });
@@ -62,13 +87,14 @@ class RequestNotes extends Component {
62
87
  };
63
88
 
64
89
  onPressConfirmDelete = () => {
65
- generalActions.deleteNote(this.props.job.id, this.state.noteToDelete.Id);
90
+ maintenanceActions.deleteNote(this.props.job.id, this.state.noteToDelete.Id);
66
91
  const newNotes = _.filter(this.state.job.Notes, note => {
67
92
  return note.Id !== this.state.noteToDelete.Id;
68
93
  });
69
94
  const newJob = { ...this.props.job };
70
95
  newJob.Notes = newNotes;
71
96
  this.props.jobAdded(newJob);
97
+ this.props.onChange && this.props.onChange();
72
98
  this.setState({
73
99
  job: newJob,
74
100
  noteToDelete: null,
@@ -80,14 +106,14 @@ class RequestNotes extends Component {
80
106
  };
81
107
 
82
108
  isReadyToSaveNote = () => {
83
- if (
84
- _.some(this.state.noteAttachments, n => {
85
- return n.Uploading;
86
- })
87
- ) {
88
- return false;
89
- }
90
- return !_.isEmpty(this.state.noteInput) || !_.isEmpty(this.state.noteAttachments);
109
+ const uploadingAttachments = _.some(this.state.noteAttachments, n => n.Uploading);
110
+ const uploadingImages = _.some(this.state.noteImages, n => n.uploading);
111
+ if (uploadingAttachments || uploadingImages) return false;
112
+ return (
113
+ !_.isEmpty(this.state.noteInput) ||
114
+ !_.isEmpty(this.state.noteAttachments) ||
115
+ !_.isEmpty(this.state.noteImages.filter(img => !img.add))
116
+ );
91
117
  };
92
118
 
93
119
  openAddNote = () => {
@@ -105,13 +131,16 @@ class RequestNotes extends Component {
105
131
  if (!!this.state.editingNote) {
106
132
  newState.noteInput = '';
107
133
  newState.noteAttachments = [];
134
+ newState.noteImages = [];
108
135
  }
109
136
  this.setState(newState);
110
137
  };
111
138
 
112
139
  openEditNote = n => {
140
+ const noteImages = [...(n.Images || []).map(i => ({ url: i, thumbNailUrl: Helper.getThumb300(i) })), { add: true }];
113
141
  this.setState({
114
142
  noteAttachments: n.Attachments || [],
143
+ noteImages,
115
144
  noteInput: n.Note || '',
116
145
  addNoteOpen: true,
117
146
  editingNote: n.Id,
@@ -126,28 +155,18 @@ class RequestNotes extends Component {
126
155
 
127
156
  try {
128
157
  this.setState({ submittingNote: true });
158
+ const attachments = this.state.noteAttachments
159
+ .filter(a => !a.add)
160
+ .map(a => {
161
+ return {
162
+ Title: a.Title,
163
+ Source: a.Source,
164
+ };
165
+ });
166
+ const images = this.state.noteImages.filter(img => !img.add).map(img => img.url);
129
167
  const res = await (this.state.editingNote
130
- ? generalActions.editNote(
131
- this.props.job.id,
132
- this.state.editingNote,
133
- this.state.noteInput,
134
- this.state.noteAttachments.map(a => {
135
- return {
136
- Title: a.Title,
137
- Source: a.Source,
138
- };
139
- }),
140
- )
141
- : generalActions.addNote(
142
- this.props.job.id,
143
- this.state.noteInput,
144
- this.state.noteAttachments.map(a => {
145
- return {
146
- Title: a.Title,
147
- Source: a.Source,
148
- };
149
- }),
150
- ));
168
+ ? maintenanceActions.editNote(this.props.job.id, this.state.editingNote, this.state.noteInput, attachments, images)
169
+ : maintenanceActions.addNote(this.props.job.id, this.state.noteInput, attachments, images));
151
170
  this.setState(
152
171
  {
153
172
  job: res.data.job,
@@ -155,10 +174,13 @@ class RequestNotes extends Component {
155
174
  addNoteOpen: false,
156
175
  noteInput: '',
157
176
  noteAttachments: [],
177
+ noteImages: [{ add: true }],
178
+ uploadingImage: false,
158
179
  editingNote: null,
159
180
  },
160
181
  () => {
161
182
  this.props.jobAdded(res.data.job);
183
+ this.props.onChange && this.props.onChange();
162
184
  },
163
185
  );
164
186
  } catch (err) {
@@ -166,10 +188,156 @@ class RequestNotes extends Component {
166
188
  console.log(err);
167
189
  this.setState({
168
190
  submittingNote: false,
191
+ uploadingImage: false,
169
192
  });
170
193
  }
171
194
  };
172
195
 
196
+ setImages = (noteImages, uploadingImage = false, callback = null) => {
197
+ this.setState({ noteImages, uploadingImage }, callback);
198
+ };
199
+
200
+ getImages = () => {
201
+ const { noteImages } = this.state;
202
+ const imagesList = _.cloneDeep(noteImages);
203
+ if (!imagesList || !Array.isArray(imagesList) || imagesList.length === 0) {
204
+ return [{ add: true }];
205
+ }
206
+ return imagesList;
207
+ };
208
+
209
+ getImageUrls = () => {
210
+ const imagesList = this.getImages();
211
+ return _.filter(imagesList, img => {
212
+ return !img.uploading && !img.add;
213
+ }).map(img => {
214
+ return img.url;
215
+ });
216
+ };
217
+
218
+ waitForThumbnails = () => {
219
+ if (this.checkThumb) return;
220
+
221
+ this.checkThumb = setInterval(async () => {
222
+ const imagesList = this.getImages();
223
+ const imagesUpdate = [];
224
+ await Promise.all(
225
+ imagesList.map(image => {
226
+ return new Promise(async resolve => {
227
+ const newImage = { ...image };
228
+ imagesUpdate.push(newImage);
229
+ if (newImage.url && !newImage.thumbNailExists) {
230
+ newImage.uploading = false;
231
+ newImage.allowRetry = false;
232
+ newImage.thumbNailExists = await Helper.imageExists(newImage.thumbNailUrl);
233
+ resolve(newImage.thumbNailExists);
234
+ }
235
+ resolve(true);
236
+ });
237
+ }),
238
+ );
239
+ const thumbnailsExist = imagesUpdate.every(image => !image.url || image.thumbNailExists);
240
+ if (thumbnailsExist) {
241
+ clearInterval(this.checkThumb);
242
+ this.checkThumb = null;
243
+ this.setImages(imagesUpdate);
244
+ }
245
+ }, 2000);
246
+ };
247
+
248
+ removeImage = index => {
249
+ const imagesUpdate = this.getImages();
250
+ imagesUpdate.splice(index, 1);
251
+
252
+ this.setImages(imagesUpdate);
253
+ };
254
+
255
+ showUploadMenu = () => {
256
+ Keyboard.dismiss();
257
+ const { uploadingImage, submittingNote } = this.state;
258
+ if (uploadingImage || submittingNote) return;
259
+ this.imageUploader.showUploadMenu();
260
+ };
261
+
262
+ toggleFullscreenVideo = url => {
263
+ if (typeof url !== 'string') url = '';
264
+ this.setState({ showFullscreenVideo: url.length > 0, currentVideoUrl: url });
265
+ };
266
+
267
+ openGallery(images, index) {
268
+ this.setState({ galleryOpen: true, galleryImages: images, galleryIndex: index });
269
+ }
270
+
271
+ closeGallery() {
272
+ this.setState({ galleryOpen: false, galleryImages: [], galleryIndex: 0 });
273
+ }
274
+
275
+ onUploadStarted = (uploadUri, imageUri) => {
276
+ const imagesUpdate = this.getImages();
277
+ imagesUpdate.splice(imagesUpdate.length - 1, 0, {
278
+ uploading: true,
279
+ uploadProgress: '0%',
280
+ uploadUri,
281
+ imageUri,
282
+ allowRetry: true,
283
+ });
284
+
285
+ this.setImages(imagesUpdate, true);
286
+ };
287
+
288
+ onUploadProgress = progress => {
289
+ const imagesUpdate = this.getImages();
290
+ imagesUpdate.map(img => {
291
+ if (img.uploadUri === progress.uri) {
292
+ img.uploadProgress = progress.percentage;
293
+ img.uploading = true;
294
+ img.allowRetry = true;
295
+ }
296
+ });
297
+
298
+ this.setImages(imagesUpdate, true);
299
+ };
300
+
301
+ onUploadSuccess = async (uri, uploadUri) => {
302
+ const imagesUpdate = this.getImages();
303
+ imagesUpdate.map(img => {
304
+ if (img.uploadUri === uploadUri && img.uploading) {
305
+ img.url = uri.replace('/general/', '/general1400/');
306
+ img.thumbNailExists = false;
307
+ img.thumbNailUrl = Helper.getThumb300(img.url);
308
+ img.allowRetry = true;
309
+ }
310
+ });
311
+
312
+ this.setImages(imagesUpdate, false, () => this.waitForThumbnails());
313
+ };
314
+
315
+ onUploadFailed = uploadUri => {
316
+ const imagesUpdate = this.getImages();
317
+ imagesUpdate.map(img => {
318
+ if (img.uploadUri === uploadUri) {
319
+ img.uploading = true; // Requried for retry
320
+ img.uploadProgress = '';
321
+ img.allowRetry = true;
322
+ }
323
+ });
324
+
325
+ this.setImages(imagesUpdate);
326
+ };
327
+
328
+ onLibrarySelected = uri => {
329
+ const imagesUpdate = this.getImages();
330
+ imagesUpdate.splice(imagesUpdate.length - 1, 0, {
331
+ uploading: false,
332
+ allowRetry: false,
333
+ url: Helper.get1400(uri),
334
+ thumbNailExists: true,
335
+ thumbNailUrl: Helper.getThumb300(uri),
336
+ });
337
+
338
+ this.setImages(imagesUpdate);
339
+ };
340
+
173
341
  renderAttachment(a, i) {
174
342
  return (
175
343
  <Components.Attachment
@@ -182,6 +350,50 @@ class RequestNotes extends Component {
182
350
  );
183
351
  }
184
352
 
353
+ renderPlayableImageUrl(images, index, containerStyle) {
354
+ const url = images[index];
355
+ const thumbUrl = Helper.getThumb300(url);
356
+
357
+ if (Helper.isVideo(url)) {
358
+ return (
359
+ <ImageBackground style={[{ flex: 1 }, containerStyle]} source={{ uri: thumbUrl }}>
360
+ <View style={styles.imagePlayContainer}>
361
+ <TouchableOpacity onPress={this.toggleFullscreenVideo.bind(this, url)}>
362
+ <Icon name="play" type="font-awesome" iconStyle={styles.imageControlIcon} />
363
+ </TouchableOpacity>
364
+ </View>
365
+ </ImageBackground>
366
+ );
367
+ }
368
+
369
+ const imageUrl = Helper.get1400(url);
370
+ return (
371
+ <TouchableOpacity style={containerStyle} onPress={this.openGallery.bind(this, images, index || 0)}>
372
+ <ImageBackground resizeMode="cover" style={styles.imageContainer} source={{ uri: imageUrl }} />
373
+ </TouchableOpacity>
374
+ );
375
+ }
376
+
377
+ renderImages(note) {
378
+ if (!note) return null;
379
+ if (!_.isNil(note.Images) && !_.isEmpty(note.Images)) {
380
+ return (
381
+ <ScrollView horizontal style={styles.sideBySideImages}>
382
+ {note.Images.map((_p, index) => this.renderPlayableImageUrl(note.Images, index, styles.sideBySideImageContainer))}
383
+ </ScrollView>
384
+ );
385
+ }
386
+ return null;
387
+ }
388
+
389
+ renderImagePopup() {
390
+ const { galleryOpen, galleryImages, galleryIndex } = this.state;
391
+ console.log('renderimagepopup', { galleryOpen, galleryImages, galleryIndex });
392
+ return (
393
+ <Components.ImagePopup visible={galleryOpen} images={galleryImages} index={galleryIndex} onClose={this.closeGallery.bind(this)} />
394
+ );
395
+ }
396
+
185
397
  renderNote(n) {
186
398
  return (
187
399
  <View style={styles.noteContainer} key={n.Id}>
@@ -217,6 +429,7 @@ class RequestNotes extends Component {
217
429
  </View>
218
430
  <Text style={styles.noteContainerText}>{n.Note}</Text>
219
431
  {(n.Attachments || []).map((a, i) => this.renderAttachment(a, i))}
432
+ {this.renderImages(n)}
220
433
  <Text style={styles.noteTimestamp}>
221
434
  {moment
222
435
  .utc(n.Timestamp)
@@ -270,10 +483,118 @@ class RequestNotes extends Component {
270
483
  );
271
484
  }
272
485
 
486
+ renderUploadMenu() {
487
+ return (
488
+ <Components.ImageUploader
489
+ ref={ref => (this.imageUploader = ref)}
490
+ onUploadStarted={this.onUploadStarted}
491
+ onUploadSuccess={this.onUploadSuccess}
492
+ onUploadFailed={this.onUploadFailed}
493
+ onUploadProgress={this.onUploadProgress}
494
+ onLibrarySelected={this.onLibrarySelected}
495
+ size={{ width: 1400 }}
496
+ quality={0.8}
497
+ fileName={'serviceNoteImage'}
498
+ popupTitle={'Upload Image'}
499
+ userId={this.props.user.uid}
500
+ allowsEditing={false}
501
+ multiple
502
+ hideLibrary
503
+ />
504
+ );
505
+ }
506
+
507
+ renderImage(item, index) {
508
+ const isVideoUrl = Helper.isVideo(item.url);
509
+ const imagesList = this.getImages();
510
+
511
+ if (item.add) {
512
+ return (
513
+ <TouchableOpacity activeOpacity={0.8} onPress={() => this.showUploadMenu()}>
514
+ <View
515
+ style={[
516
+ styles.imageContainer,
517
+ imagesList.length > 1 && styles.imageContainerNotEmpty,
518
+ index % 3 === 0 && { marginLeft: 0 },
519
+ index > 2 && { marginTop: 8 },
520
+ ]}
521
+ >
522
+ <View style={styles.imageCircle}>
523
+ <Icon name="camera" type="font-awesome" iconStyle={styles.addImageIcon} />
524
+ </View>
525
+ </View>
526
+ </TouchableOpacity>
527
+ );
528
+ }
529
+ return (
530
+ <View
531
+ style={[
532
+ styles.imageContainer,
533
+ imagesList.length > 1 && styles.imageContainerNotEmpty,
534
+ index % 3 === 0 && { marginLeft: 0 },
535
+ index > 2 && { marginTop: 8 },
536
+ ]}
537
+ >
538
+ {item.uploading ? (
539
+ <Components.ImageUploadProgress uploader={this.imageUploader} image={item} color={this.props.colourBrandingMain} />
540
+ ) : (
541
+ <ImageBackground
542
+ style={styles.imageBackground}
543
+ source={Helper.getImageSource(item.thumbNailExists ? item.thumbNailUrl : item.url || item)}
544
+ >
545
+ {isVideoUrl && (
546
+ <View style={styles.imagePlayContainer}>
547
+ <TouchableOpacity onPress={this.toggleFullscreenVideo.bind(this, item.url)}>
548
+ <Icon name="play" type="font-awesome" iconStyle={styles.imageControlIcon} />
549
+ </TouchableOpacity>
550
+ </View>
551
+ )}
552
+ <TouchableOpacity style={styles.removeImage} onPress={() => this.removeImage(index)}>
553
+ <Icon name="remove" type="font-awesome" iconStyle={styles.imageControlIcon} style={styles.removeImage} />
554
+ </TouchableOpacity>
555
+ </ImageBackground>
556
+ )}
557
+ </View>
558
+ );
559
+ }
560
+
561
+ renderImageList() {
562
+ const imagesList = this.getImages();
563
+
564
+ return (
565
+ <View style={styles.imageListContainer}>
566
+ <FlatList
567
+ keyboardShouldPersistTaps="always"
568
+ enableEmptySections
569
+ data={imagesList}
570
+ renderItem={({ item, index }) => this.renderImage(item, index)}
571
+ keyExtractor={(item, index) => index}
572
+ numColumns={3}
573
+ scrollEnabled={false}
574
+ />
575
+ </View>
576
+ );
577
+ }
578
+
579
+ renderVideoPlayerPopup() {
580
+ const { showFullscreenVideo, currentVideoUrl } = this.state;
581
+ if (!currentVideoUrl) return;
582
+
583
+ return (
584
+ <Components.VideoPopup
585
+ uri={currentVideoUrl}
586
+ visible={showFullscreenVideo}
587
+ showFullscreenButton={false}
588
+ onClose={this.toggleFullscreenVideo}
589
+ />
590
+ );
591
+ }
592
+
273
593
  renderAdd() {
274
594
  return (
275
595
  <Modal animationType="slide" visible={this.state.addNoteOpen}>
276
596
  <KeyboardAvoidingView style={styles.container} behavior={Platform.OS === 'ios' && 'padding'}>
597
+ {this.renderUploadMenu()}
277
598
  <Components.Header leftText="Cancel" onPressLeft={this.closeAddNote} text="" />
278
599
  <ScrollView style={styles.scrollContainer} contentContainerStyle={styles.innerContainer}>
279
600
  <Components.TextStyle type="pageHeading">Add Staff Note</Components.TextStyle>
@@ -295,6 +616,7 @@ class RequestNotes extends Component {
295
616
  sectionStyle={styles.inputSection}
296
617
  />
297
618
  <Components.GenericInputSection label="Attachments" sectionStyle={styles.inputSection}>
619
+ {this.renderImageList()}
298
620
  {this.state.editingNote ? (
299
621
  (this.state.noteAttachments || []).map((a, i) => this.renderAttachment(a, i))
300
622
  ) : (
@@ -305,6 +627,7 @@ class RequestNotes extends Component {
305
627
  </Components.GenericInputSection>
306
628
  </ScrollView>
307
629
  <View style={styles.popupFooter}>{this.renderFooterContent()}</View>
630
+ {this.renderVideoPlayerPopup()}
308
631
  </KeyboardAvoidingView>
309
632
  </Modal>
310
633
  );
@@ -321,6 +644,7 @@ class RequestNotes extends Component {
321
644
  {this.renderNotes()}
322
645
  </ScrollView>
323
646
  <Components.AddButton onPress={this.openAddNote} />
647
+ {this.renderImagePopup()}
324
648
  </View>
325
649
  );
326
650
  }
@@ -402,6 +726,83 @@ const styles = {
402
726
  attachmentInfo: {
403
727
  marginTop: 8,
404
728
  },
729
+ imageListContainer: {
730
+ marginVertical: 8,
731
+ },
732
+ imageContainer: {
733
+ width: PHOTO_SIZE,
734
+ height: PHOTO_SIZE,
735
+ borderStyle: 'dashed',
736
+ justifyContent: 'center',
737
+ alignItems: 'center',
738
+ borderWidth: 1,
739
+ borderColor: Colours.LINEGREY,
740
+ backgroundColor: Colours.BOXGREY,
741
+ borderRadius: 4,
742
+ marginLeft: 8,
743
+ },
744
+ imageContainerNotEmpty: {
745
+ borderWidth: 1,
746
+ borderColor: Colours.LINEGREY,
747
+ backgroundColor: '#fff',
748
+ borderStyle: 'dashed',
749
+ },
750
+ imageBackground: {
751
+ flex: 1,
752
+ height: '100%',
753
+ width: '100%',
754
+ borderRadius: 4,
755
+ },
756
+ imageCircle: {
757
+ width: 90,
758
+ height: 90,
759
+ borderRadius: 45,
760
+ backgroundColor: Colours.PINKISH_GREY,
761
+ justifyContent: 'center',
762
+ },
763
+ addImageIcon: {
764
+ color: '#fff',
765
+ fontSize: 32,
766
+ },
767
+ imagePlayContainer: {
768
+ position: 'absolute',
769
+ top: 0,
770
+ left: 0,
771
+ right: 0,
772
+ bottom: 0,
773
+ alignItems: 'center',
774
+ justifyContent: 'center',
775
+ },
776
+ imageControlIcon: {
777
+ color: '#fff',
778
+ fontSize: 20,
779
+ textShadowColor: 'rgba(0,0,0,0.3)',
780
+ textShadowOffset: { width: 2, height: 2 },
781
+ },
782
+ removeImage: {
783
+ position: 'absolute',
784
+ top: 0,
785
+ right: 0,
786
+ padding: 4,
787
+ width: 40,
788
+ height: 40,
789
+ alignItems: 'center',
790
+ justifyContent: 'center',
791
+ },
792
+ sideBySideImages: {
793
+ flex: 1,
794
+ flexDirection: 'row',
795
+ marginTop: 12,
796
+ marginHorizontal: -8,
797
+ },
798
+ sideBySideImageContainer: {
799
+ backgroundColor: '#fff',
800
+ width: 60,
801
+ height: 50,
802
+ borderRadius: 2,
803
+ overflow: 'hidden',
804
+ marginRight: 2,
805
+ },
405
806
  };
406
807
 
407
808
  const mapStateToProps = state => {