@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 +21 -16
- package/src/apis/index.js +2 -0
- package/src/apis/profileActions.js +13 -0
- package/src/apis/typeActions.js +13 -0
- package/src/components/AudienceSelectorLauncher.js +61 -0
- package/src/components/AudienceSelectorPage.js +344 -0
- package/src/components/CommentReply.js +5 -5
- package/src/components/CommentSection.js +9 -6
- package/src/components/FormCardSectionOptionLauncher.js +16 -7
- package/src/components/ImageUploadProgress.js +1 -1
- package/src/components/ImageUploader.js +5 -5
- package/src/components/PlussChat.js +1 -1
- package/src/components/VideoPopup.js +2 -1
- package/src/components/expo-image-picker-multiple/ImageBrowser.js +3 -3
- package/src/components/index.js +2 -0
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
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"expo-
|
|
21
|
-
"expo-
|
|
22
|
-
"expo-
|
|
23
|
-
"expo-
|
|
24
|
-
"expo-
|
|
25
|
-
"expo-
|
|
26
|
-
"expo-
|
|
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": "
|
|
33
|
-
"react-native": "
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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, {
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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, {
|
|
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={
|
|
12
|
-
<FormCardSection hasContent 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 },
|
|
15
|
-
<Text style={[styles.text, { marginRight: 16 }]}>{
|
|
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={
|
|
19
|
+
name={icon ? icon : 'angle-right'}
|
|
18
20
|
type="font-awesome"
|
|
19
|
-
iconStyle={[styles.text, { fontSize: 20, color:
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
258
|
-
const rollPermission = await
|
|
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, {
|
|
789
|
+
export default connect(mapStateToProps, { stockImagesLoaded, imageLibraryLoaded }, null, { forwardRef: true })(ImageUploader);
|
|
@@ -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
|
|
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
|
|
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
|
|
53
|
-
const { status: cameraRoll } = await
|
|
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',
|
package/src/components/index.js
CHANGED
|
@@ -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';
|