@plusscommunities/pluss-core-app 1.3.0 → 1.4.3-beta.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.
package/package.json CHANGED
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "name": "@plusscommunities/pluss-core-app",
3
- "version": "1.3.0",
3
+ "version": "1.4.3-beta.0",
4
4
  "description": "Core extension package for Pluss Communities platform",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
7
+ "prepatch": "npm version prepatch --preid=beta",
7
8
  "patch": "npm version patch",
9
+ "preupload": "npm publish --access public --tag beta && rm -rf node_modules",
10
+ "preupload:p": "npm run prepatch && npm run preupload",
8
11
  "upload": "npm publish --access public && rm -rf node_modules",
9
12
  "upload:p": "npm run patch && npm run upload"
10
13
  },
@@ -12,31 +15,33 @@
12
15
  "license": "ISC",
13
16
  "dependencies": {
14
17
  "@expo/vector-icons": "^12.0.0",
15
- "aws-amplify": "^3.4.0",
16
- "aws-amplify-react-native": "^4.3.3",
17
- "expo-av": "~9.1.2",
18
- "expo-constants": "~10.1.3",
19
- "expo-file-system": "~11.0.2",
20
- "expo-image-manipulator": "~9.1.0",
21
- "expo-image-picker": "~10.1.4",
22
- "expo-linear-gradient": "~9.1.0",
23
- "expo-media-library": "~12.0.2",
24
- "expo-permissions": "~12.0.1",
25
- "expo-screen-orientation": "~3.1.0",
26
- "expo-sharing": "~9.1.2",
18
+ "@react-native-async-storage/async-storage": "~1.15.0",
19
+ "@react-native-community/netinfo": "7.1.3",
20
+ "@react-native-picker/picker": "2.2.1",
21
+ "aws-amplify": "^4.3.11",
22
+ "aws-amplify-react-native": "^6.0.2",
23
+ "expo-av": "~10.2.0",
24
+ "expo-constants": "~13.0.0",
25
+ "expo-file-system": "~13.1.0",
26
+ "expo-image-manipulator": "~10.2.0",
27
+ "expo-image-picker": "~12.0.1",
28
+ "expo-linear-gradient": "~11.0.0",
29
+ "expo-media-library": "~14.0.0",
30
+ "expo-screen-orientation": "~4.1.1",
31
+ "expo-sharing": "~10.1.0",
27
32
  "expo-video-player": "^1.6.0",
28
33
  "js-cookie": "^2.2.1",
29
34
  "lodash": "^4.17.4",
30
35
  "mime-types": "^2.1.24",
31
36
  "moment": "^2.18.1",
32
- "react": "16.13.1",
33
- "react-native": "https://github.com/expo/react-native/archive/sdk-41.0.0.tar.gz",
37
+ "react": "17.0.1",
38
+ "react-native": "0.64.3",
34
39
  "react-native-auto-height-image": "3.1.3",
35
40
  "react-native-elements": "^0.17.0",
36
41
  "react-native-image-zoom-viewer": "^3.0.1",
37
42
  "react-native-iphone-x-helper": "^1.3.1",
38
43
  "react-native-vimeo-iframe": "^1.0.4",
39
- "react-native-webview": "11.2.3",
44
+ "react-native-webview": "11.15.0",
40
45
  "react-native-youtube-iframe": "^2.2.1",
41
46
  "react-redux": "^5.0.5"
42
47
  },
package/src/apis/index.js CHANGED
@@ -5,4 +5,6 @@ export * from './contactActions';
5
5
  export * from './eventActions';
6
6
  export * from './analyticsActions';
7
7
  export * from './notificationActions';
8
+ export * from './typeActions';
8
9
  export { default as userActions } from './userActions';
10
+ export { default as profileActions } from './profileActions';
@@ -0,0 +1,13 @@
1
+ import { getUrl } from '../helper';
2
+ import { authedFunction } from '../session';
3
+
4
+ const profileActions = {
5
+ getUserTagsBySite: site => {
6
+ return authedFunction({
7
+ method: 'GET',
8
+ url: getUrl('profile', 'usertags/site', { site }),
9
+ });
10
+ },
11
+ };
12
+
13
+ export default profileActions;
@@ -0,0 +1,13 @@
1
+ import { getUrl } from '../helper';
2
+ import { authedFunction } from '../session';
3
+
4
+ export const typeActions = {
5
+ getUserTypes: site => {
6
+ const url = getUrl('types', 'getusertypes');
7
+ return authedFunction({
8
+ method: 'POST',
9
+ url,
10
+ data: { site },
11
+ });
12
+ },
13
+ };
@@ -0,0 +1,61 @@
1
+ import React, { PureComponent } from 'react';
2
+ import { StyleSheet } from 'react-native';
3
+ import { connect } from 'react-redux';
4
+ import { FormCardSectionOptionLauncher } from './FormCardSectionOptionLauncher';
5
+ import { getMainBrandingColourFromState, TEXT_DARK } from '../colours';
6
+ import { Services } from '../config';
7
+
8
+ class AudienceSelectorLauncher extends PureComponent {
9
+ onPressAudience = () => {
10
+ const { user, audienceType, audienceTypeSelection } = this.props;
11
+
12
+ Services.navigation.navigate('audienceSelectorPage', {
13
+ site: user.site,
14
+ audienceType: audienceType || 'Custom',
15
+ audienceTypeSelection: audienceTypeSelection || [],
16
+ onChange: (audienceType, audienceTypeSelection) => {
17
+ if (this.props.onChange) this.props.onChange(audienceType, audienceTypeSelection);
18
+ },
19
+ });
20
+ };
21
+
22
+ render() {
23
+ const { style, textStyle, audienceTypeSelection } = this.props;
24
+ const selected =
25
+ audienceTypeSelection && audienceTypeSelection.length > 0
26
+ ? `Current selection: ${audienceTypeSelection.map(i => i.Title).join(', ')}`
27
+ : '';
28
+
29
+ return (
30
+ <FormCardSectionOptionLauncher
31
+ sectionStyle={[styles.audienceSection, style]}
32
+ textStyle={[styles.audienceText, textStyle]}
33
+ onPress={this.onPressAudience}
34
+ title="Audience"
35
+ description={selected}
36
+ value={selected ? 'Custom' : 'All Users'}
37
+ />
38
+ );
39
+ }
40
+ }
41
+
42
+ const styles = StyleSheet.create({
43
+ audienceSection: {
44
+ paddingHorizontal: 0,
45
+ },
46
+ audienceText: {
47
+ fontFamily: 'sf-semibold',
48
+ fontSize: 16,
49
+ color: TEXT_DARK,
50
+ },
51
+ });
52
+
53
+ const mapStateToProps = state => {
54
+ return {
55
+ user: state.user,
56
+ colourBrandingMain: getMainBrandingColourFromState(state),
57
+ };
58
+ };
59
+
60
+ const audienceSelectorLauncher = connect(mapStateToProps, {})(AudienceSelectorLauncher);
61
+ export { audienceSelectorLauncher as AudienceSelectorLauncher };
@@ -0,0 +1,344 @@
1
+ import React, { Component } from 'react';
2
+ import { connect } from 'react-redux';
3
+ import _ from 'lodash';
4
+ import { TouchableOpacity, View, ScrollView, Text, StyleSheet } from 'react-native';
5
+ import { Icon } from 'react-native-elements';
6
+ import { Services } from '../config';
7
+ import { FormCard } from './FormCard';
8
+ import { FormCardSection } from './FormCardSection';
9
+ import { InlineButton } from './InlineButton';
10
+ import Header from './Header';
11
+ import { Spinner } from './Spinner';
12
+ import { TEXT_DARK, COLOUR_GREEN, BG_GREY, INACTIVE_BUTTON, TEXT_LIGHT, getMainBrandingColourFromState } from '../colours';
13
+ import { typeActions, profileActions } from '../apis';
14
+
15
+ class AudienceSelectorPage extends Component {
16
+ constructor(props) {
17
+ super(props);
18
+
19
+ this.state = {
20
+ loading: false,
21
+ categories: [
22
+ {
23
+ name: 'All Primary Users',
24
+ key: 'resident',
25
+ },
26
+ {
27
+ name: 'All Staff Users',
28
+ key: 'staff',
29
+ },
30
+ {
31
+ name: 'All Linked Users',
32
+ key: 'family',
33
+ },
34
+ ],
35
+ types: [],
36
+ tags: [],
37
+ combinedList: [],
38
+ seeAll: false,
39
+ };
40
+ }
41
+
42
+ componentDidMount = async () => {
43
+ try {
44
+ this.setState({ loading: true });
45
+ await this.getUserTypes();
46
+ await this.getUserTags();
47
+ this.setState({ loading: false, combinedList: this.getAvailableAudienceTags() });
48
+ } catch (error) {
49
+ console.error('loading types error', error);
50
+ this.setState({ loading: false });
51
+ }
52
+ };
53
+
54
+ getUserTypes = async () => {
55
+ const { data } = await typeActions.getUserTypes(this.props.site);
56
+ data.forEach(e => {
57
+ e.name = e.category ? `(${e.category[0].toUpperCase() + e.category.substring(1)}) ${e.displayName}` : e.displayName;
58
+ e.key = e.typeName;
59
+ });
60
+ // console.log('getUserTypes', data);
61
+ this.setState({ types: data });
62
+ };
63
+
64
+ getUserTags = async () => {
65
+ const { data } = await profileActions.getUserTagsBySite(this.props.site);
66
+ data.forEach(e => {
67
+ e.name = e.Title;
68
+ e.key = e.Id;
69
+ });
70
+ // console.log('getUserTags', data);
71
+ this.setState({ tags: data });
72
+ };
73
+
74
+ getAvailableAudienceTags = () => {
75
+ const { categories, types, tags } = this.state;
76
+ const { audienceTypeSelection } = this.props;
77
+
78
+ const categoryTags = categories.map(c => {
79
+ const Id = `category_${c.key}`;
80
+ return {
81
+ AudienceType: 'Category',
82
+ AudienceTypeSelection: c.key,
83
+ Id,
84
+ Title: c.name,
85
+ Selected: audienceTypeSelection.some(i => i.Id === Id),
86
+ };
87
+ });
88
+ const userTypeTags = types.map(t => {
89
+ const Id = `userType_${t.typeName}`;
90
+ return {
91
+ AudienceType: 'UserType',
92
+ AudienceTypeSelection: t.typeName,
93
+ Id,
94
+ Title: `User Type: ${t.displayName}`,
95
+ Selected: audienceTypeSelection.some(i => i.Id === Id),
96
+ };
97
+ });
98
+ const userTagTags = tags.map(t => {
99
+ const Id = `userTag_${t.Id}`;
100
+ return {
101
+ AudienceType: 'UserTags',
102
+ AudienceTypeSelection: t.Id,
103
+ Id,
104
+ Title: `User Tag: ${t.Title}`,
105
+ Selected: audienceTypeSelection.some(i => i.Id === Id),
106
+ };
107
+ });
108
+ return [...categoryTags, ...userTypeTags, ...userTagTags];
109
+ };
110
+
111
+ onPressBack = () => {
112
+ Services.navigation.goBack();
113
+ };
114
+
115
+ onSelectAll = () => {
116
+ const newList = [...this.state.combinedList];
117
+ newList.forEach(i => (i.Selected = false));
118
+ this.setState({ combinedList: newList });
119
+ };
120
+
121
+ onSeeAll = () => {
122
+ this.setState({ seeAll: !this.state.seeAll });
123
+ };
124
+
125
+ onToggleAudienceOption = option => {
126
+ const newList = [...this.state.combinedList];
127
+ const selected = newList.find(i => i.Id === option.Id);
128
+ if (selected) {
129
+ selected.Selected = !selected.Selected;
130
+ this.setState({ combinedList: newList });
131
+ }
132
+ };
133
+
134
+ onDone = () => {
135
+ if (this.props.onChange) {
136
+ const { combinedList } = this.state;
137
+ const selected = combinedList.filter(i => i.Selected);
138
+ if (selected && selected.length > 0) {
139
+ this.props.onChange('Custom', selected);
140
+ } else {
141
+ this.props.onChange(null, null);
142
+ }
143
+ }
144
+ Services.navigation.goBack();
145
+ };
146
+
147
+ renderOption(label, selected, onSelect, key = null, hasUnderline = true) {
148
+ return (
149
+ <TouchableOpacity key={key} onPress={onSelect}>
150
+ <FormCardSection hasUnderline={hasUnderline} hasContent>
151
+ <View style={styles.labelContainer}>
152
+ <Text style={styles.labelText}>{label}</Text>
153
+ <Icon
154
+ name="check-circle"
155
+ type="font-awesome"
156
+ iconStyle={[{ color: INACTIVE_BUTTON, fontSize: 20 }, selected && { color: COLOUR_GREEN }]}
157
+ />
158
+ </View>
159
+ </FormCardSection>
160
+ </TouchableOpacity>
161
+ );
162
+ }
163
+
164
+ renderSelectAll() {
165
+ const { combinedList, loading } = this.state;
166
+ const allSelected = !combinedList.find(i => i.Selected);
167
+ if (loading) return null;
168
+
169
+ return (
170
+ <FormCard style={styles.selectAllContainer}>{this.renderOption('All Users', allSelected, this.onSelectAll, null, false)}</FormCard>
171
+ );
172
+ }
173
+
174
+ renderSelection() {
175
+ const { loading, combinedList, seeAll } = this.state;
176
+ if (loading) return null;
177
+
178
+ const selectedText = combinedList
179
+ .filter(i => i.Selected)
180
+ .map(i => i.Title)
181
+ .join(', ');
182
+ const hasSelected = !_.isEmpty(selectedText);
183
+
184
+ return (
185
+ <View style={styles.selectionContainer}>
186
+ <View style={styles.selectionContainerInner}>
187
+ <Text style={styles.selectionTitle}>or select from below</Text>
188
+ {hasSelected ? (
189
+ <TouchableOpacity onPress={this.onSeeAll}>
190
+ <Text style={styles.seeAllButton}>{seeAll ? 'See less' : 'See all'}</Text>
191
+ </TouchableOpacity>
192
+ ) : null}
193
+ </View>
194
+ {hasSelected ? (
195
+ <Text style={styles.selectionText} numberOfLines={seeAll ? null : 1}>{`Current selection: ${selectedText}`}</Text>
196
+ ) : null}
197
+ </View>
198
+ );
199
+ }
200
+
201
+ renderTips() {
202
+ return (
203
+ <View style={styles.tipContainer}>
204
+ <Text style={styles.tipText}>
205
+ <Text style={{ fontFamily: 'sf-semibold' }}>Tip: </Text>
206
+ Group your users using User Tags from your website Community Manager
207
+ </Text>
208
+ </View>
209
+ );
210
+ }
211
+
212
+ renderAvailableAudiences() {
213
+ const { loading, combinedList } = this.state;
214
+
215
+ return (
216
+ <ScrollView style={styles.availableScrollContainer} contentContainerStyle={styles.availabelScrollContent}>
217
+ <FormCard>
218
+ {combinedList.map((option, index) => {
219
+ const notLast = index < combinedList.length - 1;
220
+ return this.renderOption(option.Title, option.Selected, () => this.onToggleAudienceOption(option), index, notLast);
221
+ })}
222
+ </FormCard>
223
+ {loading ? <Spinner /> : null}
224
+ {this.renderTips()}
225
+ </ScrollView>
226
+ );
227
+ }
228
+
229
+ renderButtons() {
230
+ const { loading } = this.state;
231
+
232
+ return (
233
+ <View style={styles.doneButtonContainer}>
234
+ <InlineButton
235
+ color={loading ? INACTIVE_BUTTON : this.props.colourBrandingMain}
236
+ onPress={this.onDone}
237
+ touchableStyle={styles.doneButton}
238
+ fillTouchable
239
+ large
240
+ disabled={loading}
241
+ >
242
+ Done
243
+ </InlineButton>
244
+ </View>
245
+ );
246
+ }
247
+
248
+ render() {
249
+ return (
250
+ <View style={styles.container}>
251
+ <Header leftIcon="angle-left" onPressLeft={this.onPressBack} text={'Select Audience'} />
252
+ {this.renderSelectAll()}
253
+ {this.renderSelection()}
254
+ {this.renderAvailableAudiences()}
255
+ {this.renderButtons()}
256
+ </View>
257
+ );
258
+ }
259
+ }
260
+
261
+ const styles = StyleSheet.create({
262
+ container: {
263
+ flex: 1,
264
+ position: 'relative',
265
+ backgroundColor: BG_GREY,
266
+ },
267
+ selectAllContainer: {
268
+ marginTop: 20,
269
+ },
270
+ selectionContainer: {
271
+ padding: 20,
272
+ },
273
+ selectionContainerInner: {
274
+ flexDirection: 'row',
275
+ justifyContent: 'space-between',
276
+ },
277
+ selectionTitle: {
278
+ fontFamily: 'sf-bold',
279
+ fontSize: 14,
280
+ color: TEXT_DARK,
281
+ },
282
+ selectionText: {
283
+ fontFamily: 'sf-bold',
284
+ fontSize: 14,
285
+ color: TEXT_DARK,
286
+ marginTop: 10,
287
+ },
288
+ seeAllButton: {
289
+ marginLeft: 10,
290
+ fontFamily: 'sf-bold',
291
+ fontSize: 14,
292
+ color: TEXT_LIGHT,
293
+ },
294
+ availableScrollContainer: {
295
+ flex: 1,
296
+ },
297
+ availabelScrollContent: {
298
+ flexGrow: 1,
299
+ justifyContent: 'space-between',
300
+ flexDirection: 'column',
301
+ },
302
+ tipContainer: {
303
+ padding: 10,
304
+ },
305
+ tipText: {
306
+ fontSize: 14,
307
+ fontFamily: 'sf-regular',
308
+ color: TEXT_DARK,
309
+ },
310
+ labelContainer: {
311
+ flexDirection: 'row',
312
+ justifyContent: 'space-between',
313
+ },
314
+ labelText: {
315
+ fontFamily: 'sf-medium',
316
+ fontSize: 16,
317
+ color: TEXT_DARK,
318
+ },
319
+ description: {
320
+ marginTop: 5,
321
+ fontSize: 14,
322
+ fontFamily: 'sf-regular',
323
+ color: TEXT_DARK,
324
+ },
325
+ doneButtonContainer: {
326
+ backgroundColor: '#fff',
327
+ marginTop: 1,
328
+ flexDirection: 'row',
329
+ paddingTop: 10,
330
+ paddingBottom: 20,
331
+ },
332
+ doneButton: {
333
+ flex: 1,
334
+ marginHorizontal: 6,
335
+ },
336
+ });
337
+
338
+ const mapStateToProps = state => {
339
+ return {
340
+ colourBrandingMain: getMainBrandingColourFromState(state),
341
+ };
342
+ };
343
+
344
+ export default connect(mapStateToProps, {})(AudienceSelectorPage);
@@ -28,7 +28,7 @@ class CommentReply extends Component {
28
28
  componentDidMount() {
29
29
  if (this.props.commentSection) {
30
30
  this.setState({
31
- commentsLoading: this.props.commentSection.getWrappedInstance().isLoading(),
31
+ commentsLoading: this.props.commentSection.isLoading(),
32
32
  });
33
33
  }
34
34
  }
@@ -93,13 +93,13 @@ class CommentReply extends Component {
93
93
  });
94
94
  Keyboard.dismiss();
95
95
  if (this.props.commentSection) {
96
- this.props.commentSection.getWrappedInstance().startedAddingComment();
96
+ this.props.commentSection.startedAddingComment();
97
97
  }
98
98
  reactionActions
99
99
  .addComment(this.props.entityId, this.props.entityType, this.props.entityName, this.props.site, text, image)
100
100
  .then(res => {
101
101
  if (this.props.commentSection) {
102
- this.props.commentSection.getWrappedInstance().commentAdded(res.data);
102
+ this.props.commentSection.commentAdded(res.data);
103
103
  }
104
104
  this.setState({
105
105
  addingComment: false,
@@ -125,7 +125,7 @@ class CommentReply extends Component {
125
125
  if (this.state.uploadingCommentImage || !_.isEmpty(this.state.commentImageInput)) {
126
126
  return;
127
127
  }
128
- this.commentImageUploader.getWrappedInstance().showUploadMenu();
128
+ this.commentImageUploader.showUploadMenu();
129
129
  }
130
130
 
131
131
  renderImageAttachment() {
@@ -329,5 +329,5 @@ const mapStateToProps = state => {
329
329
  return { user };
330
330
  };
331
331
 
332
- const commentReply = connect(mapStateToProps, {}, null, { withRef: true })(CommentReply);
332
+ const commentReply = connect(mapStateToProps, {}, null, { forwardRef: true })(CommentReply);
333
333
  export { commentReply as CommentReply };
@@ -7,7 +7,11 @@ import { Icon } from 'react-native-elements';
7
7
  import { getPluralS, getThumb300, get1400, getSiteSettingFromState } from '../helper';
8
8
  import { getMainBrandingColourFromState, TEXT_DARKEST, BG_GREY, TEXT_LIGHT, LINEGREY } from '../colours';
9
9
  import { reactionActions, notificationActions } from '../apis';
10
- import { ConfirmPopup, ProfilePic, ImagePopup, InlineButton, Spinner } from './';
10
+ import { ConfirmPopup } from './ConfirmPopup';
11
+ import { ProfilePic } from './ProfilePic';
12
+ import { ImagePopup } from './ImagePopup';
13
+ import { InlineButton } from './InlineButton';
14
+ import { Spinner } from './Spinner';
11
15
 
12
16
  class CommentSection extends Component {
13
17
  constructor(props) {
@@ -28,7 +32,6 @@ class CommentSection extends Component {
28
32
 
29
33
  componentDidMount() {
30
34
  this.getNotificationSate();
31
- // setTimeout(this.getNotificationSate, 3000);
32
35
 
33
36
  if (!_.includes(this.props.user.hidden, 'viewComment')) {
34
37
  this.getComments();
@@ -97,7 +100,7 @@ class CommentSection extends Component {
97
100
  }
98
101
 
99
102
  onGoToAdd() {
100
- this.props.commentReply.getWrappedInstance().focusInput();
103
+ this.props.commentReply.focusInput();
101
104
  }
102
105
 
103
106
  onMute = () => {
@@ -172,7 +175,7 @@ class CommentSection extends Component {
172
175
  commentsLoading: true,
173
176
  });
174
177
  if (this.props.commentReply) {
175
- this.props.commentReply.getWrappedInstance().loadingStarted();
178
+ this.props.commentReply.loadingStarted();
176
179
  }
177
180
  this.loadComments();
178
181
  }
@@ -206,7 +209,7 @@ class CommentSection extends Component {
206
209
  },
207
210
  );
208
211
  if (this.props.commentReply) {
209
- this.props.commentReply.getWrappedInstance().loadingCompleted();
212
+ this.props.commentReply.loadingCompleted();
210
213
  }
211
214
  if (this.props.live) {
212
215
  this.loadTimer = setTimeout(() => {
@@ -612,5 +615,5 @@ const mapStateToProps = state => {
612
615
  };
613
616
  };
614
617
 
615
- const commentSection = connect(mapStateToProps, {}, null, { withRef: true })(CommentSection);
618
+ const commentSection = connect(mapStateToProps, {}, null, { forwardRef: true })(CommentSection);
616
619
  export { commentSection as CommentSection };
@@ -2,23 +2,26 @@ import React, { PureComponent } from 'react';
2
2
  import { View, Text, TouchableOpacity } from 'react-native';
3
3
  import { Icon } from 'react-native-elements';
4
4
  import { connect } from 'react-redux';
5
- import { TEXT_DARK, getMainBrandingColourFromState } from '../colours';
5
+ import { TEXT_DARK, TEXT_LIGHT, getMainBrandingColourFromState } from '../colours';
6
6
  import { FormCardSection } from './FormCardSection';
7
7
 
8
8
  class FormCardSectionOptionLauncher extends PureComponent {
9
9
  render() {
10
+ const { onPress, sectionStyle, textStyle, title, description, value, icon, colourBrandingMain } = this.props;
11
+
10
12
  return (
11
- <TouchableOpacity onPress={this.props.onPress}>
12
- <FormCardSection hasContent sectionStyle={this.props.sectionStyle}>
13
+ <TouchableOpacity onPress={onPress}>
14
+ <FormCardSection hasContent sectionStyle={sectionStyle}>
13
15
  <View style={styles.container}>
14
- <Text style={[styles.text, { marginRight: 16, flex: 1 }, this.props.textStyle]}>{this.props.title}</Text>
15
- <Text style={[styles.text, { marginRight: 16 }]}>{this.props.value}</Text>
16
+ <Text style={[styles.text, { marginRight: 16, flex: 1 }, textStyle]}>{title}</Text>
17
+ <Text style={[styles.text, { marginRight: 16 }]}>{value}</Text>
16
18
  <Icon
17
- name={this.props.icon ? this.props.icon : 'angle-right'}
19
+ name={icon ? icon : 'angle-right'}
18
20
  type="font-awesome"
19
- iconStyle={[styles.text, { fontSize: 20, color: this.props.colourBrandingMain }]}
21
+ iconStyle={[styles.text, { fontSize: 20, color: colourBrandingMain }]}
20
22
  />
21
23
  </View>
24
+ {description ? <Text style={styles.description}>{description}</Text> : null}
22
25
  </FormCardSection>
23
26
  </TouchableOpacity>
24
27
  );
@@ -36,6 +39,12 @@ const styles = {
36
39
  fontSize: 17,
37
40
  color: TEXT_DARK,
38
41
  },
42
+ description: {
43
+ fontFamily: 'sf-regular',
44
+ fontSize: 15,
45
+ color: TEXT_LIGHT,
46
+ marginTop: 8,
47
+ },
39
48
  };
40
49
 
41
50
  const mapStateToProps = state => {
@@ -5,7 +5,7 @@ import { Spinner } from './Spinner';
5
5
 
6
6
  class ImageUploadProgress extends Component {
7
7
  onRetryUpload = (imageUri, uploadUri) => {
8
- this.props.uploader.getWrappedInstance().retryUpload(imageUri, uploadUri);
8
+ this.props.uploader.retryUpload(imageUri, uploadUri);
9
9
  };
10
10
 
11
11
  render() {
@@ -3,7 +3,7 @@ import { View, ScrollView, TouchableOpacity, Text, Platform, Linking, Modal, Dim
3
3
  import { connect } from 'react-redux';
4
4
  import { Icon } from 'react-native-elements';
5
5
  import _ from 'lodash';
6
- import * as Permissions from 'expo-permissions';
6
+ import { Camera } from 'expo-camera';
7
7
  import * as MediaLibrary from 'expo-media-library';
8
8
  import * as ImageManipulator from 'expo-image-manipulator';
9
9
  import * as ImagePicker from 'expo-image-picker';
@@ -60,7 +60,7 @@ class ImageUploader extends Component {
60
60
  }
61
61
 
62
62
  loadLocalAlbums = async () => {
63
- const hasPermission = await Permissions.getAsync(Permissions.CAMERA_ROLL);
63
+ const hasPermission = await MediaLibrary.getPermissionsAsync();
64
64
  if (!hasPermission.granted) return;
65
65
 
66
66
  this.setState({ loadingLocalFolders: true }, async () => {
@@ -254,8 +254,8 @@ class ImageUploader extends Component {
254
254
  }
255
255
 
256
256
  askPermissionsAsync = async () => {
257
- const cameraPermission = await Permissions.askAsync(Permissions.CAMERA);
258
- const rollPermission = await Permissions.askAsync(Permissions.CAMERA_ROLL);
257
+ const cameraPermission = await Camera.requestCameraPermissionsAsync();
258
+ const rollPermission = await MediaLibrary.requestPermissionsAsync();
259
259
  if (cameraPermission.status !== 'granted' || rollPermission.status !== 'granted') {
260
260
  this.showWarningPopup(cameraPermission.status !== 'granted', rollPermission.status !== 'granted');
261
261
  return false;
@@ -786,4 +786,4 @@ const mapStateToProps = state => {
786
786
  };
787
787
  };
788
788
 
789
- export default connect(mapStateToProps, { stockImagesLoaded, imageLibraryLoaded }, null, { withRef: true })(ImageUploader);
789
+ export default connect(mapStateToProps, { stockImagesLoaded, imageLibraryLoaded }, null, { forwardRef: true })(ImageUploader);
@@ -158,7 +158,7 @@ class PlussChat extends Component {
158
158
  };
159
159
 
160
160
  showUploadMenu() {
161
- this.imageUploader.getWrappedInstance().showUploadMenu();
161
+ this.imageUploader.showUploadMenu();
162
162
  }
163
163
 
164
164
  onFocusInput = () => {
@@ -3,7 +3,8 @@ import { Platform, View, Modal, TouchableOpacity, StyleSheet } from 'react-nativ
3
3
  import * as ScreenOrientation from 'expo-screen-orientation';
4
4
  import { StatusBarHeight, getCompressed, imageExists } from '../helper';
5
5
  import { Pl60Icon } from '../fonts';
6
- import { SharingTools, MediaPlayer } from './';
6
+ import { SharingTools } from './SharingTools';
7
+ import { MediaPlayer } from './MediaPlayer';
7
8
 
8
9
  class VideoPopup extends Component {
9
10
  constructor(props) {
@@ -3,7 +3,7 @@ import { StyleSheet, View, FlatList, Dimensions, ActivityIndicator, Platform } f
3
3
  import _ from 'lodash';
4
4
  import * as ScreenOrientation from 'expo-screen-orientation';
5
5
  import * as MediaLibrary from 'expo-media-library';
6
- import * as Permissions from 'expo-permissions';
6
+ import { Camera } from 'expo-camera';
7
7
  import { isVideo } from '../../helper';
8
8
  import ImageTile from './ImageTile';
9
9
 
@@ -49,8 +49,8 @@ export default class ImageBrowser extends React.Component {
49
49
  };
50
50
 
51
51
  getPermissionsAsync = async () => {
52
- const { status: camera } = await Permissions.askAsync(Permissions.CAMERA);
53
- const { status: cameraRoll } = await Permissions.askAsync(Permissions.CAMERA_ROLL);
52
+ const { status: camera } = await Camera.requestCameraPermissionsAsync();
53
+ const { status: cameraRoll } = await MediaLibrary.requestPermissionsAsync();
54
54
  this.setState({
55
55
  hasCameraPermission: camera === 'granted',
56
56
  hasCameraRollPermission: cameraRoll === 'granted',
@@ -36,6 +36,7 @@ export * from './FontScalePopup';
36
36
  export * from './FontScaleButton';
37
37
  export * from './UserListPopup';
38
38
  export * from './Reactions';
39
+ export * from './AudienceSelectorLauncher';
39
40
  export { default as EmptyStateWidget } from './EmptyStateWidget';
40
41
  export { default as EmptyStateMain } from './EmptyStateMain';
41
42
  export { default as LoadingStateWidget } from './LoadingStateWidget';
@@ -48,3 +49,4 @@ export { default as PlussChat } from './PlussChat';
48
49
  export { default as PositionedImage } from './PositionedImage';
49
50
  export { default as FormattedText } from './FormattedText';
50
51
  export { default as MediaPlayer } from './MediaPlayer';
52
+ export { default as AudienceSelectorPage } from './AudienceSelectorPage';