@plusscommunities/pluss-circles-web-groups 1.6.2-beta.0 → 1.6.2-beta.2

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.
@@ -1,860 +0,0 @@
1
- import React, { Component } from "react";
2
- import { connect } from "react-redux";
3
- import _ from "lodash";
4
- import { Link } from "react-router-dom";
5
- import { PlussCore } from "../feature.config";
6
- import { circlesLoaded } from "../actions";
7
- import { circleActions } from "../apis";
8
- import { Icon } from "@components";
9
- import moment from "moment";
10
- import { values } from "../values.config";
11
-
12
- const { Components, Helper, Actions, Session, Colours } = PlussCore;
13
-
14
- class Circle extends Component {
15
- constructor(props) {
16
- super(props);
17
- const circleId = Helper.safeReadParams(props, "circleId");
18
- this.state = {
19
- circleId,
20
- circle: _.find(props.circles, (c) => {
21
- return c.Id === circleId;
22
- }),
23
- messageInput: "",
24
- messages: [],
25
- images: [],
26
- files: [],
27
- membersExpanded: true,
28
- replyingTo: null,
29
- showDeleteConfirm: false,
30
- messageToDelete: null,
31
- deletingMessageId: null,
32
- imageInput: null,
33
- fileInput: null,
34
- imageInputShowing: false,
35
- fileInputShowing: false,
36
- };
37
- }
38
-
39
- componentDidMount() {
40
- this.markCircleAsRead();
41
- this.checkGetData();
42
- this.connect();
43
- this.props.setNavData({ hideSideMenu: true });
44
- this.getFiles();
45
- }
46
-
47
- componentWillUnmount() {
48
- this.props.setNavData({ hideSideMenu: false });
49
- this.disconnect();
50
- }
51
-
52
- scrollToBottom() {
53
- if (!this.chat) return;
54
- const scrollHeight = this.chat.scrollHeight;
55
- const height = this.chat.clientHeight;
56
- const maxScrollTop = scrollHeight - height;
57
- this.chat.scrollTop = maxScrollTop > 0 ? maxScrollTop : 0;
58
- }
59
-
60
- getFiles = () => {
61
- circleActions.getFiles(this.state.circleId).then((res) => {
62
- this.setState({
63
- files: res.data,
64
- });
65
- });
66
- circleActions.getImages(this.state.circleId).then((res) => {
67
- this.setState({
68
- images: res.data,
69
- });
70
- });
71
- };
72
-
73
- checkGetData = () => {
74
- if (this.state.circle) {
75
- return;
76
- }
77
- const { auth } = this.props;
78
- this.setState({ loadingAll: true }, async () => {
79
- try {
80
- const res = await circleActions.getAll(auth.site);
81
- console.log("getData", res.data);
82
- const circle = _.find(res.data, (c) => {
83
- return c.Id === this.state.circleId;
84
- });
85
-
86
- this.props.circlesLoaded(res.data);
87
- this.setState({ loadingAll: false, circle });
88
- } catch (error) {
89
- console.error("getData", error);
90
- this.setState({ loadingAll: false });
91
- }
92
- });
93
- };
94
-
95
- mergeMessages(receivedMessages, excludePending) {
96
- const newMessages = _.sortBy(
97
- _.concat(this.state.messages, receivedMessages),
98
- "createdAt",
99
- );
100
-
101
- this.setState({
102
- messages: _.filter(
103
- _.uniqBy(newMessages, (m) => {
104
- return m._id;
105
- }),
106
- (m) => {
107
- return !excludePending || !m.uploading;
108
- },
109
- ),
110
- });
111
- if (!_.isEmpty(receivedMessages)) {
112
- setTimeout(() => {
113
- this.scrollToBottom();
114
- }, 100);
115
- }
116
- }
117
-
118
- connect = () => {
119
- this.getMessages();
120
-
121
- this.interval = setInterval(this.getMessages, 2000);
122
- };
123
-
124
- disconnect = () => {
125
- clearInterval(this.interval);
126
- };
127
-
128
- getMaxTime() {
129
- const maxMessage = _.maxBy(this.state.messages, (m) => {
130
- return m.createdAt;
131
- });
132
- if (maxMessage) {
133
- return maxMessage.createdAt;
134
- }
135
- return 0;
136
- }
137
-
138
- getDateMessages = async (date) => {
139
- const startOf = moment(date, "YYYY-MM-DD").startOf("d");
140
- const endOf = moment(date, "YYYY-MM-DD").endOf("d");
141
- const res = await circleActions.getMessages(
142
- this.state.circleId,
143
- 10000,
144
- startOf.valueOf(),
145
- endOf.valueOf(),
146
- );
147
- this.mergeMessages(res.data);
148
- };
149
-
150
- getMessages = async (excludePending) => {
151
- const res = await circleActions.getMessages(
152
- this.state.circleId,
153
- 50,
154
- this.getMaxTime() + 1,
155
- );
156
-
157
- this.mergeMessages(res.data, excludePending);
158
- };
159
-
160
- getCircle() {
161
- return this.state.circle || { IsPrivate: true };
162
- }
163
-
164
- getTitle = () => {
165
- const { circle } = this.state;
166
- if (!circle) {
167
- return "";
168
- }
169
- if (circle.IsPrivate) {
170
- return `PM: ${circle.Audience.map((user) => {
171
- return user.displayName;
172
- }).join(", ")}`;
173
- }
174
- return circle.Title;
175
- };
176
-
177
- onHandleChange = (event) => {
178
- var stateChange = {};
179
- stateChange[event.target.getAttribute("id")] = event.target.value;
180
- this.setState(stateChange);
181
- };
182
-
183
- onImageUpdated = (image) => {
184
- console.log("updated image");
185
- console.log(image);
186
- this.setState({
187
- imageInput: image,
188
- });
189
- };
190
-
191
- showImageInput = () => {
192
- this.setState({
193
- imageInputShowing: true,
194
- fileInputShowing: false,
195
- });
196
- };
197
-
198
- onFileUpdated = (url) => {
199
- console.log("updated url");
200
- console.log(url);
201
- this.setState({
202
- fileInput: url,
203
- });
204
- };
205
-
206
- showFileInput = () => {
207
- this.setState({
208
- imageInputShowing: false,
209
- fileInputShowing: true,
210
- });
211
- };
212
-
213
- onReply = (m) => {
214
- this.setState({
215
- replyingTo: m,
216
- });
217
- if (m) {
218
- const input = document.getElementById("messageInput");
219
- if (input) input.focus();
220
- }
221
- };
222
-
223
- isMember() {
224
- const audience = this.getCircle().Audience || [];
225
- return _.some(audience, (u) => {
226
- return u.userId === this.props.user.Id;
227
- });
228
- }
229
-
230
- handleMessageDateChange = (date) => {
231
- this.setState({
232
- messageDate: date,
233
- messageDateText: moment(date, "YYYY-MM-DD").format("DD/MM/YYYY"),
234
- showMessageDate: false,
235
- messages: [],
236
- });
237
-
238
- this.disconnect();
239
- this.getDateMessages(date);
240
- };
241
-
242
- onClearDate = () => {
243
- this.setState(
244
- {
245
- messageDate: undefined,
246
- messageDateText: undefined,
247
- showMessageDate: false,
248
- messages: [],
249
- },
250
- this.connect,
251
- );
252
- };
253
-
254
- toggleMembers = () => {
255
- this.setState({
256
- membersExpanded: !this.state.membersExpanded,
257
- });
258
- };
259
-
260
- toggleImages = () => {
261
- this.setState({
262
- imagesExpanded: !this.state.imagesExpanded,
263
- });
264
- };
265
-
266
- toggleFiles = () => {
267
- this.setState({
268
- filesExpanded: !this.state.filesExpanded,
269
- });
270
- };
271
-
272
- onKeyDown = (event) => {
273
- if (event.key === "Enter" && !event.shiftKey) {
274
- event.preventDefault();
275
- this.sendMessage();
276
- }
277
- };
278
-
279
- onDeleteMessage = (message) => {
280
- this.setState({
281
- showDeleteConfirm: true,
282
- messageToDelete: message,
283
- });
284
- };
285
-
286
- onConfirmDelete = async () => {
287
- const { messageToDelete } = this.state;
288
- if (!messageToDelete) return;
289
-
290
- // Optimistically update UI - mark as deleted immediately
291
- const updatedMessages = this.state.messages.map((m) => {
292
- if (m._id === messageToDelete._id) {
293
- return { ...m, deleted: true, text: " " };
294
- }
295
- return m;
296
- });
297
-
298
- this.setState({
299
- messages: updatedMessages,
300
- showDeleteConfirm: false,
301
- deletingMessageId: messageToDelete._id,
302
- messageToDelete: null,
303
- });
304
-
305
- try {
306
- await circleActions.deleteMessage(
307
- messageToDelete._id,
308
- this.state.circleId,
309
- );
310
- // Message already marked as deleted, just clear the deleting state
311
- this.setState({ deletingMessageId: null });
312
- } catch (error) {
313
- console.error("Failed to delete message:", error);
314
- // Revert the optimistic update on failure
315
- const revertedMessages = this.state.messages.map((m) => {
316
- if (m._id === messageToDelete._id) {
317
- return messageToDelete;
318
- }
319
- return m;
320
- });
321
- this.setState({
322
- messages: revertedMessages,
323
- deletingMessageId: null,
324
- });
325
- alert("Failed to delete message. Please try again.");
326
- }
327
- };
328
-
329
- onCancelDelete = () => {
330
- this.setState({
331
- showDeleteConfirm: false,
332
- messageToDelete: null,
333
- });
334
- };
335
-
336
- markCircleAsRead = async () => {
337
- try {
338
- await circleActions.markAsRead(this.state.circleId, this.props.user.Id);
339
- } catch (error) {
340
- console.error("Failed to mark circle as read:", error);
341
- }
342
- };
343
- sendMessage = () => {
344
- const message = {
345
- _id: Helper.randomString(),
346
- text: this.state.messageInput,
347
- user: {
348
- _id: this.props.user.Id,
349
- name: this.props.user.displayName,
350
- avatar: this.props.user.profilePic,
351
- },
352
- };
353
-
354
- if (!_.isEmpty(this.state.imageInput)) {
355
- message.image = this.state.imageInput;
356
- }
357
-
358
- if (!_.isEmpty(this.state.fileInput)) {
359
- message.attachments = this.state.fileInput;
360
- }
361
-
362
- if (
363
- _.isEmpty(message.text) &&
364
- _.isEmpty(message.image) &&
365
- _.isEmpty(message.attachments)
366
- ) {
367
- return;
368
- }
369
-
370
- if (this.state.replyingTo) {
371
- message.replyingTo = this.state.replyingTo;
372
- }
373
-
374
- const clonedMessage = _.cloneDeep(message);
375
- clonedMessage.uploading = true;
376
-
377
- circleActions.sendMessage(this.state.circleId, message).then((res) => {
378
- Object.keys(res.data).forEach((key) => {
379
- clonedMessage[key] = res.data[key];
380
- });
381
- clonedMessage.uploading = false;
382
- this.getMessages(true);
383
- });
384
- this.setState({
385
- messages: [...this.state.messages, clonedMessage],
386
- messageInput: "",
387
- imageInput: null,
388
- imageInputShowing: false,
389
- fileInput: null,
390
- fileInputShowing: false,
391
- });
392
- setTimeout(() => {
393
- this.onReply(null);
394
- this.scrollToBottom();
395
- this.imageInput && this.imageInput.setValue(null);
396
- this.fileInput && this.fileInput.setValue(null);
397
- }, 100);
398
- };
399
-
400
- validateCircleAdmin() {
401
- if (
402
- Session.validateAccess(
403
- this.props.auth.site,
404
- values.permission,
405
- this.props.auth,
406
- )
407
- ) {
408
- return true;
409
- }
410
- return _.some(this.getCircle().Audience, (user) => {
411
- return user.userId === this.props.user.Id && user.isAdmin;
412
- });
413
- }
414
-
415
- renderChatInput() {
416
- if (!this.isMember()) {
417
- return (
418
- <Components.Text type="highlightedHelp" className="chat_noMessage">
419
- You can't send a message to this {_.capitalize(values.entityName)} as
420
- you are not a member.
421
- </Components.Text>
422
- );
423
- }
424
- return (
425
- <div className="chat_inputFlex">
426
- <Icon
427
- className="chat_send"
428
- icon="fa-paper-plane"
429
- onClick={this.sendMessage}
430
- />
431
- <div className="chat_inputContainer">
432
- <Components.GenericInput
433
- id="messageInput"
434
- type="textarea"
435
- className="chat_input"
436
- componentClass="textarea"
437
- placeholder="Enter message..."
438
- value={this.state.messageInput}
439
- onChange={(e) => this.onHandleChange(e)}
440
- inputStyle={{
441
- minHeight: 50,
442
- }}
443
- onKeyDown={this.onKeyDown}
444
- />
445
- <div>
446
- <Icon
447
- className={`chat_imageIcon${this.state.imageInputShowing ? " chat_imageIcon-selected" : ""}`}
448
- icon="fa-camera"
449
- onClick={this.showImageInput}
450
- />
451
- <Icon
452
- className={`chat_imageIcon${this.state.fileInputShowing ? " chat_imageIcon-selected" : ""}`}
453
- icon="fa-paperclip"
454
- onClick={this.showFileInput}
455
- />
456
- </div>
457
- <div
458
- className="overflow-x"
459
- style={{ display: this.state.imageInputShowing ? "block" : "none" }}
460
- >
461
- <Components.ImageInput
462
- ref={(ref) => {
463
- this.imageInput = ref;
464
- }}
465
- multiple
466
- limit={10}
467
- refreshCallback={this.onImageUpdated}
468
- noMenu
469
- noCompress
470
- noDownload
471
- />
472
- </div>
473
- <div
474
- className="overflow-x"
475
- style={{ display: this.state.fileInputShowing ? "block" : "none" }}
476
- >
477
- <Components.FileInput
478
- ref={(ref) => {
479
- this.fileInput = ref;
480
- }}
481
- multiple
482
- limit={10}
483
- refreshCallback={this.onFileUpdated}
484
- noDownload
485
- accept="application/pdf"
486
- simpleStyle
487
- />
488
- </div>
489
- </div>
490
- </div>
491
- );
492
- }
493
-
494
- renderMessage(m) {
495
- if (m.system) {
496
- return (
497
- <div key={m._id} className="message">
498
- <Components.Text type="h5" className="message_system">
499
- {m.text}
500
- </Components.Text>
501
- </div>
502
- );
503
- }
504
- const isSelf = m.user._id === this.props.user.Id;
505
- const isDeleted = m.deleted === true;
506
- const isDeleting = this.state.deletingMessageId === m._id;
507
-
508
- return (
509
- <div
510
- key={m._id}
511
- className={`message${isSelf ? " message-self" : ""}${m.uploading ? " message-uploading" : ""}${isDeleting ? " message-deleting" : ""}`}
512
- >
513
- <Components.Text type="h5-noUpper" className="message_time">
514
- {moment.utc(m.createdAt).local().format("D MMM YYYY • h:mma")}
515
- </Components.Text>
516
- <div className="message_inner">
517
- <Components.ProfilePic
518
- size={40}
519
- image={m.user.avatar}
520
- className="message_profilePic"
521
- />
522
- <div className="message_bubbleContainer">
523
- <Components.Text type="body" className="message_name">
524
- {m.user.name}
525
- {m.replyingTo && !isDeleted
526
- ? ` replied to ${m.replyingTo.user.name}`
527
- : ""}
528
- </Components.Text>
529
- {m.replyingTo && !isDeleted && (
530
- <div className="message_replyBubble">
531
- <Components.Text type="body" className="message_text">
532
- {Helper.toParagraphed(
533
- (m.replyingTo.text || "").substr(0, 100),
534
- )}
535
- </Components.Text>
536
- </div>
537
- )}
538
- <div className="message_bubble">
539
- <Components.Text type="body" className="message_text">
540
- {isDeleted ? "[Message deleted]" : Helper.toParagraphed(m.text)}
541
- </Components.Text>
542
- {!isDeleted && (
543
- <>
544
- <div>
545
- {(m.image || []).map((url, i) => {
546
- return (
547
- <a href={url} target="_blank" key={i}>
548
- <img
549
- className="message_image"
550
- src={url}
551
- alt={Helper.getFileName(url)}
552
- />
553
- </a>
554
- );
555
- })}
556
- </div>
557
- <div>
558
- {(m.attachments || []).map((url, i) => {
559
- return (
560
- <Components.Attachment
561
- source={url}
562
- key={i}
563
- white={isSelf}
564
- />
565
- );
566
- })}
567
- </div>
568
- </>
569
- )}
570
- </div>
571
- {!isDeleted && (
572
- <div
573
- className="message_reply"
574
- style={{ display: "flex", gap: "16px" }}
575
- >
576
- <Components.Text
577
- type="button"
578
- onClick={() => {
579
- this.onReply(m);
580
- }}
581
- >
582
- Reply
583
- </Components.Text>
584
- {isSelf && (
585
- <Components.Text
586
- type="button"
587
- onClick={() => {
588
- this.onDeleteMessage(m);
589
- }}
590
- >
591
- Delete
592
- </Components.Text>
593
- )}
594
- </div>
595
- )}
596
- </div>
597
- </div>
598
- </div>
599
- );
600
- }
601
-
602
- renderHeaderRight() {
603
- if (!this.validateCircleAdmin() || this.getCircle().IsPrivate) {
604
- return <div className="flex flex-center"></div>;
605
- }
606
- return (
607
- <div className="flex flex-center">
608
- <Link to={`/${values.featureKey}/edit/${this.state.circleId}`}>
609
- <Icon className="header_back" icon="fa-gear" />
610
- </Link>
611
- </div>
612
- );
613
- }
614
-
615
- renderEmptyDate() {
616
- return this.renderMessage({
617
- system: true,
618
- text: "No messages on this date",
619
- });
620
- }
621
-
622
- renderSideBar() {
623
- const members = this.getCircle().Audience || [];
624
- return (
625
- <div className="chat_sideBar">
626
- <div className="chat_section">
627
- <div className="chat_section_titleSection">
628
- <Icon
629
- className="chat_section_titleSection_caret"
630
- name={`chevron-${this.state.membersExpanded ? "up" : "down"}`}
631
- onClick={this.toggleMembers}
632
- />
633
- <div className="flex-1">
634
- <Components.Text type="formTitleMedium">
635
- Member{Helper.getPluralS(members.length)} ({members.length})
636
- </Components.Text>
637
- </div>
638
- </div>
639
- {this.state.membersExpanded && (
640
- <div className="paddingTop-8">
641
- {members.map((user) => {
642
- return <Components.UserListing user={user} />;
643
- })}
644
- </div>
645
- )}
646
- </div>
647
- <div className="chat_section">
648
- <div className="chat_section_titleSection">
649
- <Icon
650
- className="chat_section_titleSection_caret"
651
- name={`chevron-${this.state.imagesExpanded ? "up" : "down"}`}
652
- onClick={this.toggleImages}
653
- />
654
- <div className="flex-1">
655
- <Components.Text type="formTitleMedium">
656
- Image{Helper.getPluralS(this.state.images.length)} (
657
- {this.state.images.length})
658
- </Components.Text>
659
- </div>
660
- </div>
661
- {this.state.imagesExpanded && (
662
- <div className="paddingTop-8">
663
- {this.state.images.map((image) => {
664
- return (
665
- <a href={image.Url} target="_blank">
666
- <img
667
- src={image.Url}
668
- className="chat_section_image"
669
- alt={Helper.getFileName(image.Url)}
670
- />
671
- </a>
672
- );
673
- })}
674
- </div>
675
- )}
676
- </div>
677
- <div className="chat_section">
678
- <div className="chat_section_titleSection">
679
- <Icon
680
- className="chat_section_titleSection_caret"
681
- name={`chevron-${this.state.filesExpanded ? "up" : "down"}`}
682
- onClick={this.toggleFiles}
683
- />
684
- <div className="flex-1">
685
- <Components.Text type="formTitleMedium">
686
- File{Helper.getPluralS(this.state.files.length)} (
687
- {this.state.files.length})
688
- </Components.Text>
689
- </div>
690
- </div>
691
- {this.state.filesExpanded && (
692
- <div className="paddingTop-8">
693
- {this.state.files.map((file, i) => {
694
- return <Components.Attachment source={file.Url} key={i} />;
695
- })}
696
- </div>
697
- )}
698
- </div>
699
- </div>
700
- );
701
- }
702
-
703
- renderReplyTo() {
704
- if (!this.state.replyingTo) {
705
- return null;
706
- }
707
- const m = this.state.replyingTo;
708
- return (
709
- <div className="chat_replyTo">
710
- <div className="chat_replyTo_container">
711
- <Components.Text type="h5">
712
- Replying to{" "}
713
- {m && m.user && !_.isEmpty(m.user.displayName)
714
- ? m.user.displayName
715
- : ""}
716
- </Components.Text>
717
- {m && !_.isEmpty(m.text) && (
718
- <Components.Text type="body">
719
- {m.text.substr(0, 50)}
720
- </Components.Text>
721
- )}
722
- </div>
723
- <div className="chat_replyTo_remove">
724
- <Components.Icon
725
- className="removeIcon"
726
- icon="xmark"
727
- onClick={() => {
728
- this.onReply(null);
729
- }}
730
- colour={Colours.COLOUR_DUSK}
731
- />
732
- </div>
733
- </div>
734
- );
735
- }
736
-
737
- renderDeleteConfirmPopup() {
738
- if (!this.state.showDeleteConfirm) return null;
739
-
740
- return (
741
- <Components.Popup
742
- title="Delete Message"
743
- buttons={[
744
- {
745
- text: "Delete",
746
- onClick: this.onConfirmDelete,
747
- type: "primary",
748
- isActive: true,
749
- },
750
- {
751
- text: "Cancel",
752
- onClick: this.onCancelDelete,
753
- type: "tertiary",
754
- isActive: true,
755
- },
756
- ]}
757
- onClose={this.onCancelDelete}
758
- hasPadding
759
- >
760
- <Components.Text type="body">
761
- Are you sure you want to delete this message? This action cannot be
762
- undone.
763
- </Components.Text>
764
- </Components.Popup>
765
- );
766
- }
767
-
768
- render() {
769
- return (
770
- <Components.OverlayPage
771
- fullPage
772
- fullPageStyle={{ display: "flex", flexDirection: "column" }}
773
- >
774
- {this.renderDeleteConfirmPopup()}
775
- <Components.Header rightContent={this.renderHeaderRight()}>
776
- <Icon
777
- className="header_back"
778
- icon="fa-angle-left"
779
- onClick={() => {
780
- window.history.back();
781
- }}
782
- />
783
- <Components.Text type="formTitleLarge" className="header_title">
784
- {this.getTitle()}
785
- </Components.Text>
786
- </Components.Header>
787
- <div className="flex flex-1 flex-reverse overflow-hidden">
788
- {this.renderSideBar()}
789
- <div className="flex-1 flex flex-column overflow-hidden">
790
- <Components.Header onlyContainer>
791
- <div className="flex flex-center flex-1 paddingHorizontal-16">
792
- <Components.Text type="h5" className="marginRight-20">
793
- Filter by
794
- </Components.Text>
795
- <div>
796
- <Components.GenericInput
797
- id="messageDate"
798
- label="Date"
799
- alwaysShowLabel
800
- value={this.state.messageDateText}
801
- readOnly
802
- onClick={() =>
803
- this.setState({
804
- showMessageDate: !this.state.showMessageDate,
805
- })
806
- }
807
- rightContent={
808
- !_.isEmpty(this.state.messageDate) && (
809
- <Components.Icon
810
- colour={Colours.COLOUR_DUSK_LIGHT}
811
- icon="fa-xmark"
812
- className="timepicker_clear"
813
- onClick={this.onClearDate}
814
- />
815
- )
816
- }
817
- />
818
- {this.state.showMessageDate && (
819
- <Components.DatePicker
820
- hideTop
821
- selectedDate={this.state.messageDate}
822
- selectDate={this.handleMessageDateChange}
823
- />
824
- )}
825
- </div>
826
- </div>
827
- </Components.Header>
828
- <div className="chat">
829
- <div className="chat_newMessage">{this.renderChatInput()}</div>
830
- {this.renderReplyTo()}
831
- <div ref={(ref) => (this.chat = ref)} className="chat_messages">
832
- {_.isEmpty(this.state.messages) &&
833
- !_.isEmpty(this.state.messageDate) &&
834
- this.renderEmptyDate()}
835
- {this.state.messages.map((m) => {
836
- return this.renderMessage(m);
837
- })}
838
- </div>
839
- </div>
840
- </div>
841
- </div>
842
- </Components.OverlayPage>
843
- );
844
- }
845
- }
846
-
847
- const mapStateToProps = (state) => {
848
- const { circles } = state[values.reducerKey];
849
- const { auth } = state;
850
- return {
851
- circles,
852
- auth,
853
- user: Helper.getUserFromState(state),
854
- };
855
- };
856
-
857
- export default connect(mapStateToProps, {
858
- circlesLoaded,
859
- setNavData: Actions.setNavData,
860
- })(Circle);