@plusscommunities/pluss-circles-web 1.0.3 → 1.0.8

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,14 +1,15 @@
1
1
  {
2
2
  "name": "@plusscommunities/pluss-circles-web",
3
- "version": "1.0.3",
3
+ "version": "1.0.8",
4
4
  "description": "Extension package to enable circles on Pluss Communities Platform",
5
5
  "main": "dist/index.cjs.js",
6
6
  "scripts": {
7
- "build": "rollup -c",
7
+ "build": "npm i && rollup -c",
8
8
  "core": "npm i @plusscommunities/pluss-core-web@latest",
9
- "upload": "npm version patch && npm publish --access public",
10
- "deploy": "npm run core && npm run build && npm run upload",
11
- "test": "echo \"Error: no test specified\" && exit 1"
9
+ "patch": "npm version patch",
10
+ "upload": "npm run build && npm publish --access public && rm -rf node_modules",
11
+ "upload:p": "npm run patch && npm run upload",
12
+ "upload:cp": "npm run core && npm run patch && npm run upload"
12
13
  },
13
14
  "author": "Phillip Suh",
14
15
  "license": "ISC",
@@ -28,7 +29,7 @@
28
29
  },
29
30
  "dependencies": {
30
31
  "@babel/runtime": "^7.14.0",
31
- "@plusscommunities/pluss-core-web": "^1.0.4",
32
+ "@plusscommunities/pluss-core-web": "^1.2.5",
32
33
  "lodash": "^4.17.4",
33
34
  "moment": "^2.18.1",
34
35
  "react": "^16.14.0",
package/rollup.config.js CHANGED
@@ -38,6 +38,7 @@ MODE.map((m) => {
38
38
  // these are babel comfigurations
39
39
  babel({
40
40
  exclude: 'node_modules/**',
41
+ presets: ['@babel/preset-react', '@babel/preset-env'],
41
42
  plugins: ['@babel/transform-runtime'],
42
43
  babelHelpers: 'runtime',
43
44
  }),
@@ -0,0 +1,16 @@
1
+ import _ from 'lodash';
2
+ import { CIRCLES_LOADED, CIRCLE_UPDATED } from './types';
3
+
4
+ export const circlesLoaded = (circles) => {
5
+ return {
6
+ type: CIRCLES_LOADED,
7
+ payload: circles,
8
+ };
9
+ };
10
+
11
+ export const circleUpdated = (circle) => {
12
+ return {
13
+ type: CIRCLE_UPDATED,
14
+ payload: circle,
15
+ };
16
+ };
@@ -0,0 +1 @@
1
+ export * from './CirclesActions';
@@ -0,0 +1,2 @@
1
+ export const CIRCLES_LOADED = 'CIRCLES_LOADED';
2
+ export const CIRCLE_UPDATED = 'CIRCLE_UPDATED';
@@ -0,0 +1,94 @@
1
+ import _ from 'lodash';
2
+ import { PlussCore } from '../feature.config';
3
+ const { Helper, Session } = PlussCore;
4
+
5
+ export const circleActions = {
6
+ getAll: (site) => {
7
+ return Session.authedFunction({
8
+ method: 'GET',
9
+ url: Helper.getUrl('circles', 'get/all', { site }),
10
+ });
11
+ },
12
+ getFiles: (circleId) => {
13
+ return Session.authedFunction({
14
+ method: 'GET',
15
+ url: Helper.getUrl('circles', 'get/files', { circleId }),
16
+ });
17
+ },
18
+ getImages: (circleId) => {
19
+ return Session.authedFunction({
20
+ method: 'GET',
21
+ url: Helper.getUrl('circles', 'get/images', { circleId }),
22
+ });
23
+ },
24
+ add: (site, title, image, audience) => {
25
+ return Session.authedFunction({
26
+ method: 'POST',
27
+ url: Helper.getUrl('circles', 'update/add'),
28
+ data: {
29
+ site,
30
+ title,
31
+ image,
32
+ audience,
33
+ },
34
+ });
35
+ },
36
+ edit: (circleId, title, image, audience) => {
37
+ return Session.authedFunction({
38
+ method: 'POST',
39
+ url: Helper.getUrl('circles', 'update/edit'),
40
+ data: {
41
+ circleId,
42
+ title,
43
+ image,
44
+ audience,
45
+ },
46
+ });
47
+ },
48
+ addAdmin: (circleId, userId) => {
49
+ return Session.authedFunction({
50
+ method: 'POST',
51
+ url: Helper.getUrl('circles', 'update/addadmin'),
52
+ data: {
53
+ circleId,
54
+ userId,
55
+ },
56
+ });
57
+ },
58
+ removeAdmin: (circleId, userId) => {
59
+ return Session.authedFunction({
60
+ method: 'POST',
61
+ url: Helper.getUrl('circles', 'update/removeadmin'),
62
+ data: {
63
+ circleId,
64
+ userId,
65
+ },
66
+ });
67
+ },
68
+ getMessages: async (groupId, limit, minTime, maxTime) => {
69
+ const query = { groupId };
70
+ if (!_.isUndefined(minTime)) {
71
+ query.minTime = minTime;
72
+ }
73
+ if (!_.isUndefined(maxTime)) {
74
+ query.maxTime = maxTime;
75
+ }
76
+ if (limit) {
77
+ query.limit = limit;
78
+ }
79
+ return Session.authedFunction({
80
+ method: 'GET',
81
+ url: Helper.getUrl('circles', 'getMessages', query),
82
+ });
83
+ },
84
+ sendMessage: async (circleId, message) => {
85
+ return Session.authedFunction({
86
+ method: 'post',
87
+ url: Helper.getUrl('circles', 'sendMessage'),
88
+ data: {
89
+ groupId: circleId,
90
+ message,
91
+ },
92
+ });
93
+ },
94
+ };
@@ -0,0 +1 @@
1
+ export * from './circleActions';
@@ -5,8 +5,36 @@ export { PlussCore };
5
5
 
6
6
  const FeatureConfig = {
7
7
  key: 'circles',
8
+ singularName: 'Circle',
8
9
  description: 'App users can create circles and message each other.',
9
10
  emptyText: "You aren't in any Circles",
11
+ widgetOptions: [],
12
+ menu: {
13
+ order: 11,
14
+ text: 'Circles',
15
+ icon: 'circle-o',
16
+ isFontAwesome: true,
17
+ url: '/circles',
18
+ countProps: null,
19
+ visibleExps: {
20
+ type: 'feature',
21
+ value: 'circles',
22
+ },
23
+ },
24
+ addUrl: '/circles/add',
25
+ addPermission: 'circles',
26
+ permissions: [
27
+ {
28
+ displayName: 'Manage and View Circles',
29
+ key: 'circles',
30
+ },
31
+ ],
32
+ routes: [
33
+ { path: '/circles', component: 'Circles', exact: false },
34
+ { path: '/circles/add', component: 'AddCircle', exact: true },
35
+ { path: '/circles/circle/:circleId', component: 'Circle', exact: true },
36
+ { path: '/circles/edit/:circleId', component: 'AddCircle', exact: true },
37
+ ],
10
38
  env: {
11
39
  baseStage: '',
12
40
  baseAPIUrl: '',
@@ -14,6 +42,15 @@ const FeatureConfig = {
14
42
  uploadBucket: '',
15
43
  colourBrandingMain: '',
16
44
  colourBrandingOff: '',
45
+ colourBrandingApp: '',
46
+ defaultProfileImage: '',
47
+ utcOffset: '',
48
+ hasAvailableNews: false,
49
+ newsHaveTags: true,
50
+ defaultAllowComments: true,
51
+ makeApiKey: '',
52
+ logo: '',
53
+ clientName: '',
17
54
  },
18
55
  init: (environment) => {
19
56
  FeatureConfig.env = environment;
package/src/index.js CHANGED
@@ -1,3 +1,10 @@
1
+ import Circles from './screens/Circles';
2
+ import AddCircle from './screens/AddCircle';
3
+ import Circle from './screens/Circle';
4
+ import CircleReducer from './reducers/CircleReducer';
5
+
6
+ export const Reducers = { circles: CircleReducer };
7
+ export const Screens = { Circles, AddCircle, Circle };
1
8
  export { default as Config } from './feature.config';
2
9
  export { default as ViewWidget } from './components/ViewWidget';
3
10
  export { default as ViewFull } from './components/ViewFull';
@@ -0,0 +1,17 @@
1
+ import _ from 'lodash';
2
+ import { CIRCLES_LOADED, CIRCLE_UPDATED } from '../actions/types';
3
+
4
+ const INITIAL_STATE = {
5
+ circles: [],
6
+ };
7
+
8
+ export default (state = INITIAL_STATE, action) => {
9
+ switch (action.type) {
10
+ case CIRCLES_LOADED:
11
+ return { ...state, circles: action.payload };
12
+ case CIRCLE_UPDATED:
13
+ return { ...state, circles: _.uniqBy([action.payload, ...state.circles], (c) => c.Id) };
14
+ default:
15
+ return state;
16
+ }
17
+ };
@@ -0,0 +1,429 @@
1
+ import React, { Component } from 'react';
2
+ import { withRouter } from 'react-router';
3
+ import _ from 'lodash';
4
+ import { connect } from 'react-redux';
5
+ import { PlussCore } from '../feature.config';
6
+ import { circleActions } from '../apis';
7
+ import { circlesLoaded, circleUpdated } from '../actions';
8
+
9
+ const { Actions, Components, Session, Apis, Colours, Helper } = PlussCore;
10
+
11
+ class AddCircle extends Component {
12
+ constructor(props) {
13
+ super(props);
14
+
15
+ const circleId = Helper.safeReadParams(props, 'circleId');
16
+ this.state = {
17
+ circleId,
18
+ circle: _.find(props.circles, (c) => {
19
+ return c.Id === circleId;
20
+ }),
21
+ title: '',
22
+ showWarnings: false,
23
+ success: false,
24
+ users: [],
25
+ selectedUsers: [],
26
+ userSearch: '',
27
+ };
28
+ }
29
+
30
+ componentDidMount() {
31
+ Session.checkLoggedIn(this, this.props.auth);
32
+ this.props.addRecentlyCreated('circle');
33
+ this.getUsers();
34
+
35
+ this.checkGetCircle();
36
+ }
37
+
38
+ getUsers = () => {
39
+ Apis.userActions
40
+ .fetchUsers(this.props.auth.site)
41
+ .then((res) => {
42
+ this.setState({
43
+ loadingAll: false,
44
+ });
45
+ if (res.userFetchFail) {
46
+ return;
47
+ }
48
+ if (res.data != null && !_.isEmpty(res.data.results.Items)) {
49
+ this.setState({
50
+ users: _.sortBy(res.data.results.Items, (u) => {
51
+ return (u.displayName || '').toLowerCase();
52
+ }),
53
+ });
54
+ }
55
+ })
56
+ .catch((error) => {
57
+ this.setState({ loadingAll: false });
58
+ });
59
+ };
60
+
61
+ getShownUsers = () => {
62
+ return _.filter(this.state.users, (u) => {
63
+ if (
64
+ _.some(this.state.selectedUsers, (selectedUser) => {
65
+ return u.userId === selectedUser.userId;
66
+ })
67
+ ) {
68
+ return false;
69
+ }
70
+ if (!_.isEmpty(this.state.userSearch)) {
71
+ return (u.displayName || '').toLowerCase().indexOf(this.state.userSearch.toLowerCase()) > -1;
72
+ }
73
+ return true;
74
+ });
75
+ };
76
+
77
+ parseCircle = (circle) => {
78
+ const selectedUsers = circle.Audience.map((user) => {
79
+ return {
80
+ userId: user.userId,
81
+ profilePic: user.profilePic,
82
+ displayName: user.displayName,
83
+ isAdmin: user.isAdmin,
84
+ };
85
+ });
86
+ const newState = {
87
+ title: circle.Title,
88
+ selectedUsers,
89
+ };
90
+ this.setState(newState);
91
+
92
+ if (!_.isEmpty(circle.Image)) {
93
+ this.checkSetImage(circle.Image);
94
+ }
95
+ };
96
+
97
+ checkSetImage(url) {
98
+ if (this.imageInput) {
99
+ this.setState({
100
+ image: url,
101
+ });
102
+ this.imageInput.getWrappedInstance().setValue(url);
103
+ } else {
104
+ setTimeout(() => {
105
+ this.checkSetImage(url);
106
+ }, 100);
107
+ }
108
+ }
109
+
110
+ checkGetCircle = () => {
111
+ if (!this.state.circleId) {
112
+ return;
113
+ }
114
+ if (this.state.circle) {
115
+ return this.parseCircle(this.state.circle);
116
+ }
117
+ const { auth } = this.props;
118
+ this.setState({ loadingAll: true }, async () => {
119
+ try {
120
+ const res = await circleActions.getAll(auth.site);
121
+ console.log('getData', res.data);
122
+ const circle = _.find(res.data, (c) => {
123
+ return c.Id === this.state.circleId;
124
+ });
125
+
126
+ this.props.circlesLoaded(res.data);
127
+ this.setState({ loadingAll: false, circle });
128
+ this.parseCircle(circle);
129
+ } catch (error) {
130
+ console.error('getData', error);
131
+ this.setState({ loadingAll: false });
132
+ }
133
+ });
134
+ };
135
+
136
+ onHandleChange = (event) => {
137
+ var stateChange = {};
138
+ stateChange[event.target.getAttribute('id')] = event.target.value;
139
+ this.setState(stateChange);
140
+ };
141
+
142
+ onImageUpdated = (url) => {
143
+ this.setState({
144
+ image: url,
145
+ });
146
+ };
147
+
148
+ toggleSelf = () => {
149
+ this.setState({
150
+ includeSelf: !this.state.includeSelf,
151
+ });
152
+ };
153
+
154
+ onSelectUser = (user) => {
155
+ const newState = {
156
+ selectedUsers: _.xor(this.state.selectedUsers, [user]),
157
+ };
158
+ if (_.isEmpty(this.state.title) && _.includes(newState.selectedUsers, user)) {
159
+ newState.title = `${user.displayName.split(' ')[0]}'s Circle`;
160
+ }
161
+ this.setState(newState);
162
+ };
163
+
164
+ onSave = () => {
165
+ this.setState({ showWarnings: false });
166
+ if (!this.validateForm()) {
167
+ this.setState({ showWarnings: true });
168
+ return;
169
+ }
170
+ if (this.state.updating) return;
171
+ this.setState({ updating: true });
172
+ const audience = this.state.selectedUsers.map((user) => {
173
+ return {
174
+ userId: user.userId,
175
+ profilePic: user.profilePic,
176
+ displayName: user.displayName,
177
+ isAdmin: user.isAdmin,
178
+ };
179
+ });
180
+
181
+ if (this.state.circleId) {
182
+ circleActions
183
+ .edit(this.state.circleId, this.state.title, this.state.image, audience)
184
+ .then((res) => {
185
+ this.setState({
186
+ success: true,
187
+ updating: false,
188
+ });
189
+ console.log('success');
190
+ this.props.circleUpdated(res.data);
191
+ })
192
+ .catch((err) => {
193
+ console.log('error');
194
+ this.setState({ updating: false });
195
+ alert('Something went wrong with the request. Please try again.');
196
+ });
197
+ } else {
198
+ circleActions
199
+ .add(this.props.auth.site, this.state.title, this.state.image, audience)
200
+ .then((res) => {
201
+ this.setState({
202
+ success: true,
203
+ updating: false,
204
+ });
205
+ console.log('success');
206
+ this.props.circleUpdated(res.data);
207
+ })
208
+ .catch((err) => {
209
+ console.log('error');
210
+ this.setState({ updating: false });
211
+ alert('Something went wrong with the request. Please try again.');
212
+ });
213
+ }
214
+ };
215
+
216
+ getSelectableUsers() {
217
+ return _.filter(this.state.users, (user) => {
218
+ if (
219
+ _.some(this.state.selectedUsers, (selectedUser) => {
220
+ return user.userId === selectedUser.userId;
221
+ })
222
+ ) {
223
+ return false;
224
+ }
225
+ return true;
226
+ });
227
+ }
228
+
229
+ validateForm() {
230
+ if (_.isEmpty(this.state.title)) return false;
231
+ return true;
232
+ }
233
+
234
+ addAdmin = (user) => {
235
+ user.isAdmin = true;
236
+ this.setState({
237
+ selectedUsers: [...this.state.selectedUsers],
238
+ });
239
+ };
240
+
241
+ removeAdmin = (user) => {
242
+ user.isAdmin = false;
243
+ this.setState({
244
+ selectedUsers: [...this.state.selectedUsers],
245
+ });
246
+ };
247
+
248
+ renderSuccess() {
249
+ if (!this.state.success) return null;
250
+ return (
251
+ <Components.SuccessPopup
252
+ text={`Circle has been ${this.state.circleId == null ? 'added' : 'edited'}`}
253
+ buttons={[
254
+ {
255
+ type: 'outlined',
256
+ onClick: () => {
257
+ window.history.back();
258
+ },
259
+ text: `Back to Circles`,
260
+ },
261
+ ]}
262
+ />
263
+ );
264
+ }
265
+
266
+ renderSubmit() {
267
+ if (this.state.updating) {
268
+ return <Components.Button buttonType="secondary">Saving...</Components.Button>;
269
+ }
270
+
271
+ return (
272
+ <div>
273
+ <Components.Button
274
+ inline
275
+ buttonType="tertiary"
276
+ onClick={() => this.props.history.push('/circles')}
277
+ isActive
278
+ style={{ marginRight: 16 }}
279
+ >
280
+ Cancel
281
+ </Components.Button>
282
+ <Components.Button inline buttonType="primary" onClick={this.onSave} isActive={this.validateForm()}>
283
+ Save
284
+ </Components.Button>
285
+ </div>
286
+ );
287
+ }
288
+
289
+ renderMain() {
290
+ return (
291
+ <div style={{ marginBottom: 15 }}>
292
+ <div className="padding-60 paddingVertical-40 bottomDivideBorder">
293
+ <Components.Text type="formTitleLarge" className="marginBottom-24">
294
+ {this.state.circleId == null ? 'New' : 'Edit'} Circle
295
+ </Components.Text>
296
+ {/* Resident Information */}
297
+ <div className="flex flex-reverse">
298
+ <Components.ImageInput
299
+ ref={(ref) => {
300
+ this.imageInput = ref;
301
+ }}
302
+ label="IMAGE"
303
+ limit={1}
304
+ refreshCallback={this.onImageUpdated}
305
+ containerStyle={{ marginLeft: 40 }}
306
+ />
307
+ <div className="flex-1">
308
+ <Components.GenericInput
309
+ id="title"
310
+ type="text"
311
+ label="Title"
312
+ placeholder="Name the Circle"
313
+ value={this.state.title}
314
+ onChange={(e) => this.onHandleChange(e)}
315
+ isRequired
316
+ alwaysShowLabel
317
+ isValid={() => {
318
+ return !_.isEmpty(this.state.title);
319
+ }}
320
+ showError={() => {
321
+ return this.state.showWarnings && _.isEmpty(this.state.title);
322
+ }}
323
+ />
324
+ </div>
325
+ </div>
326
+ </div>
327
+ <div className="padding-60 paddingVertical-40 bottomDivideBorder">
328
+ <Components.Text type="formTitleMedium">Members</Components.Text>
329
+ <div className="flex marginTop-20">
330
+ <div className="flex-1">
331
+ <Components.Text type="formTitleSmall" className="marginBottom-10">
332
+ Select Users
333
+ </Components.Text>
334
+ <Components.GenericInput
335
+ id="userSearch"
336
+ type="text"
337
+ label="Search"
338
+ placeholder="Enter name"
339
+ value={this.state.userSearch}
340
+ onChange={(e) => this.onHandleChange(e)}
341
+ alwaysShowLabel
342
+ />
343
+ {this.getShownUsers().map((user) => {
344
+ return (
345
+ <Components.UserListing
346
+ key={user.userId}
347
+ user={user}
348
+ onClick={() => {
349
+ this.onSelectUser(user);
350
+ }}
351
+ />
352
+ );
353
+ })}
354
+ </div>
355
+ <div className="flex-1">
356
+ <Components.Text type="formTitleSmall" className="marginBottom-10">
357
+ Selected
358
+ </Components.Text>
359
+ {this.state.selectedUsers.map((user) => {
360
+ return (
361
+ <Components.UserListing
362
+ key={user.userId}
363
+ user={user}
364
+ rightContent={
365
+ <div className="flex flex-reverse flex-center">
366
+ <Components.SVGIcon
367
+ className="removeIcon marginLeft-8"
368
+ icon="close"
369
+ colour={Colours.COLOUR_DUSK}
370
+ onClick={() => {
371
+ this.onSelectUser(user);
372
+ }}
373
+ />
374
+ <Components.StatusButton
375
+ isActive={user.isAdmin}
376
+ activate={() => {
377
+ this.addAdmin(user);
378
+ }}
379
+ deactivate={() => {
380
+ this.removeAdmin(user);
381
+ }}
382
+ activeText="Admin"
383
+ activateText="Make Admin"
384
+ deactivateText="Remove Admin"
385
+ inactiveText="Not Admin"
386
+ />
387
+ </div>
388
+ }
389
+ />
390
+ );
391
+ })}
392
+ </div>
393
+ </div>
394
+ </div>
395
+ </div>
396
+ );
397
+ }
398
+
399
+ render() {
400
+ const { success } = this.state;
401
+
402
+ return (
403
+ <Components.OverlayPage>
404
+ <Components.OverlayPageContents noBottomButtons={success}>
405
+ <Components.OverlayPageSection className="pageSectionWrapper--newPopup">
406
+ <div>
407
+ {this.renderSuccess()}
408
+ {!success && this.renderMain()}
409
+ </div>
410
+ </Components.OverlayPageSection>
411
+ </Components.OverlayPageContents>
412
+ <Components.OverlayPageBottomButtons>{this.renderSubmit()}</Components.OverlayPageBottomButtons>
413
+ </Components.OverlayPage>
414
+ );
415
+ }
416
+ }
417
+
418
+ const mapStateToProps = (state) => {
419
+ const { circles } = state.circles;
420
+ const { auth } = state;
421
+ return {
422
+ circles,
423
+ auth,
424
+ };
425
+ };
426
+
427
+ export default connect(mapStateToProps, { circlesLoaded, circleUpdated, addRecentlyCreated: Actions.addRecentlyCreated })(
428
+ withRouter(AddCircle),
429
+ );