@plusscommunities/pluss-circles-app 1.0.11 → 1.0.12

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,13 +1,14 @@
1
1
  {
2
2
  "name": "@plusscommunities/pluss-circles-app",
3
- "version": "1.0.11",
3
+ "version": "1.0.12",
4
4
  "description": "Extension package to enable circles on Pluss Communities Platform",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
7
7
  "core": "npm i @plusscommunities/pluss-core-app@latest",
8
- "upload": "npm version patch && npm publish --access public && rm -rf node_modules",
9
- "deploy": "npm run core && npm run upload",
10
- "test": "echo \"Error: no test specified\" && exit 1"
8
+ "patch": "npm version patch",
9
+ "upload": "npm publish --access public && rm -rf node_modules",
10
+ "upload:p": "npm run patch && npm run upload",
11
+ "upload:cp": "npm run core && npm run patch && npm run upload"
11
12
  },
12
13
  "author": "Phillip Suh",
13
14
  "license": "ISC",
@@ -103,23 +103,24 @@ class Chat extends Component {
103
103
  }
104
104
 
105
105
  this.chatName = !_.isUndefined(chat.title) ? chat.title : chat.Title;
106
+ let newGroup = chat;
106
107
 
107
- let newGrop = { Title: !_.isUndefined(chat.title) ? chat.title : chat.Title };
108
+ newGroup.Title = !_.isUndefined(chat.title) ? chat.title : chat.Title;
108
109
  _.forEach(this.props.groups, group => {
109
110
  if (group.Id === this.props.chat.key || group.Id === this.props.chat.Id) {
110
- newGrop = { ...group };
111
+ newGroup = { ...group };
111
112
  }
112
113
  });
113
114
  let muteChat = false;
114
- if (!_.isUndefined(newGrop.Muted)) {
115
- newGrop.Muted.forEach(user => {
115
+ if (!_.isUndefined(newGroup.Muted)) {
116
+ newGroup.Muted.forEach(user => {
116
117
  if (user === this.props.user.uid) {
117
118
  muteChat = true;
118
119
  }
119
120
  });
120
121
  }
121
122
 
122
- const statee = { chat, group: newGrop, muteChat };
123
+ const statee = { chat, group: newGroup, muteChat };
123
124
  if (this.chatKey && !_.isUndefined(this.props.reducerMessages[this.chatKey])) {
124
125
  statee.messages = this.props.reducerMessages[this.chatKey];
125
126
  }
@@ -163,7 +164,7 @@ class Chat extends Component {
163
164
  newStates.newLoading = false;
164
165
  this.setState({ newLoading: false });
165
166
  }
166
- let newGrop2 = { Title: '' };
167
+ let newGrop2 = this.state.group;
167
168
  _.forEach(nextProps.groups, group => {
168
169
  if (group.Id === this.chatKey) {
169
170
  newGrop2 = { ...group };
@@ -183,6 +184,21 @@ class Chat extends Component {
183
184
  }
184
185
  }
185
186
 
187
+ getTitle = () => {
188
+ if (!this.state.group) {
189
+ return '';
190
+ }
191
+ if (_.isEmpty(this.state.group.Title) && this.state.group.IsPrivate) {
192
+ const otherUser = _.find(this.state.group.Audience || [], u => {
193
+ return u.userId !== this.props.user.Id;
194
+ });
195
+ if (otherUser) {
196
+ return otherUser.displayName;
197
+ }
198
+ }
199
+ return this.state.group.Title;
200
+ };
201
+
186
202
  onPressEvent = event => {
187
203
  NavigationService.navigate('eventDetail', { event });
188
204
  };
@@ -430,12 +446,11 @@ class Chat extends Component {
430
446
  onPress={this.muteTest.bind(this)}
431
447
  textStyle={this.state.muteSubmitting ? { color: TEXT_LIGHTER } : {}}
432
448
  />
433
- {!this.isGroupOwner(this.props.user.uid) && (
434
- <DropDownItem last text={'Leave'} onPress={this.onOpenDeletePrompt.bind(this, 'leave')} />
435
- )}
449
+ {/* {!this.isGroupOwner(this.props.user.uid) &&
450
+ (<DropDownItem last text={'Leave'} onPress={this.onOpenDeletePrompt.bind(this, 'leave')} />)}
436
451
  {this.isGroupOwner(this.props.user.uid) && (
437
452
  <DropDownItem last text={'Delete'} onPress={this.onOpenDeletePrompt.bind(this, 'delete')} />
438
- )}
453
+ )} */}
439
454
  </DropDownMenu>
440
455
  <Header
441
456
  leftIcon="angle-left"
@@ -443,7 +458,7 @@ class Chat extends Component {
443
458
  rightIconType="pluss"
444
459
  rightIcon="more_options"
445
460
  onPressRight={this.toggleSettings.bind(this)}
446
- text={this.state.group.Title}
461
+ text={this.getTitle()}
447
462
  onTextPress={this.goToGroupInfo.bind(this)}
448
463
  />
449
464
 
@@ -4,12 +4,11 @@ import { connect } from 'react-redux';
4
4
  import moment from 'moment';
5
5
  import _ from 'lodash';
6
6
  import { Icon } from 'react-native-elements';
7
- import { ProfilePic, EmptyStateMain } from '../common';
7
+ import { ProfilePic, EmptyStateMain, TextStyle } from '../common';
8
8
  import NavigationService from '../../js/NavigationService';
9
9
  import { TEXT_DARK, TEXT_MID, TEXT_LIGHT, COLOUR_GREEN, getMainBrandingColourFromState } from '../../js';
10
10
  import { clearGroupCounter } from '../../actions';
11
11
  import GroupInvite from '../notifications/GroupInvite';
12
- import PublicGroup from '../groups/PublicGroup';
13
12
  import { CONST_STRINGS } from '../../config';
14
13
 
15
14
  class ChatList extends Component {
@@ -21,7 +20,6 @@ class ChatList extends Component {
21
20
  showMore: true,
22
21
  dataSource: [],
23
22
  invites: [],
24
- publicGroups: [],
25
23
  };
26
24
  }
27
25
 
@@ -29,7 +27,6 @@ class ChatList extends Component {
29
27
  this._isMounted = true;
30
28
 
31
29
  this.createDataSource(this.props);
32
- this.setState({ publicGroups: this.props.publicGroups });
33
30
  }
34
31
 
35
32
  UNSAFE_componentWillReceiveProps(nextProps) {
@@ -38,11 +35,6 @@ class ChatList extends Component {
38
35
  this.createDataSource(nextProps);
39
36
  }, 500);
40
37
  }
41
- if (nextProps.publicGroups !== this.props.publicGroups) {
42
- this.setState({
43
- publicGroups: nextProps.publicGroups,
44
- });
45
- }
46
38
  }
47
39
 
48
40
  shouldComponentUpdate(nextProps, nextState) {
@@ -62,25 +54,19 @@ class ChatList extends Component {
62
54
  NavigationService.goBack();
63
55
  }
64
56
 
65
- onClosePublicGroup(group, joined) {
66
- if (joined) {
67
- const index = this.state.publicGroups.indexOf(group);
68
- if (index > -1) {
69
- this.state.publicGroups.splice(index, 1);
70
- const newPublicGroups = this.state.publicGroups;
71
- if (this._isMounted) {
72
- this.setState({
73
- publicGroups: newPublicGroups,
74
- });
75
- }
76
- }
77
- }
78
- }
79
-
80
57
  getChatName(chat) {
81
58
  if (!_.isEmpty(chat.Title)) {
82
59
  return chat.Title;
83
60
  }
61
+ if (chat.IsPrivate) {
62
+ const otherUser = _.find(chat.Audience || [], u => {
63
+ return u.userId !== this.props.user.Id;
64
+ });
65
+ if (otherUser) {
66
+ return otherUser.displayName;
67
+ }
68
+ return null;
69
+ }
84
70
  if (chat.userNames) {
85
71
  const names = _.without(Object.keys(chat.userNames), this.props.user.displayName);
86
72
  return names.length > 2 ? `${names[0]} & ${names.length - 1} others` : names.join(', ');
@@ -89,9 +75,21 @@ class ChatList extends Component {
89
75
  }
90
76
 
91
77
  getChatImage(chat) {
92
- if (!_.isUndefined(chat.Thumbnail) && !_.isEmpty(chat.Thumbnail)) {
78
+ if (!_.isEmpty(chat.Thumbnail)) {
93
79
  return chat.Thumbnail;
94
80
  }
81
+ if (!_.isEmpty(chat.Image)) {
82
+ return chat.Image;
83
+ }
84
+ if (chat.IsPrivate) {
85
+ const otherUser = _.find(chat.Audience || [], u => {
86
+ return u.userId !== this.props.user.Id;
87
+ });
88
+ if (otherUser) {
89
+ return otherUser.profilePic;
90
+ }
91
+ return null;
92
+ }
95
93
  if (chat.Creator) {
96
94
  return chat.Creator.profilePic;
97
95
  }
@@ -99,7 +97,7 @@ class ChatList extends Component {
99
97
  }
100
98
 
101
99
  getTimeText(input) {
102
- const gug = moment.unix(input);
100
+ const gug = moment.unix(input).local();
103
101
  if (
104
102
  gug.isBefore(
105
103
  moment()
@@ -114,6 +112,9 @@ class ChatList extends Component {
114
112
 
115
113
  getLastMessage(chat) {
116
114
  if (_.isUndefined(chat.lastMessage)) {
115
+ if (chat.IsPrivate) {
116
+ return '';
117
+ }
117
118
  return 'New Circle';
118
119
  }
119
120
  if (typeof chat.lastMessage === 'string') {
@@ -137,9 +138,9 @@ class ChatList extends Component {
137
138
  return !_.isEmpty(chat) && !chat.deleted && !chat.Deleted && chat != null;
138
139
  });
139
140
 
140
- const messages = !this.state.showMore ? _.take(_.orderBy(chatsToUse, 'Changed', 'desc'), 3) : _.orderBy(chatsToUse, 'Changed', 'desc');
141
+ const messages = _.orderBy(_.orderBy(chatsToUse, 'Changed', 'desc'), 'IsPrivate', 'desc');
141
142
 
142
- const chats = [...messages];
143
+ const chats = [...(!this.state.showMore ? _.take(messages, 3) : messages)];
143
144
  // const chats = [
144
145
  // ...messages,
145
146
  // !this.state.showMore && chatsToUse.length > 3 ? { type: 'more' } : {}
@@ -147,6 +148,13 @@ class ChatList extends Component {
147
148
 
148
149
  if (!_.isEmpty(chats[0]) && chats[0] != null) {
149
150
  chats[0].first = true;
151
+
152
+ const firstPrivate = _.find(chats, c => {
153
+ return c.IsPrivate;
154
+ });
155
+ if (firstPrivate) {
156
+ firstPrivate.firstPrivate = true;
157
+ }
150
158
  }
151
159
 
152
160
  let invites = nextProps.invites;
@@ -168,13 +176,6 @@ class ChatList extends Component {
168
176
  return CONST_STRINGS.EMPTY_GROUPS;
169
177
  }
170
178
 
171
- shouldRenderPublicGroups() {
172
- if (_.isEmpty(this.state.publicGroups) || this.props.user.category === 'family') {
173
- return false;
174
- }
175
- return true;
176
- }
177
-
178
179
  renderReply(chat) {
179
180
  if (chat.lastUserKey === this.props.user.uid) {
180
181
  return <Icon name="mail-reply" type="font-awesome" iconStyle={styles.chatReplyIcon} />;
@@ -200,10 +201,15 @@ class ChatList extends Component {
200
201
 
201
202
  const chatName = this.getChatName(chat);
202
203
  const chatImage = this.getChatImage(chat);
203
- const unread = chat.Unread[this.props.user.uid] > 0;
204
+ const unreadCounter = (chat.Unread && chat.Unread[this.props.user.Id]) || 0;
204
205
 
205
206
  return (
206
207
  <View key={`chat${chat.Id}`} style={[chat.first && _.isEmpty(this.state.invites) && { marginTop: 8 }]}>
208
+ {chat.firstPrivate && (
209
+ <TextStyle type="pageHeading" style={styles.dmHeading}>
210
+ Direct Messages
211
+ </TextStyle>
212
+ )}
207
213
  <TouchableOpacity activeOpacity={0.9} onPress={this.onPressChat.bind(this, chat)}>
208
214
  <View
209
215
  style={[
@@ -214,19 +220,19 @@ class ChatList extends Component {
214
220
  <ProfilePic Diameter={40} name={chatName} ProfilePic={chatImage} />
215
221
  <View style={styles.listItemTextContainer}>
216
222
  <View style={styles.rowReverse}>
217
- <Text style={[styles.chatTimeStamp, unread && { color: TEXT_DARK, fontFamily: 'sf-bold' }]}>
223
+ <Text style={[styles.chatTimeStamp, !!unreadCounter && { color: TEXT_DARK, fontFamily: 'sf-bold' }]}>
218
224
  {this.getTimeText(chat.Changed)}
219
225
  </Text>
220
226
  <View style={styles.fill}>
221
- <Text style={[styles.chatUserName, unread && styles.chatTextBold]} numberOfLines={1}>
227
+ <Text style={[styles.chatUserName, !!unreadCounter && styles.chatTextBold]} numberOfLines={1}>
222
228
  {chatName}
223
229
  </Text>
224
230
  </View>
225
231
  </View>
226
232
  <View style={styles.rowReverse}>
227
- {this.renderUnreadCounter(chat.Unread[this.props.user.uid])}
233
+ {this.renderUnreadCounter(unreadCounter)}
228
234
  <View style={styles.fill}>
229
- <Text style={[styles.chatLastMessage, unread && {}]} numberOfLines={1}>
235
+ <Text style={[styles.chatLastMessage, !!unreadCounter && {}]} numberOfLines={1}>
230
236
  {this.getLastMessage(chat)}
231
237
  </Text>
232
238
  </View>
@@ -255,34 +261,13 @@ class ChatList extends Component {
255
261
  });
256
262
  }
257
263
 
258
- renderPublicGroups() {
259
- if (!this.shouldRenderPublicGroups()) {
260
- return null;
261
- }
262
- return this.state.publicGroups.map((group, index) => {
263
- if (!group) {
264
- return null;
265
- }
266
- return (
267
- <View key={group.Id}>
268
- <PublicGroup group={group} index={index} key={group.Id} showHeader onClose={this.onClosePublicGroup.bind(this, group)} />
269
- </View>
270
- );
271
- });
272
- }
273
-
274
264
  renderListHeader() {
275
- if (!this.props.ListHeaderComponent && ((_.isEmpty(this.state.invites) && !this.shouldRenderPublicGroups()) || this.props.isWidget)) {
265
+ if (!this.props.ListHeaderComponent && (_.isEmpty(this.state.invites) || this.props.isWidget)) {
276
266
  return null;
277
267
  }
278
268
  let internalHeader = null;
279
- if (!_.isEmpty(this.state.invites) || this.shouldRenderPublicGroups()) {
280
- internalHeader = (
281
- <ScrollView horizontal>
282
- {!_.isEmpty(this.state.invites) && this.renderInvites()}
283
- {this.renderPublicGroups()}
284
- </ScrollView>
285
- );
269
+ if (!_.isEmpty(this.state.invites)) {
270
+ internalHeader = <ScrollView horizontal>{!_.isEmpty(this.state.invites) && this.renderInvites()}</ScrollView>;
286
271
  }
287
272
  return (
288
273
  <View>
@@ -428,16 +413,19 @@ const styles = {
428
413
  fontFamily: 'sf-semibold',
429
414
  textAlign: 'center',
430
415
  },
416
+ dmHeading: {
417
+ // paddingHorizontal: 16,
418
+ // paddingVertical: 8,
419
+ padding: 16,
420
+ },
431
421
  };
432
422
 
433
423
  const mapStateToProps = state => {
434
- const { circles, circleChats, user } = state;
435
- // console.log('mapStateToProps - circles', circles);
424
+ const { circles, user } = state;
436
425
 
437
426
  return {
438
427
  groups: circles.list,
439
428
  invites: circles.invites,
440
- chats: circleChats.list,
441
429
  unreadCount: circles && circles.unreadCount ? circles.unreadCount : 0,
442
430
  user,
443
431
  colourBrandingMain: getMainBrandingColourFromState(state),
@@ -1,23 +1,27 @@
1
1
  import { PlussCore } from '../../feature.config';
2
2
 
3
3
  const { Components } = PlussCore;
4
- export const InlineButton = Components.InlineButton;
5
- export const ProfilePic = Components.ProfilePic;
6
- export const EmptyStateMain = Components.EmptyStateMain;
7
- export const Spinner = Components.Spinner;
8
- export const MiddlePopup = Components.MiddlePopup;
4
+ export const Attachment = Components.Attachment;
9
5
  export const BackButton = Components.BackButton;
10
- export const UserListing = Components.UserListing;
11
- export const GenericInput = Components.GenericInput;
12
- export const Toggle = Components.Toggle;
13
- export const ImageUploader = Components.ImageUploader;
14
- export const Header = Components.Header;
6
+ export const ConfirmPopup = Components.ConfirmPopup;
7
+ export const DropDownMenu = Components.DropDownMenu;
8
+ export const DropDownItem = Components.DropDownItem;
9
+ export const EmptyStateMain = Components.EmptyStateMain;
15
10
  export const FormCard = Components.FormCard;
16
11
  export const FormCardSection = Components.FormCardSection;
12
+ export const GenericInput = Components.GenericInput;
13
+ export const Header = Components.Header;
14
+ export const ImagePopup = Components.ImagePopup;
15
+ export const ImageUploader = Components.ImageUploader;
16
+ export const InlineButton = Components.InlineButton;
17
17
  export const Input = Components.Input;
18
- export const StickyFooter = Components.StickyFooter;
19
18
  export const LoadingIndicator = Components.LoadingIndicator;
20
- export const ConfirmPopup = Components.ConfirmPopup;
21
- export const DropDownMenu = Components.DropDownMenu;
22
- export const DropDownItem = Components.DropDownItem;
19
+ export const MiddlePopup = Components.MiddlePopup;
20
+ export const PDFPopup = Components.PDFPopup;
23
21
  export const PlussChat = Components.PlussChat;
22
+ export const ProfilePic = Components.ProfilePic;
23
+ export const Spinner = Components.Spinner;
24
+ export const StickyFooter = Components.StickyFooter;
25
+ export const TextStyle = Components.TextStyle;
26
+ export const Toggle = Components.Toggle;
27
+ export const UserListing = Components.UserListing;
@@ -1,43 +1,42 @@
1
1
  import React, { Component } from 'react';
2
- import { View, ScrollView, ImageBackground, Animated, Dimensions, Text } from 'react-native';
2
+ import { View, ScrollView, Text, TouchableOpacity, Image } from 'react-native';
3
3
  import { connect } from 'react-redux';
4
+ import { Icon } from 'react-native-elements';
4
5
  import _ from 'lodash';
5
6
  import moment from 'moment';
6
- import { LinearGradient } from 'expo-linear-gradient';
7
7
  import NavigationService from '../../js/NavigationService';
8
- import {
9
- getImageSource,
10
- TEXT_DARK,
11
- TEXT_BLUEGREY,
12
- COLOUR_TRANSPARENT,
13
- LINEGREY,
14
- getMainBrandingColourFromState,
15
- StatusBarHeight,
16
- } from '../../js';
17
- import { ProfilePic, BackButton } from '../common';
18
- import { getNewEventDefaults } from '../../config';
19
-
20
- const SCREEN_HEIGHT = Dimensions.get('window').height;
21
- const CARD_IMAGE_HEIGHT = SCREEN_HEIGHT * 0.3;
22
- const SCREEN_WIDTH = Dimensions.get('window').width;
23
- const CARD_TO_FIX = CARD_IMAGE_HEIGHT - 90;
8
+ import { TEXT_DARK, COLOUR_DUSK, LINEGREY, getMainBrandingColourFromState, getFileName, getSite } from '../../js';
9
+ import { Attachment, Header, TextStyle, UserListing, PDFPopup, ImagePopup } from '../common';
10
+ import { circleActions } from '../../webapi';
24
11
 
25
12
  class GroupDetails extends Component {
26
- state = {
27
- group: {},
28
- showingAction: false,
29
- loading: false,
30
- defaultImage: null,
13
+ constructor(props) {
14
+ super(props);
31
15
 
32
- yOffset: new Animated.Value(0),
33
- };
16
+ this.state = {
17
+ membersExpanded: true,
18
+ files: [],
19
+ images: [],
20
+ };
21
+ }
34
22
 
35
- UNSAFE_componentWillMount() {
36
- this.setState({ group: this.props.group });
37
- const defaultImage = getNewEventDefaults()[Math.floor(Math.random() * getNewEventDefaults().length)];
38
- this.setState({ defaultImage });
23
+ componentDidMount() {
24
+ this.getFiles();
39
25
  }
40
26
 
27
+ getFiles = () => {
28
+ circleActions.getFiles(this.props.group.Id).then(res => {
29
+ this.setState({
30
+ files: res.data,
31
+ });
32
+ });
33
+ circleActions.getImages(this.props.group.Id).then(res => {
34
+ this.setState({
35
+ images: res.data,
36
+ });
37
+ });
38
+ };
39
+
41
40
  onPressBack() {
42
41
  if (this.props.back) {
43
42
  NavigationService.goBack(this.props.back);
@@ -46,6 +45,63 @@ class GroupDetails extends Component {
46
45
  }
47
46
  }
48
47
 
48
+ toggleMembers = () => {
49
+ this.setState({
50
+ membersExpanded: !this.state.membersExpanded,
51
+ });
52
+ };
53
+
54
+ toggleInvites = () => {
55
+ this.setState({
56
+ invitesExpanded: !this.state.invitesExpanded,
57
+ });
58
+ };
59
+
60
+ toggleImages = () => {
61
+ this.setState({
62
+ imagesExpanded: !this.state.imagesExpanded,
63
+ });
64
+ };
65
+
66
+ toggleFiles = () => {
67
+ this.setState({
68
+ filesExpanded: !this.state.filesExpanded,
69
+ });
70
+ };
71
+
72
+ onOpenAttachment = a => {
73
+ this.setState({
74
+ selectedPDF: a,
75
+ });
76
+ };
77
+
78
+ onCloseAttachment = () => {
79
+ this.setState({
80
+ selectedPDF: null,
81
+ });
82
+ };
83
+
84
+ onPressUser = user => {
85
+ circleActions.getDirect(getSite(this.props.user.site), user.userId).then(res => {
86
+ NavigationService.goBack();
87
+ NavigationService.goBack();
88
+ NavigationService.navigate('circleChat', { chat: res.data });
89
+ });
90
+ };
91
+
92
+ openGallery = index => {
93
+ this.imagePopup.scrollTo(index);
94
+ this.setState({
95
+ galleryOpen: true,
96
+ });
97
+ };
98
+
99
+ closeGallery = () => {
100
+ this.setState({
101
+ galleryOpen: false,
102
+ });
103
+ };
104
+
49
105
  getShortName(name) {
50
106
  if (!name) {
51
107
  return name;
@@ -54,115 +110,157 @@ class GroupDetails extends Component {
54
110
  return names && names.length > 0 ? `${names[0]}${names.length > 1 ? names[names.length - 1] : ''}` : '';
55
111
  }
56
112
 
57
- getGroupImage() {
58
- if (!_.isUndefined(this.state.group.Image) && !_.isEmpty(this.state.group.Image)) {
59
- return this.state.group.Image;
60
- }
61
- return this.state.defaultImage;
62
- }
63
-
64
113
  renderPeople(isPending) {
65
- // const source = _.filter([...this.state.group.Audience, ...this.state.group.Invites], item => {
66
- // return item != null && item.userId !== this.props.user.uid;
67
- // });
68
- let source = [...this.state.group.Audience];
114
+ let source = [...this.props.group.Audience];
69
115
  if (isPending) {
70
- source = [...this.state.group.Invites];
116
+ source = [...this.props.group.Invites];
71
117
  }
72
118
  if (_.isEmpty(source)) {
73
119
  return null;
74
120
  }
121
+ const isExpanded = isPending ? this.state.invitesExpanded : this.state.membersExpanded;
75
122
  return (
76
- <View style={[{ paddingTop: 8, paddingBottom: 16 }, !isPending && { borderTopColor: LINEGREY, borderTopWidth: 1 }]}>
77
- <Text style={[styles.normalText, { fontFamily: 'sf-semibold' }]}>
78
- {isPending ? `Invited (${source.length})` : `Members (${source.length})`}
79
- </Text>
80
- {source.map((user, index) => {
81
- return (
82
- <View key={user.userId} style={{ flexDirection: 'row', position: 'relative', marginTop: 8, alignItems: 'center' }}>
83
- <ProfilePic Diameter={40} ProfilePic={user.profilePic} />
84
- <Text
85
- numberOfLines={1}
86
- style={[styles.normalText, { fontFamily: 'sf-semibold', marginTop: 0, marginLeft: 14, lineHeight: 16 }]}
87
- >
88
- {user.displayName}
89
- </Text>
90
- </View>
91
- );
92
- })}
123
+ <View style={styles.section}>
124
+ <View style={styles.sectionHeadingContainer}>
125
+ <TouchableOpacity
126
+ onPress={isPending ? this.toggleInvites : this.toggleMembers}
127
+ hitSlop={{ top: 16, left: 16, right: 16, bottom: 16 }}
128
+ >
129
+ <Icon name={`chevron-${isExpanded ? 'up' : 'down'}`} type="font-awesome" iconStyle={styles.sectionCaret} />
130
+ </TouchableOpacity>
131
+ <View style={{ flex: 1 }}>
132
+ <TextStyle type="detailLabel">{isPending ? `Invited (${source.length})` : `Members (${source.length})`}</TextStyle>
133
+ </View>
134
+ </View>
135
+ {isExpanded &&
136
+ source.map(user => {
137
+ return (
138
+ <UserListing
139
+ user={user}
140
+ key={user.userId}
141
+ onPress={() => {
142
+ this.onPressUser(user);
143
+ }}
144
+ />
145
+ );
146
+ })}
93
147
  </View>
94
148
  );
95
149
  }
96
150
 
97
- render() {
98
- const onScroll = Animated.event([{ nativeEvent: { contentOffset: { y: this.state.yOffset } } }], { useNativeDriver: false });
151
+ renderImages() {
152
+ return (
153
+ <View style={styles.section}>
154
+ <View style={styles.sectionHeadingContainer}>
155
+ <TouchableOpacity onPress={this.toggleImages} hitSlop={{ top: 16, left: 16, right: 16, bottom: 16 }}>
156
+ <Icon name={`chevron-${this.state.imagesExpanded ? 'up' : 'down'}`} type="font-awesome" iconStyle={styles.sectionCaret} />
157
+ </TouchableOpacity>
158
+ <View style={{ flex: 1 }}>
159
+ <TextStyle type="detailLabel">{`Images (${this.state.images.length})`}</TextStyle>
160
+ </View>
161
+ </View>
162
+ {this.state.imagesExpanded && (
163
+ <View style={styles.imageContainer}>
164
+ {this.state.images.map((image, i) => {
165
+ return (
166
+ <TouchableOpacity
167
+ onPress={() => {
168
+ this.openGallery(i);
169
+ }}
170
+ >
171
+ <Image source={{ uri: image.Url }} style={styles.image} />
172
+ </TouchableOpacity>
173
+ );
174
+ })}
175
+ </View>
176
+ )}
177
+ </View>
178
+ );
179
+ }
99
180
 
100
- const headerShow = this.state.yOffset.interpolate({
101
- inputRange: [CARD_TO_FIX - 1, CARD_TO_FIX],
102
- outputRange: [0, 1],
103
- extrapolate: 'clamp',
104
- });
181
+ renderFiles() {
182
+ return (
183
+ <View style={styles.section}>
184
+ <View style={styles.sectionHeadingContainer}>
185
+ <TouchableOpacity onPress={this.toggleFiles} hitSlop={{ top: 16, left: 16, right: 16, bottom: 16 }}>
186
+ <Icon name={`chevron-${this.state.filesExpanded ? 'up' : 'down'}`} type="font-awesome" iconStyle={styles.sectionCaret} />
187
+ </TouchableOpacity>
188
+ <View style={{ flex: 1 }}>
189
+ <TextStyle type="detailLabel">{`Files (${this.state.files.length})`}</TextStyle>
190
+ </View>
191
+ </View>
192
+ {this.state.filesExpanded && (
193
+ <View style={styles.fileContainer}>
194
+ {this.state.files.map((file, i) => {
195
+ return (
196
+ <Attachment
197
+ onPress={() => {
198
+ this.onOpenAttachment(file.Url);
199
+ }}
200
+ key={i}
201
+ title={getFileName(file.Url)}
202
+ />
203
+ );
204
+ })}
205
+ </View>
206
+ )}
207
+ </View>
208
+ );
209
+ }
105
210
 
106
- if (_.isEmpty(this.state.group)) {
211
+ renderImagePopup() {
212
+ return (
213
+ <ImagePopup
214
+ visible={this.state.galleryOpen}
215
+ images={this.state.images.map(img => {
216
+ return img.Url;
217
+ })}
218
+ onClose={this.closeGallery}
219
+ ref={ref => {
220
+ this.imagePopup = ref;
221
+ }}
222
+ />
223
+ );
224
+ }
225
+
226
+ renderPDF() {
227
+ if (_.isEmpty(this.state.selectedPDF)) {
228
+ return null;
229
+ }
230
+ return (
231
+ <PDFPopup source={this.state.selectedPDF} onClose={this.onCloseAttachment} title={getFileName(this.state.selectedPDF)} pdfCount={1} />
232
+ );
233
+ }
234
+
235
+ render() {
236
+ if (_.isEmpty(this.props.group)) {
107
237
  return null;
108
238
  }
109
239
  return (
110
240
  <View style={{ position: 'relative', flex: 1, backgroundColor: '#fff' }}>
111
- {/* Top Back Button */}
112
- <View style={styles.backButton}>
113
- <View style={styles.headerContent}>
114
- <BackButton
115
- onPress={this.onPressBack.bind(this)}
116
- style={{
117
- marginLeft: 16,
118
- }}
119
- />
120
- </View>
121
- </View>
122
- <Animated.View style={[styles.secondHeader, { opacity: headerShow }]}>
123
- <ImageBackground
124
- Id="image"
125
- style={[styles.image, { backgroundColor: TEXT_BLUEGREY }]}
126
- key={this.state.group.Id}
127
- source={getImageSource(this.getGroupImage())}
128
- />
129
- </Animated.View>
130
- <ScrollView style={styles.scroll} scrollEventThrottle={16} onScroll={onScroll} keyboardShouldPersistTaps="always">
131
- <View style={styles.mainHeader}>
132
- <ImageBackground
133
- Id="image"
134
- style={[styles.image, { backgroundColor: TEXT_BLUEGREY }]}
135
- key={this.state.group.Id}
136
- source={getImageSource(this.getGroupImage())}
137
- >
138
- <LinearGradient
139
- style={styles.gradientOverlay}
140
- colors={['rgba(0,0,0,0.7)', COLOUR_TRANSPARENT]}
141
- start={{ x: 0, y: 0 }}
142
- end={{ x: 0, y: 1 }}
143
- locations={[0, 0.5]}
144
- />
145
- </ImageBackground>
146
- </View>
147
- <View style={{ paddingHorizontal: 16, paddingBottom: 16 }}>
148
- <View style={styles.titleContainer}>
149
- <Text style={styles.eventTitle}>{this.state.group.Title}</Text>
150
- <View style={{ flexDirection: 'row', marginTop: 2, paddingBottom: 8 }}>
151
- <Text style={[styles.eventTitle, { fontFamily: 'sf-semibold', fontSize: 14 }]}>Created on</Text>
152
- <Text style={[styles.eventTitle, { marginLeft: 24, fontSize: 14, fontFamily: 'sf-regular' }]}>
153
- {moment(this.state.group.CreatedTime).format('MMMM Do, YYYY')}
154
- </Text>
155
- </View>
241
+ {this.renderPDF()}
242
+ {this.renderImagePopup()}
243
+ <Header leftIcon="angle-left" onPressLeft={this.onPressBack.bind(this)} text={this.props.group.Title} />
244
+ <ScrollView style={styles.scroll} scrollEventThrottle={16} keyboardShouldPersistTaps="always">
245
+ <View style={styles.titleContainer}>
246
+ <Text style={styles.eventTitle}>{this.props.group.Title}</Text>
247
+ <View style={{ flexDirection: 'row', marginTop: 2, paddingBottom: 8 }}>
248
+ <Text style={[styles.eventTitle, { fontFamily: 'sf-semibold', fontSize: 14 }]}>Created on</Text>
249
+ <Text style={[styles.eventTitle, { marginLeft: 24, fontSize: 14, fontFamily: 'sf-regular' }]}>
250
+ {moment(this.props.group.CreatedTime).format('MMMM Do, YYYY')}
251
+ </Text>
156
252
  </View>
157
- {!_.isEmpty(this.state.group.Description) && (
158
- <View style={{ borderTopColor: LINEGREY, borderTopWidth: 1, paddingTop: 8, paddingBottom: 16 }}>
159
- <Text style={[styles.normalText, { fontFamily: 'sf-semibold' }]}>Description</Text>
160
- <Text style={[styles.normalText]}>{this.state.group.Description}</Text>
161
- </View>
162
- )}
163
- {this.renderPeople()}
164
- {this.renderPeople(true)}
165
253
  </View>
254
+ {!_.isEmpty(this.props.group.Description) && (
255
+ <View style={{ borderTopColor: LINEGREY, borderTopWidth: 1, paddingTop: 8, paddingBottom: 16 }}>
256
+ <Text style={[styles.normalText, { fontFamily: 'sf-semibold' }]}>Description</Text>
257
+ <Text style={[styles.normalText]}>{this.props.group.Description}</Text>
258
+ </View>
259
+ )}
260
+ {this.renderPeople()}
261
+ {this.renderPeople(true)}
262
+ {this.renderImages()}
263
+ {this.renderFiles()}
166
264
  </ScrollView>
167
265
  </View>
168
266
  );
@@ -170,63 +268,13 @@ class GroupDetails extends Component {
170
268
  }
171
269
 
172
270
  const styles = {
173
- secondHeader: {
174
- position: 'absolute',
175
- height: CARD_IMAGE_HEIGHT,
176
- width: SCREEN_WIDTH,
177
- top: -CARD_TO_FIX,
178
- backgroundColor: 'green',
179
- elevation: 0,
180
- zIndex: 2,
181
- opacity: 0,
182
- },
183
- backButton: {
184
- position: 'absolute',
185
- left: 0,
186
- top: 0,
187
- zIndex: 10,
188
- elevation: 10,
189
- height: StatusBarHeight(70),
190
- },
191
- headerContent: {
192
- alignSelf: 'stretch',
193
- flexDirection: 'row',
194
- flex: 1,
195
- paddingTop: StatusBarHeight(12),
196
- },
197
- popupContainer: {
198
- borderRadius: 0,
199
- backgroundColor: '#fff',
200
- width: SCREEN_WIDTH,
201
- flex: 1,
202
- },
203
271
  scroll: {
204
272
  flex: 1,
205
273
  backgroundColor: '#fff',
206
274
  },
207
- buttonContainer: {
208
- paddingBottom: 16,
209
- paddingHorizontal: 16,
210
- },
211
- mainHeader: {
212
- position: 'relative',
213
- height: CARD_IMAGE_HEIGHT,
214
- width: SCREEN_WIDTH,
215
- },
216
- gradientOverlay: {
217
- flex: 1,
218
- },
219
- image: {
220
- backgroundColor: '#fff',
221
- position: 'relative',
222
- width: undefined,
223
- height: undefined,
224
- flex: 1,
225
- },
226
275
  titleContainer: {
227
- /* flex: 1,
228
- flexDirection: 'row-reverse', */
229
276
  paddingTop: 20,
277
+ paddingHorizontal: 16,
230
278
  },
231
279
  eventTitle: {
232
280
  fontFamily: 'sf-bold',
@@ -243,6 +291,37 @@ const styles = {
243
291
  marginTop: 6,
244
292
  backgroundColor: 'transparent',
245
293
  },
294
+ section: {
295
+ paddingTop: 16,
296
+ paddingBottom: 8,
297
+ borderTopColor: LINEGREY,
298
+ borderTopWidth: 1,
299
+ },
300
+ sectionHeadingContainer: {
301
+ flexDirection: 'row-reverse',
302
+ alignItems: 'center',
303
+ paddingHorizontal: 16,
304
+ paddingBottom: 8,
305
+ },
306
+ sectionCaret: {
307
+ color: COLOUR_DUSK,
308
+ fontSize: 16,
309
+ },
310
+ fileContainer: {
311
+ paddingHorizontal: 16,
312
+ },
313
+ imageContainer: {
314
+ flexDirection: 'row',
315
+ flexWrap: 'wrap',
316
+ paddingLeft: 16,
317
+ paddingRight: 8,
318
+ },
319
+ image: {
320
+ marginRight: 8,
321
+ marginBottom: 8,
322
+ height: 80,
323
+ width: 80,
324
+ },
246
325
  };
247
326
 
248
327
  const mapStateToProps = state => {
@@ -1,6 +1,6 @@
1
1
  import _ from 'lodash';
2
- // import * as PlussCore from '../../pluss-core/src';
3
- import * as PlussCore from '@plusscommunities/pluss-core-app';
2
+ import * as PlussCore from '../../pluss-core/src';
3
+ // import * as PlussCore from '@plusscommunities/pluss-core-app';
4
4
  import { CIRCLE_INVITES_LOADED, CIRCLES_LOAD_INITIALISE, UPDATE_CIRCLE_UNREAD } from './actions/types';
5
5
  import { getSessionTokenAWS } from './js';
6
6
  import { circleActions } from './webapi';
package/src/js/Colors.js CHANGED
@@ -16,3 +16,4 @@ export const COLOUR_TRANSPARENT = Colours.COLOUR_TRANSPARENT;
16
16
  export const COLOUR_TEAL = Colours.COLOUR_TEAL;
17
17
  export const COLOUR_PURPLE = Colours.COLOUR_PURPLE;
18
18
  export const COLOUR_TANGERINE = Colours.COLOUR_TANGERINE;
19
+ export const COLOUR_DUSK = Colours.COLOUR_DUSK;
package/src/js/index.js CHANGED
@@ -9,6 +9,7 @@ export const getSessionUidAWS = Session.getSessionUidAWS;
9
9
 
10
10
  // Helper
11
11
  export const getFirstName = Helper.getFirstName;
12
+ export const getFileName = Helper.getFileName;
12
13
  export const getImageSource = Helper.getImageSource;
13
14
  export const StatusBarHeight = Helper.StatusBarHeight;
14
15
  export const getPluralS = Helper.getPluralS;
@@ -35,14 +35,13 @@ export default (state = INITIAL_STATE, action) => {
35
35
  return { ...state, unreadCount: action.payload };
36
36
  }
37
37
  case CLEAR_SINGLE_CIRCLE_UNREAD: {
38
- let growps = [...state.list];
38
+ const growps = [...state.list];
39
39
  let newCount = state.unreadCount;
40
- if (action.payload.isPrivate) {
41
- growps = [...state.privateList];
42
- newCount = state.privateCount;
43
- }
44
40
  if (!_.isEmpty(growps)) {
45
41
  growps.forEach(g => {
42
+ if (!g.Unread) {
43
+ g.Unread = {};
44
+ }
46
45
  if (g.Id === action.payload.groupId) {
47
46
  g.Unread[action.payload.userId] = 0;
48
47
  }
@@ -55,9 +54,6 @@ export default (state = INITIAL_STATE, action) => {
55
54
  });
56
55
  newCount = counter;
57
56
  }
58
- if (action.payload.isPrivate) {
59
- return { ...state, privateList: growps, privateCount: newCount };
60
- }
61
57
  return { ...state, list: growps, unreadCount: newCount };
62
58
  }
63
59
  case CIRCLE_INVITES_LOADED:
@@ -94,10 +90,7 @@ export default (state = INITIAL_STATE, action) => {
94
90
  return {
95
91
  ...state,
96
92
  list: _.filter(action.payload, group => {
97
- return group != null && !group.Deleted && !group.IsPrivate;
98
- }),
99
- privateList: _.filter(action.payload, group => {
100
- return group != null && !group.Deleted && group.IsPrivate;
93
+ return group != null && !group.Deleted;
101
94
  }),
102
95
  };
103
96
  case CIRCLES_LOADED:
@@ -117,38 +110,26 @@ export default (state = INITIAL_STATE, action) => {
117
110
  return {
118
111
  ...state,
119
112
  list: _.filter(pResult, group => {
120
- return group != null && !group.Deleted && !group.IsPrivate;
121
- }),
122
- privateList: _.filter(pResult, group => {
123
- return group != null && !group.Deleted && group.IsPrivate;
113
+ return group != null && !group.Deleted;
124
114
  }),
125
115
  };
126
116
  case CREATED_CIRCLE:
127
- const zzzz = action.payload && action.payload.IsPrivate ? [...state.privateList] : [...state.list];
117
+ const zzzz = [...state.list];
128
118
  zzzz.push(action.payload);
129
- if (action.payload.IsPrivate) {
130
- return { ...state, privateList: zzzz };
131
- }
132
119
  return { ...state, list: zzzz };
133
120
  case CLEAR_CIRCLES:
134
- return { list: [], privateList: [], invites: [] };
121
+ return { list: [], invites: [] };
135
122
  case REHYDRATE:
136
123
  if (action.payload[GROUPS_REDUCER_KEY]) {
137
124
  if (action.payload[GROUPS_REDUCER_KEY].list == null) {
138
125
  action.payload[GROUPS_REDUCER_KEY].list = [];
139
126
  }
140
- if (action.payload[GROUPS_REDUCER_KEY].privateList == null) {
141
- action.payload[GROUPS_REDUCER_KEY].privateList = [];
142
- }
143
127
  if (action.payload[GROUPS_REDUCER_KEY].invites == null) {
144
128
  action.payload[GROUPS_REDUCER_KEY].invites = [];
145
129
  }
146
130
  if (action.payload[GROUPS_REDUCER_KEY].unreadCount == null) {
147
131
  action.payload[GROUPS_REDUCER_KEY].unreadCount = 0;
148
132
  }
149
- if (action.payload[GROUPS_REDUCER_KEY].privateCount == null) {
150
- action.payload[GROUPS_REDUCER_KEY].privateCount = 0;
151
- }
152
133
  /* fixSelectedTimes(action.payload[GROUPS_REDUCER_KEY].list);
153
134
  fixRepeatedTimes(action.payload[GROUPS_REDUCER_KEY].list); */
154
135
  return action.payload[GROUPS_REDUCER_KEY];
@@ -14,6 +14,24 @@ export default {
14
14
  },
15
15
  });
16
16
  },
17
+ getDirect: (site, userId) => {
18
+ return authedFunction({
19
+ method: 'GET',
20
+ url: getUrl('circles', 'get/direct', { site, userId }),
21
+ });
22
+ },
23
+ getFiles: circleId => {
24
+ return authedFunction({
25
+ method: 'GET',
26
+ url: getUrl('circles', 'get/files', { circleId }),
27
+ });
28
+ },
29
+ getImages: circleId => {
30
+ return authedFunction({
31
+ method: 'GET',
32
+ url: getUrl('circles', 'get/images', { circleId }),
33
+ });
34
+ },
17
35
  clearGroupUnread: async (groupID, userID) => {
18
36
  return authedFunction({
19
37
  method: 'POST',