@plusscommunities/pluss-core-app 1.2.4 → 1.3.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 +1 -1
- package/src/apis/index.js +1 -0
- package/src/apis/notificationActions.js +47 -0
- package/src/components/CommentSection.js +156 -16
package/package.json
CHANGED
package/src/apis/index.js
CHANGED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { getUrl } from '../helper';
|
|
2
|
+
import { authedFunction } from '../session';
|
|
3
|
+
|
|
4
|
+
export const notificationActions = {
|
|
5
|
+
getAppNotificationSetting: () => {
|
|
6
|
+
return authedFunction({
|
|
7
|
+
method: 'GET',
|
|
8
|
+
url: getUrl('notifications', 'getState/get', {
|
|
9
|
+
type: 'app',
|
|
10
|
+
}),
|
|
11
|
+
});
|
|
12
|
+
},
|
|
13
|
+
muteApp: () => {
|
|
14
|
+
return authedFunction({
|
|
15
|
+
method: 'POST',
|
|
16
|
+
url: getUrl('notifications', 'updateState/mute'),
|
|
17
|
+
data: { type: 'app' },
|
|
18
|
+
});
|
|
19
|
+
},
|
|
20
|
+
unmuteApp: () => {
|
|
21
|
+
return authedFunction({
|
|
22
|
+
method: 'POST',
|
|
23
|
+
url: getUrl('notifications', 'updateState/unmute'),
|
|
24
|
+
data: { type: 'app' },
|
|
25
|
+
});
|
|
26
|
+
},
|
|
27
|
+
getEntityNotificationSetting: (type, id) => {
|
|
28
|
+
return authedFunction({
|
|
29
|
+
method: 'GET',
|
|
30
|
+
url: getUrl('notifications', 'getState/get', { type, id }),
|
|
31
|
+
});
|
|
32
|
+
},
|
|
33
|
+
muteEntity: (type, id) => {
|
|
34
|
+
return authedFunction({
|
|
35
|
+
method: 'POST',
|
|
36
|
+
url: getUrl('notifications', 'updateState/mute'),
|
|
37
|
+
data: { type, id },
|
|
38
|
+
});
|
|
39
|
+
},
|
|
40
|
+
unmuteEntity: (type, id) => {
|
|
41
|
+
return authedFunction({
|
|
42
|
+
method: 'POST',
|
|
43
|
+
url: getUrl('notifications', 'updateState/unmute'),
|
|
44
|
+
data: { type, id },
|
|
45
|
+
});
|
|
46
|
+
},
|
|
47
|
+
};
|
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
import React, { Component } from 'react';
|
|
2
|
-
import { View, Text, TouchableOpacity, Image } from 'react-native';
|
|
2
|
+
import { View, Text, TouchableOpacity, Image, StyleSheet } from 'react-native';
|
|
3
3
|
import _ from 'lodash';
|
|
4
4
|
import moment from 'moment';
|
|
5
5
|
import { connect } from 'react-redux';
|
|
6
6
|
import { Icon } from 'react-native-elements';
|
|
7
|
-
import { getPluralS, getThumb300, get1400 } from '../helper';
|
|
7
|
+
import { getPluralS, getThumb300, get1400, getSiteSettingFromState } from '../helper';
|
|
8
8
|
import { getMainBrandingColourFromState, TEXT_DARKEST, BG_GREY, TEXT_LIGHT, LINEGREY } from '../colours';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { ConfirmPopup } from './ConfirmPopup';
|
|
12
|
-
import { ProfilePic } from './ProfilePic';
|
|
13
|
-
import { ImagePopup } from './ImagePopup';
|
|
9
|
+
import { reactionActions, notificationActions } from '../apis';
|
|
10
|
+
import { ConfirmPopup, ProfilePic, ImagePopup, InlineButton, Spinner } from './';
|
|
14
11
|
|
|
15
12
|
class CommentSection extends Component {
|
|
16
13
|
constructor(props) {
|
|
@@ -23,10 +20,16 @@ class CommentSection extends Component {
|
|
|
23
20
|
commentToReport: null,
|
|
24
21
|
commentReportedStatus: null,
|
|
25
22
|
reportLoading: false,
|
|
23
|
+
processing: false,
|
|
24
|
+
muteExpiry: null,
|
|
25
|
+
muteLoaded: false,
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
componentDidMount() {
|
|
30
|
+
this.getNotificationSate();
|
|
31
|
+
// setTimeout(this.getNotificationSate, 3000);
|
|
32
|
+
|
|
30
33
|
if (!_.includes(this.props.user.hidden, 'viewComment')) {
|
|
31
34
|
this.getComments();
|
|
32
35
|
}
|
|
@@ -97,6 +100,64 @@ class CommentSection extends Component {
|
|
|
97
100
|
this.props.commentReply.getWrappedInstance().focusInput();
|
|
98
101
|
}
|
|
99
102
|
|
|
103
|
+
onMute = () => {
|
|
104
|
+
const { entityType, entityId } = this.props;
|
|
105
|
+
this.setState({ processing: true }, async () => {
|
|
106
|
+
try {
|
|
107
|
+
const { data } = await notificationActions.muteEntity(entityType, entityId);
|
|
108
|
+
// console.log('onMute', data);
|
|
109
|
+
const muteExpiry = moment(data.Expiry);
|
|
110
|
+
this.setState({ muteExpiry, processing: false });
|
|
111
|
+
} catch (error) {
|
|
112
|
+
console.error('onMute', error);
|
|
113
|
+
this.setState({ processing: false });
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
onUnmute = () => {
|
|
119
|
+
const { entityType, entityId } = this.props;
|
|
120
|
+
this.setState({ processing: true }, async () => {
|
|
121
|
+
try {
|
|
122
|
+
const { data } = await notificationActions.unmuteEntity(entityType, entityId);
|
|
123
|
+
// console.log('onUnmute', data);
|
|
124
|
+
this.setState({ muteExpiry: null, processing: false });
|
|
125
|
+
} catch (error) {
|
|
126
|
+
console.error('onUnmute', error);
|
|
127
|
+
this.setState({ processing: false });
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
isMuted = () => {
|
|
133
|
+
const { muteExpiry } = this.state;
|
|
134
|
+
return muteExpiry && moment() <= muteExpiry;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
getMuteRemaining = () => {
|
|
138
|
+
const { muteExpiry } = this.state;
|
|
139
|
+
const actual = muteExpiry ? moment.duration(muteExpiry.diff(moment())).asHours() : 0;
|
|
140
|
+
return Math.abs(Math.ceil(actual));
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
getNotificationSate = () => {
|
|
144
|
+
const { processing } = this.state;
|
|
145
|
+
const { entityType, entityId, notificationsForComments } = this.props;
|
|
146
|
+
if (processing || !notificationsForComments) return;
|
|
147
|
+
|
|
148
|
+
this.setState({ processing: true }, async () => {
|
|
149
|
+
try {
|
|
150
|
+
const { data } = await notificationActions.getEntityNotificationSetting(entityType, entityId);
|
|
151
|
+
// console.log('getNotificationSate', data);
|
|
152
|
+
const muteExpiry = data ? moment(data.Expiry) : null;
|
|
153
|
+
this.setState({ muteExpiry, muteLoaded: true, processing: false });
|
|
154
|
+
} catch (error) {
|
|
155
|
+
console.error('getNotificationSate', error);
|
|
156
|
+
this.setState({ processing: false });
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
};
|
|
160
|
+
|
|
100
161
|
getAdjustedSize(size) {
|
|
101
162
|
if (this.props.scaleFont) {
|
|
102
163
|
return size + this.props.user.fontScale;
|
|
@@ -288,6 +349,47 @@ class CommentSection extends Component {
|
|
|
288
349
|
);
|
|
289
350
|
}
|
|
290
351
|
|
|
352
|
+
renderMute() {
|
|
353
|
+
const { notificationsForComments } = this.props;
|
|
354
|
+
const { muteLoaded, muteExpiry, processing } = this.state;
|
|
355
|
+
if (!notificationsForComments || !muteLoaded) return null;
|
|
356
|
+
|
|
357
|
+
if (processing)
|
|
358
|
+
return (
|
|
359
|
+
<View style={styles.muteSpinnerContainer}>
|
|
360
|
+
<Spinner size={'small'} color={this.props.colourBrandingMain} />
|
|
361
|
+
</View>
|
|
362
|
+
);
|
|
363
|
+
|
|
364
|
+
const isMuted = this.isMuted();
|
|
365
|
+
const hours = this.getMuteRemaining();
|
|
366
|
+
const mutedFor = `Muted for ${hours} hour${getPluralS(hours)}`;
|
|
367
|
+
// console.log(muteExpiry.format('DD MMM YYYY hh:mm a'));
|
|
368
|
+
|
|
369
|
+
return (
|
|
370
|
+
<View style={styles.muteContainer}>
|
|
371
|
+
{isMuted && muteExpiry ? <Text style={[{ color: this.props.colourBrandingMain }, styles.mutedForText]}>{mutedFor}</Text> : null}
|
|
372
|
+
<InlineButton
|
|
373
|
+
onPress={isMuted ? this.onUnmute : this.onMute}
|
|
374
|
+
color="#fff"
|
|
375
|
+
style={[{ borderColor: this.props.colourBrandingMain }, styles.muteButton]}
|
|
376
|
+
disabled={processing}
|
|
377
|
+
disabledOpacity
|
|
378
|
+
noText
|
|
379
|
+
>
|
|
380
|
+
<View style={styles.muteButtonInner}>
|
|
381
|
+
<Icon
|
|
382
|
+
name={isMuted ? 'bell-o' : 'bell-slash-o'}
|
|
383
|
+
type={'font-awesome'}
|
|
384
|
+
iconStyle={[{ color: this.props.colourBrandingMain }, styles.muteButtonIcon]}
|
|
385
|
+
/>
|
|
386
|
+
<Text style={[{ color: this.props.colourBrandingMain }, styles.muteButtonText]}>{isMuted ? 'Unmute' : 'Mute'}</Text>
|
|
387
|
+
</View>
|
|
388
|
+
</InlineButton>
|
|
389
|
+
</View>
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
|
|
291
393
|
renderComments() {
|
|
292
394
|
if (this.state.commentsLoading) {
|
|
293
395
|
return (
|
|
@@ -317,11 +419,14 @@ class CommentSection extends Component {
|
|
|
317
419
|
<View style={styles.commentSection}>
|
|
318
420
|
{!this.isEmpty() && (
|
|
319
421
|
<View style={styles.commentSectionTitleRow}>
|
|
320
|
-
{
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
422
|
+
<View style={{ alignItems: 'flex-end' }}>
|
|
423
|
+
{this.renderMute()}
|
|
424
|
+
{!this.props.hideAddComment && this.state.comments.length > 2 && (
|
|
425
|
+
<TouchableOpacity onPress={this.onGoToAdd.bind(this)}>
|
|
426
|
+
<Text style={[styles.goToText, { color: this.props.colourBrandingMain }]}>Add a comment</Text>
|
|
427
|
+
</TouchableOpacity>
|
|
428
|
+
)}
|
|
429
|
+
</View>
|
|
325
430
|
<Text style={[styles.commentCount, { fontSize: this.getAdjustedSize(15) }]}>
|
|
326
431
|
{this.state.comments.length}
|
|
327
432
|
{` comment${getPluralS(this.state.comments.length)}`}
|
|
@@ -374,22 +479,22 @@ class CommentSection extends Component {
|
|
|
374
479
|
}
|
|
375
480
|
}
|
|
376
481
|
|
|
377
|
-
const styles = {
|
|
482
|
+
const styles = StyleSheet.create({
|
|
378
483
|
commentSectionOuter: {
|
|
379
484
|
flex: 1,
|
|
380
485
|
paddingHorizontal: 16,
|
|
381
486
|
},
|
|
382
487
|
commentSection: {
|
|
383
488
|
flex: 1,
|
|
384
|
-
paddingVertical:
|
|
489
|
+
paddingVertical: 12,
|
|
385
490
|
borderTopColor: LINEGREY,
|
|
386
491
|
borderTopWidth: 1,
|
|
387
|
-
borderTopStyle: 'solid',
|
|
388
492
|
},
|
|
389
493
|
commentCount: {
|
|
390
494
|
fontFamily: 'sf-semibold',
|
|
391
495
|
color: TEXT_DARKEST,
|
|
392
496
|
flex: 1,
|
|
497
|
+
marginTop: 4,
|
|
393
498
|
},
|
|
394
499
|
comment: {
|
|
395
500
|
marginTop: 16,
|
|
@@ -463,12 +568,47 @@ const styles = {
|
|
|
463
568
|
reportLoadingContainer: {
|
|
464
569
|
paddingVertical: 10,
|
|
465
570
|
},
|
|
466
|
-
|
|
571
|
+
goToText: {
|
|
572
|
+
marginTop: 4,
|
|
573
|
+
},
|
|
574
|
+
muteSpinnerContainer: {
|
|
575
|
+
width: 100,
|
|
576
|
+
marginBottom: 6,
|
|
577
|
+
},
|
|
578
|
+
muteContainer: {
|
|
579
|
+
flexDirection: 'row',
|
|
580
|
+
alignItems: 'center',
|
|
581
|
+
justifyContent: 'flex-end',
|
|
582
|
+
marginBottom: 6,
|
|
583
|
+
},
|
|
584
|
+
mutedForText: {
|
|
585
|
+
fontFamily: 'sf-semibold',
|
|
586
|
+
fontSize: 13,
|
|
587
|
+
marginRight: 8,
|
|
588
|
+
},
|
|
589
|
+
muteButton: {
|
|
590
|
+
borderWidth: 1,
|
|
591
|
+
paddingHorizontal: 8,
|
|
592
|
+
},
|
|
593
|
+
muteButtonInner: {
|
|
594
|
+
flexDirection: 'row',
|
|
595
|
+
alignItems: 'center',
|
|
596
|
+
},
|
|
597
|
+
muteButtonIcon: {
|
|
598
|
+
fontSize: 14,
|
|
599
|
+
},
|
|
600
|
+
muteButtonText: {
|
|
601
|
+
fontFamily: 'sf-semibold',
|
|
602
|
+
fontSize: 14,
|
|
603
|
+
marginLeft: 6,
|
|
604
|
+
},
|
|
605
|
+
});
|
|
467
606
|
|
|
468
607
|
const mapStateToProps = state => {
|
|
469
608
|
return {
|
|
470
609
|
colourBrandingMain: getMainBrandingColourFromState(state),
|
|
471
610
|
user: state.user,
|
|
611
|
+
notificationsForComments: getSiteSettingFromState(state, 'NotificationsForComments', false),
|
|
472
612
|
};
|
|
473
613
|
};
|
|
474
614
|
|