post-likes.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. // jshint ignore:start
  2. import React from 'react';
  3. import moment from 'moment';
  4. import Avatar from 'misago/components/avatar';
  5. import Message from 'misago/components/modal-message';
  6. import Loader from 'misago/components/modal-loader';
  7. import ajax from 'misago/services/ajax';
  8. export default class extends React.Component {
  9. constructor(props) {
  10. super(props);
  11. this.state = {
  12. isReady: false,
  13. error: null,
  14. likes: []
  15. };
  16. }
  17. componentDidMount() {
  18. ajax.get(this.props.post.api.likes).then((data) => {
  19. this.setState({
  20. isReady: true,
  21. likes: data.map(hydrateLike)
  22. });
  23. }, (rejection) => {
  24. this.setState({
  25. isReady: true,
  26. error: rejection.detail
  27. });
  28. });
  29. };
  30. render() {
  31. if (this.state.error) {
  32. return (
  33. <ModalDialog className="modal-message">
  34. <Message
  35. message={this.state.error}
  36. />
  37. </ModalDialog>
  38. );
  39. } else if (this.state.isReady) {
  40. if (this.state.likes.length) {
  41. return (
  42. <ModalDialog
  43. className="modal-sm"
  44. likes={this.state.likes}
  45. >
  46. <LikesList
  47. likes={this.state.likes}
  48. />
  49. </ModalDialog>
  50. );
  51. }
  52. return (
  53. <ModalDialog className="modal-message">
  54. <Message
  55. message={gettext("No users have liked this post.")}
  56. />
  57. </ModalDialog>
  58. );
  59. }
  60. return (
  61. <ModalDialog className="modal-sm">
  62. <Loader />
  63. </ModalDialog>
  64. );
  65. }
  66. }
  67. export function hydrateLike(data) {
  68. return Object.assign({}, data, {
  69. liked_on: moment(data.liked_on)
  70. });
  71. }
  72. export function ModalDialog({ className, children, likes }) {
  73. let title = gettext("Post Likes");
  74. if (likes) {
  75. const likesCount = likes.length;
  76. const message = ngettext(
  77. "%(likes)s like",
  78. "%(likes)s likes",
  79. likesCount);
  80. title = interpolate(message, { likes: likesCount }, true);
  81. }
  82. return (
  83. <div
  84. className={"modal-dialog " + (className || '')}
  85. role="document"
  86. >
  87. <div className="modal-content">
  88. <div className="modal-header">
  89. <button
  90. aria-label={gettext("Close")}
  91. className="close"
  92. data-dismiss="modal"
  93. type="button"
  94. >
  95. <span aria-hidden="true">&times;</span>
  96. </button>
  97. <h4 className="modal-title">{title}</h4>
  98. </div>
  99. {children}
  100. </div>
  101. </div>
  102. )
  103. }
  104. export function LikesList(props) {
  105. return (
  106. <div className="modal-body modal-post-likers">
  107. <ul className="media-list">
  108. {props.likes.map((like) => {
  109. return (
  110. <LikeDetails
  111. key={like.id}
  112. {...like}
  113. />
  114. );
  115. })}
  116. </ul>
  117. </div>
  118. );
  119. }
  120. export function LikeDetails(props) {
  121. if (props.url) {
  122. const user = {
  123. id: props.liker_id,
  124. avatars: props.avatars
  125. };
  126. return (
  127. <li className="media">
  128. <div className="media-left">
  129. <a
  130. className="user-avatar"
  131. href={props.url}
  132. >
  133. <Avatar size="50" user={user} />
  134. </a>
  135. </div>
  136. <div className="media-body">
  137. <a
  138. className="item-title"
  139. href={props.url}
  140. >
  141. {props.username}
  142. </a>
  143. {' '}
  144. <LikeDate likedOn={props.liked_on} />
  145. </div>
  146. </li>
  147. );
  148. }
  149. return (
  150. <li className="media">
  151. <div className="media-left">
  152. <span className="user-avatar">
  153. <Avatar size="50" />
  154. </span>
  155. </div>
  156. <div className="media-body">
  157. <strong>{props.username}</strong>
  158. {' '}
  159. <LikeDate likedOn={props.liked_on} />
  160. </div>
  161. </li>
  162. );
  163. }
  164. export function LikeDate(props) {
  165. return (
  166. <span
  167. className="text-muted"
  168. title={props.likedOn.format('LLL')}
  169. >
  170. {props.likedOn.fromNow()}
  171. </span>
  172. );
  173. }