Skip to content
This repository was archived by the owner on Sep 4, 2025. It is now read-only.

Commit bd14934

Browse files
Merge pull request #689 from unitaryfund/650_subscriptions
Member subscriptions
2 parents 0ca02a9 + f3c57c9 commit bd14934

File tree

9 files changed

+132
-10
lines changed

9 files changed

+132
-10
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { library } from '@fortawesome/fontawesome-svg-core'
2+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
3+
import { faBell, faBellSlash } from '@fortawesome/free-solid-svg-icons'
4+
import { Button } from 'react-bootstrap'
5+
import TooltipTrigger from './TooltipTrigger'
6+
7+
library.add(faBell, faBellSlash)
8+
9+
const SubscribeButton = (props) =>
10+
<span>
11+
{!props.isSubscribed &&
12+
<TooltipTrigger message={'Subscribe to ' + props.typeLabel}>
13+
<Button className='submission-button' variant='secondary' aria-label={'Subscribe to ' + props.typeLabel} onClick={props.onSubscribe}><FontAwesomeIcon icon='bell' /></Button>
14+
</TooltipTrigger>}
15+
{props.isSubscribed &&
16+
<TooltipTrigger message={'Unsubscribe from ' + props.typeLabel}>
17+
<Button className='submission-button' variant='primary' aria-label={'Unsubscribe from ' + props.typeLabel} onClick={props.onSubscribe}><FontAwesomeIcon icon='bell-slash' /></Button>
18+
</TooltipTrigger>}
19+
</span>
20+
21+
export default SubscribeButton

src/index.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ import './index.css'
44
import '../node_modules/bootstrap/dist/css/bootstrap.min.css'
55
import App from './App'
66
import reportWebVitals from './reportWebVitals'
7-
import CookieConsent from "react-cookie-consent";
8-
7+
import CookieConsent from 'react-cookie-consent'
98

109
ReactDOM.render(
1110
<React.StrictMode>

src/views/AddSubmission.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import ResultsAddModal from '../components/ResultsAddModal'
2121
import SubmissionRefsDeleteModal from '../components/SubmissionRefsDeleteModal'
2222
import TooltipTrigger from '../components/TooltipTrigger'
2323

24-
2524
library.add(faPlus)
2625

2726
const requiredFieldMissingError = 'Required field.'
@@ -533,9 +532,9 @@ class AddSubmission extends React.Component {
533532
<i>Draft saved at {this.state.draftedAt}</i>
534533
</FormFieldAlertRow>}
535534
<FormFieldWideRow className='text-center'>
536-
<TooltipTrigger message="Draft is saved under 'My Submissions'">
537-
<Button variant='light' className='submission-ref-button' onClick={() => this.handleOnSubmit(null, true, null)} disabled={!this.state.isValidated && !this.isAllValid()}>Save draft</Button>
538-
</TooltipTrigger>
535+
<TooltipTrigger message="Draft is saved under 'My Submissions'">
536+
<Button variant='light' className='submission-ref-button' onClick={() => this.handleOnSubmit(null, true, null)} disabled={!this.state.isValidated && !this.isAllValid()}>Save draft</Button>
537+
</TooltipTrigger>
539538
<input className='btn btn-primary submission-ref-button' type='submit' value='Submit' disabled={!this.state.isValidated && !this.isAllValid()} />
540539
</FormFieldWideRow>
541540
</form>

src/views/Delete.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@ class Delete extends React.Component {
3232
}
3333

3434
handleDeleteOnClick () {
35-
const confirmString = window.prompt('To delete your account, type your username or email below, then hit "OK."', '').trim().toLowerCase()
35+
let confirmString = window.prompt('To unsubscribe from all email updates, type your username or email below, then hit "OK."', '')
36+
if (confirmString) {
37+
confirmString = confirmString.trim().toLowerCase()
38+
}
3639
if (confirmString && ((confirmString === this.state.data.usernameNormal) || (confirmString === this.state.data.email))) {
3740
axios.delete(config.api.getUriPrefix() + '/user')
3841
.then(res => {

src/views/Method.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import TooltipTrigger from '../components/TooltipTrigger'
1313
import SocialShareIcons from '../components/SocialShareIcons'
1414
import { sortByCounts } from '../components/SortFunctions'
1515
import FormFieldWideRow from '../components/FormFieldWideRow'
16+
import SubscribeButton from '../components/SubscriptionButton'
1617
const FormFieldRow = React.lazy(() => import('../components/FormFieldRow'))
1718
const FormFieldSelectRow = React.lazy(() => import('../components/FormFieldSelectRow'))
1819

@@ -29,13 +30,28 @@ class Method extends React.Component {
2930
allMethodNames: []
3031
}
3132

33+
this.handleSubscribe = this.handleSubscribe.bind(this)
3234
this.handleShowEditModal = this.handleShowEditModal.bind(this)
3335
this.handleHideEditModal = this.handleHideEditModal.bind(this)
3436
this.handleEditModalDone = this.handleEditModalDone.bind(this)
3537
this.handleTrimMethods = this.handleTrimMethods.bind(this)
3638
this.handleOnChange = this.handleOnChange.bind(this)
3739
}
3840

41+
handleSubscribe () {
42+
if (this.props.isLoggedIn) {
43+
axios.post(config.api.getUriPrefix() + '/method/' + this.props.match.params.id + '/subscribe', {})
44+
.then(res => {
45+
this.setState({ item: res.data.data })
46+
})
47+
.catch(err => {
48+
window.alert('Error: ' + ErrorHandler(err) + '\nSorry! Check your connection and login status, and try again.')
49+
})
50+
} else {
51+
this.handleLoginRedirect()
52+
}
53+
}
54+
3955
handleShowEditModal () {
4056
let mode = 'Edit'
4157
if (!this.props.isLoggedIn) {
@@ -141,6 +157,7 @@ class Method extends React.Component {
141157
<TooltipTrigger message='Edit method'>
142158
<Button className='submission-button' variant='secondary' aria-label='Edit method' onClick={this.handleShowEditModal}><FontAwesomeIcon icon='edit' /></Button>
143159
</TooltipTrigger>
160+
<SubscribeButton isSubscribed={this.state.item.isSubscribed} typeLabel='method' onSubscribe={this.handleSubscribe} />
144161
<SocialShareIcons url={config.api.getUriPrefix() + '/method/' + this.props.match.params.id} />
145162
</FormFieldWideRow>
146163
<br />

src/views/Platform.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import FormFieldWideRow from '../components/FormFieldWideRow'
1313
import TooltipTrigger from '../components/TooltipTrigger'
1414
import SocialShareIcons from '../components/SocialShareIcons'
1515
import { intRegex, nonblankRegex, numberRegex } from '../components/ValidationRegex'
16+
import SubscribeButton from '../components/SubscriptionButton'
1617
const FormFieldRow = React.lazy(() => import('../components/FormFieldRow'))
1718
const FormFieldSelectRow = React.lazy(() => import('../components/FormFieldSelectRow'))
1819

@@ -62,6 +63,7 @@ class Platform extends React.Component {
6263
}
6364
}
6465

66+
this.handleSubscribe = this.handleSubscribe.bind(this)
6567
this.handleAccordionToggle = this.handleAccordionToggle.bind(this)
6668
this.handleShowEditModal = this.handleShowEditModal.bind(this)
6769
this.handleHideEditModal = this.handleHideEditModal.bind(this)
@@ -80,6 +82,20 @@ class Platform extends React.Component {
8082
this.handleOnPropertyRemove = this.handleOnPropertyRemove.bind(this)
8183
}
8284

85+
handleSubscribe () {
86+
if (this.props.isLoggedIn) {
87+
axios.post(config.api.getUriPrefix() + '/platform/' + this.props.match.params.id + '/subscribe', {})
88+
.then(res => {
89+
this.setState({ item: res.data.data })
90+
})
91+
.catch(err => {
92+
window.alert('Error: ' + ErrorHandler(err) + '\nSorry! Check your connection and login status, and try again.')
93+
})
94+
} else {
95+
this.handleLoginRedirect()
96+
}
97+
}
98+
8399
handleAccordionToggle () {
84100
this.setState({ showAccordion: !this.state.showAccordion, isValidated: false })
85101
}
@@ -369,6 +385,7 @@ class Platform extends React.Component {
369385
<TooltipTrigger message='Edit platform'>
370386
<Button className='submission-button' variant='secondary' aria-label='Edit platform' onClick={this.handleShowEditModal}><FontAwesomeIcon icon='edit' /></Button>
371387
</TooltipTrigger>
388+
<SubscribeButton isSubscribed={this.state.item.isSubscribed} typeLabel='submission' onSubscribe={this.handleSubscribe} />
372389
<SocialShareIcons url={config.api.getUriPrefix() + '/platform/' + this.props.match.params.id} />
373390
</FormFieldWideRow>
374391
<br />

src/views/Profile.js

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class Profile extends React.Component {
1515
constructor (props) {
1616
super(props)
1717
this.state = {
18-
data: { affiliation: '', name: '' },
18+
data: { affiliation: '', name: '', usernameNormal: '', email: '' },
1919
showEditModal: false,
2020
requestFailedMessage: ''
2121
}
@@ -24,6 +24,7 @@ class Profile extends React.Component {
2424
this.handleHideModal = this.handleHideModal.bind(this)
2525
this.handleShowModal = this.handleShowModal.bind(this)
2626
this.handleUpdateDetails = this.handleUpdateDetails.bind(this)
27+
this.handleUnsubscribe = this.handleUnsubscribe.bind(this)
2728
}
2829

2930
handleOnChange (field, value) {
@@ -54,6 +55,28 @@ class Profile extends React.Component {
5455
})
5556
}
5657

58+
handleUnsubscribe () {
59+
let confirmString = window.prompt('To unsubscribe from all email updates, type your username or email below, then hit "OK."', '')
60+
if (confirmString) {
61+
confirmString = confirmString.trim().toLowerCase()
62+
}
63+
if (confirmString && ((confirmString === this.state.data.usernameNormal) || (confirmString === this.state.data.email))) {
64+
axios.post(config.api.getUriPrefix() + '/user/unsubscribe', {})
65+
.then(res => {
66+
this.setState({
67+
data: res.data.data,
68+
requestFailedMessage: ''
69+
})
70+
window.alert('Successfully unsubscribed from all email updates!')
71+
})
72+
.catch(err => {
73+
this.setState({ requestFailedMessage: ErrorHandler(err) })
74+
})
75+
} else {
76+
this.setState({ requestFailedMessage: 'Entered incorrect username/email!' })
77+
}
78+
}
79+
5780
componentDidMount () {
5881
axios.get(config.api.getUriPrefix() + '/user')
5982
.then(res => {
@@ -92,6 +115,10 @@ class Profile extends React.Component {
92115
<Link to='/Token'><Button variant='primary'>Manage API Token</Button></Link>
93116
</FormFieldWideRow>
94117
<br />
118+
<FormFieldWideRow className='text-center'>
119+
<Button variant='primary' onClick={this.handleUnsubscribe}>Unsubscribe From All Email Updates</Button>
120+
</FormFieldWideRow>
121+
<br />
95122
<FormFieldWideRow className='text-center'>
96123
<Link to='/Password'><Button variant='primary'>Change password</Button></Link>
97124
</FormFieldWideRow>

src/views/Submission.js

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,22 @@ import { Button, Modal } from 'react-bootstrap'
99
import { Link } from 'react-router-dom'
1010
import { library } from '@fortawesome/fontawesome-svg-core'
1111
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
12-
import { faEdit, faLink, faHeart, faMobileAlt, faStickyNote, faSuperscript } from '@fortawesome/free-solid-svg-icons'
12+
import { faEdit, faLink, faHeart, faMobileAlt, faStickyNote, faSuperscript, faBell, faBellSlash } from '@fortawesome/free-solid-svg-icons'
1313
import logo from './../images/metriq_logo_secondary_blue.png'
1414
import Commento from '../components/Commento'
1515
import FormFieldWideRow from '../components/FormFieldWideRow'
1616
import SocialShareIcons from '../components/SocialShareIcons'
1717
import { metricValueRegex, nonblankRegex } from '../components/ValidationRegex'
1818
import ResultsTable from '../components/ResultsTable'
1919
import FormFieldAlertRow from '../components/FormFieldAlertRow'
20+
import SubscribeButton from '../components/SubscriptionButton'
2021
const FormFieldRow = React.lazy(() => import('../components/FormFieldRow'))
2122
const FormFieldTypeaheadRow = React.lazy(() => import('../components/FormFieldTypeaheadRow'))
2223
const SubmissionRefsAddModal = React.lazy(() => import('../components/SubmissionRefsAddModal'))
2324
const SubmissionRefsDeleteModal = React.lazy(() => import('../components/SubmissionRefsDeleteModal'))
2425
const ResultsAddModal = React.lazy(() => import('../components/ResultsAddModal'))
2526

26-
library.add(faEdit, faLink, faHeart, faMobileAlt, faStickyNote, faSuperscript)
27+
library.add(faEdit, faLink, faHeart, faMobileAlt, faStickyNote, faSuperscript, faBell, faBellSlash)
2728

2829
class Submission extends React.Component {
2930
constructor (props) {
@@ -96,6 +97,7 @@ class Submission extends React.Component {
9697
}
9798

9899
this.handleEditSubmissionDetails = this.handleEditSubmissionDetails.bind(this)
100+
this.handleSubscribe = this.handleSubscribe.bind(this)
99101
this.handleModerationReport = this.handleModerationReport.bind(this)
100102
this.handleHideEditModal = this.handleHideEditModal.bind(this)
101103
this.handleEditModalDone = this.handleEditModalDone.bind(this)
@@ -136,6 +138,20 @@ class Submission extends React.Component {
136138
this.setState({ showEditModal: true, modalMode: mode, submission: submission })
137139
}
138140

141+
handleSubscribe () {
142+
if (this.props.isLoggedIn) {
143+
axios.post(config.api.getUriPrefix() + '/submission/' + this.props.match.params.id + '/subscribe', {})
144+
.then(res => {
145+
this.setState({ item: res.data.data })
146+
})
147+
.catch(err => {
148+
window.alert('Error: ' + ErrorHandler(err) + '\nSorry! Check your connection and login status, and try again.')
149+
})
150+
} else {
151+
this.handleLoginRedirect()
152+
}
153+
}
154+
139155
handleModerationReport () {
140156
let mode = 'Moderation'
141157
const modalTextMode = 'Moderation'
@@ -574,6 +590,7 @@ class Submission extends React.Component {
574590
<TooltipTrigger message='Edit submission'>
575591
<Button className='submission-button' variant='secondary' aria-label='Edit submission' onClick={this.handleEditSubmissionDetails}><FontAwesomeIcon icon='edit' /></Button>
576592
</TooltipTrigger>
593+
<SubscribeButton isSubscribed={this.state.item.isSubscribed} typeLabel='submission' onSubscribe={this.handleSubscribe} />
577594
<SocialShareIcons url={config.api.getUriPrefix() + '/submission/' + this.props.match.params.id} />
578595
</FormFieldWideRow>
579596
<br />

src/views/Task.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { parse } from 'json2csv'
1616
import Commento from '../components/Commento'
1717
import TooltipTrigger from '../components/TooltipTrigger'
1818
import SocialShareIcons from '../components/SocialShareIcons'
19+
import SubscribeButton from '../components/SubscriptionButton'
1920
const SotaChart = React.lazy(() => import('../components/SotaChart'))
2021

2122
library.add(faEdit)
@@ -35,13 +36,33 @@ class Task extends React.Component {
3536
isLog: false
3637
}
3738

39+
this.handleSubscribe = this.handleSubscribe.bind(this)
3840
this.handleShowEditModal = this.handleShowEditModal.bind(this)
3941
this.handleHideEditModal = this.handleHideEditModal.bind(this)
4042
this.handleEditModalDone = this.handleEditModalDone.bind(this)
4143
this.handleOnChange = this.handleOnChange.bind(this)
4244
this.handleTrimTasks = this.handleTrimTasks.bind(this)
4345
this.handleCsvExport = this.handleCsvExport.bind(this)
4446
this.handleOnLoadData = this.handleOnLoadData.bind(this)
47+
this.handleLoginRedirect = this.handleLoginRedirect.bind(this)
48+
}
49+
50+
handleLoginRedirect () {
51+
this.props.history.push('/Login/' + encodeURIComponent('Task/' + this.props.match.params.id))
52+
}
53+
54+
handleSubscribe () {
55+
if (this.props.isLoggedIn) {
56+
axios.post(config.api.getUriPrefix() + '/task/' + this.props.match.params.id + '/subscribe', {})
57+
.then(res => {
58+
this.setState({ item: res.data.data })
59+
})
60+
.catch(err => {
61+
window.alert('Error: ' + ErrorHandler(err) + '\nSorry! Check your connection and login status, and try again.')
62+
})
63+
} else {
64+
this.handleLoginRedirect()
65+
}
4566
}
4667

4768
handleShowEditModal () {
@@ -180,6 +201,7 @@ class Task extends React.Component {
180201
<FontAwesomeIcon icon='edit' />
181202
</Button>
182203
</TooltipTrigger>
204+
<SubscribeButton isSubscribed={this.state.item.isSubscribed} typeLabel='task' onSubscribe={this.handleSubscribe} />
183205
<SocialShareIcons url={config.api.getUriPrefix() + '/task/' + this.props.match.params.id} />
184206
</FormFieldWideRow>
185207
<br />

0 commit comments

Comments
 (0)