notificationSettings.tsx 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. import React from 'react';
  2. import AlertLink from 'app/components/alertLink';
  3. import AsyncComponent from 'app/components/asyncComponent';
  4. import Link from 'app/components/links/link';
  5. import {IconMail} from 'app/icons';
  6. import {t} from 'app/locale';
  7. import {Organization} from 'app/types';
  8. import withOrganizations from 'app/utils/withOrganizations';
  9. import {
  10. CONFIRMATION_MESSAGE,
  11. NOTIFICATION_SETTINGS_TYPES,
  12. NotificationSettingsObject,
  13. SELF_NOTIFICATION_SETTINGS_TYPES,
  14. } from 'app/views/settings/account/notifications/constants';
  15. import FeedbackAlert from 'app/views/settings/account/notifications/feedbackAlert';
  16. import {NOTIFICATION_SETTING_FIELDS} from 'app/views/settings/account/notifications/fields2';
  17. import {
  18. decideDefault,
  19. getParentIds,
  20. getStateToPutForDefault,
  21. isSufficientlyComplex,
  22. mergeNotificationSettings,
  23. } from 'app/views/settings/account/notifications/utils';
  24. import Form from 'app/views/settings/components/forms/form';
  25. import JsonForm from 'app/views/settings/components/forms/jsonForm';
  26. import {FieldObject} from 'app/views/settings/components/forms/type';
  27. import SettingsPageHeader from 'app/views/settings/components/settingsPageHeader';
  28. import TextBlock from 'app/views/settings/components/text/textBlock';
  29. type Props = AsyncComponent['props'] & {
  30. organizations: Organization[];
  31. };
  32. type State = {
  33. notificationSettings: NotificationSettingsObject;
  34. legacyData: {[key: string]: string};
  35. } & AsyncComponent['state'];
  36. class NotificationSettings extends AsyncComponent<Props, State> {
  37. getDefaultState(): State {
  38. return {
  39. ...super.getDefaultState(),
  40. notificationSettings: {},
  41. legacyData: {},
  42. };
  43. }
  44. getEndpoints(): ReturnType<AsyncComponent['getEndpoints']> {
  45. return [
  46. ['notificationSettings', `/users/me/notification-settings/`],
  47. ['legacyData', '/users/me/notifications/'],
  48. ];
  49. }
  50. getStateToPutForDefault = (
  51. changedData: {[key: string]: string},
  52. notificationType: string
  53. ) => {
  54. /**
  55. * Update the current providers' parent-independent notification settings
  56. * with the new value. If the new value is "never", then also update all
  57. * parent-specific notification settings to "default". If the previous value
  58. * was "never", then assume providerList should be "email" only.
  59. */
  60. const {notificationSettings} = this.state;
  61. const updatedNotificationSettings = getStateToPutForDefault(
  62. notificationType,
  63. notificationSettings,
  64. changedData,
  65. getParentIds(notificationType, notificationSettings)
  66. );
  67. this.setState({
  68. notificationSettings: mergeNotificationSettings(
  69. notificationSettings,
  70. updatedNotificationSettings
  71. ),
  72. });
  73. return updatedNotificationSettings;
  74. };
  75. get notificationSettingsType() {
  76. const hasApprovalFeatureFlag =
  77. this.props.organizations.filter(org => org.features?.includes('slack-requests'))
  78. .length > 0;
  79. // filter out approvals if the feature flag isn't set
  80. return NOTIFICATION_SETTINGS_TYPES.filter(
  81. type => type !== 'approval' || hasApprovalFeatureFlag
  82. );
  83. }
  84. getInitialData(): {[key: string]: string} {
  85. const {notificationSettings} = this.state;
  86. return Object.fromEntries(
  87. this.notificationSettingsType.map(notificationType => [
  88. notificationType,
  89. decideDefault(notificationType, notificationSettings),
  90. ])
  91. );
  92. }
  93. getFields(): FieldObject[] {
  94. const {notificationSettings} = this.state;
  95. const fields: FieldObject[] = [];
  96. for (const notificationType of this.notificationSettingsType) {
  97. const field = Object.assign({}, NOTIFICATION_SETTING_FIELDS[notificationType], {
  98. getData: data => this.getStateToPutForDefault(data, notificationType),
  99. help: (
  100. <React.Fragment>
  101. <p>
  102. {NOTIFICATION_SETTING_FIELDS[notificationType].help}
  103. &nbsp;
  104. <Link
  105. data-test-id="fine-tuning"
  106. to={`/settings/account/notifications/${notificationType}`}
  107. >
  108. Fine tune
  109. </Link>
  110. </p>
  111. </React.Fragment>
  112. ),
  113. }) as any;
  114. if (
  115. isSufficientlyComplex(notificationType, notificationSettings) &&
  116. typeof field !== 'function'
  117. ) {
  118. field.confirm = {never: CONFIRMATION_MESSAGE};
  119. }
  120. fields.push(field);
  121. }
  122. return fields;
  123. }
  124. renderBody() {
  125. const {legacyData} = this.state;
  126. return (
  127. <React.Fragment>
  128. <SettingsPageHeader title="Notifications" />
  129. <TextBlock>Personal notifications sent via email or an integration.</TextBlock>
  130. <FeedbackAlert />
  131. <Form
  132. saveOnBlur
  133. apiMethod="PUT"
  134. apiEndpoint="/users/me/notification-settings/"
  135. initialData={this.getInitialData()}
  136. >
  137. <JsonForm title={t('Notifications')} fields={this.getFields()} />
  138. </Form>
  139. <Form
  140. initialData={legacyData}
  141. saveOnBlur
  142. apiMethod="PUT"
  143. apiEndpoint="/users/me/notifications/"
  144. >
  145. <JsonForm
  146. title={t('My Activity')}
  147. fields={SELF_NOTIFICATION_SETTINGS_TYPES.map(
  148. type => NOTIFICATION_SETTING_FIELDS[type] as FieldObject
  149. )}
  150. />
  151. </Form>
  152. <AlertLink to="/settings/account/emails" icon={<IconMail />}>
  153. {t('Looking to add or remove an email address? Use the emails panel.')}
  154. </AlertLink>
  155. </React.Fragment>
  156. );
  157. }
  158. }
  159. export default withOrganizations(NotificationSettings);