You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Favorite.tsx 3.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2022 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. import classNames from 'classnames';
  21. import * as React from 'react';
  22. import { addFavorite, removeFavorite } from '../../api/favorites';
  23. import { translate, translateWithParameters } from '../../helpers/l10n';
  24. import FavoriteIcon from '../icons/FavoriteIcon';
  25. import { ButtonLink } from './buttons';
  26. import Tooltip from './Tooltip';
  27. interface Props {
  28. className?: string;
  29. component: string;
  30. componentName?: string;
  31. favorite: boolean;
  32. qualifier: string;
  33. handleFavorite?: (component: string, isFavorite: boolean) => void;
  34. }
  35. interface State {
  36. favorite: boolean;
  37. }
  38. export default class Favorite extends React.PureComponent<Props, State> {
  39. mounted = false;
  40. buttonNode?: HTMLElement | null;
  41. constructor(props: Props) {
  42. super(props);
  43. this.state = {
  44. favorite: props.favorite
  45. };
  46. }
  47. componentDidMount() {
  48. this.mounted = true;
  49. }
  50. componentDidUpdate(_prevProps: Props, prevState: State) {
  51. if (prevState.favorite !== this.props.favorite) {
  52. this.setState({ favorite: this.props.favorite });
  53. }
  54. }
  55. componentWillUnmount() {
  56. this.mounted = false;
  57. }
  58. toggleFavorite = () => {
  59. const newFavorite = !this.state.favorite;
  60. const apiMethod = newFavorite ? addFavorite : removeFavorite;
  61. return apiMethod(this.props.component).then(() => {
  62. if (this.mounted) {
  63. this.setState({ favorite: newFavorite }, () => {
  64. if (this.props.handleFavorite) {
  65. this.props.handleFavorite(this.props.component, newFavorite);
  66. }
  67. if (this.buttonNode) {
  68. this.buttonNode.focus();
  69. }
  70. });
  71. }
  72. });
  73. };
  74. render() {
  75. const { className, componentName, qualifier } = this.props;
  76. const { favorite } = this.state;
  77. const actionName = favorite ? 'remove' : 'add';
  78. const tooltip = componentName
  79. ? translateWithParameters(`favorite.action.${qualifier}.${actionName}_x`, componentName)
  80. : translate('favorite.action', qualifier, actionName);
  81. return (
  82. <Tooltip overlay={tooltip}>
  83. <ButtonLink
  84. innerRef={node => (this.buttonNode = node)}
  85. className={classNames('favorite-link', 'link-no-underline', className)}
  86. onClick={this.toggleFavorite}>
  87. <FavoriteIcon aria-label={tooltip} favorite={favorite} />
  88. </ButtonLink>
  89. </Tooltip>
  90. );
  91. }
  92. }