stats.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. import React from "react"
  2. import escapeHtml from "misago/utils/escape-html"
  3. const LAST_POSTER_URL = '<a href="%(url)s" class="poster-title">%(user)s</a>'
  4. const LAST_POSTER_SPAN = '<span class="poster-title">%(user)s</span>'
  5. const LAST_REPLY =
  6. '<abbr class="last-title" title="%(absolute)s">%(relative)s</abbr>'
  7. export function Weight(props) {
  8. if (props.thread.weight == 2) {
  9. return (
  10. <li className="thread-pinned-globally">
  11. <span className="material-icon">bookmark</span>
  12. <span className="icon-legend">{gettext("Pinned globally")}</span>
  13. </li>
  14. )
  15. } else if (props.thread.weight == 1) {
  16. return (
  17. <li className="thread-pinned-locally">
  18. <span className="material-icon">bookmark_border</span>
  19. <span className="icon-legend">{gettext("Pinned locally")}</span>
  20. </li>
  21. )
  22. } else {
  23. return null
  24. }
  25. }
  26. export function Unapproved(props) {
  27. if (props.thread.is_unapproved) {
  28. return (
  29. <li className="thread-unapproved">
  30. <span className="material-icon">remove_circle</span>
  31. <span className="icon-legend">{gettext("Unapproved")}</span>
  32. </li>
  33. )
  34. } else if (props.thread.has_unapproved_posts) {
  35. return (
  36. <li className="thread-unapproved-posts">
  37. <span className="material-icon">remove_circle_outline</span>
  38. <span className="icon-legend">{gettext("Unapproved posts")}</span>
  39. </li>
  40. )
  41. } else {
  42. return null
  43. }
  44. }
  45. export function IsHidden(props) {
  46. if (props.thread.is_hidden) {
  47. return (
  48. <li className="thread-hidden">
  49. <span className="material-icon">visibility_off</span>
  50. <span className="icon-legend">{gettext("Hidden")}</span>
  51. </li>
  52. )
  53. } else {
  54. return null
  55. }
  56. }
  57. export function IsClosed(props) {
  58. if (props.thread.is_closed) {
  59. return (
  60. <li className="thread-closed">
  61. <span className="material-icon">lock_outline</span>
  62. <span className="icon-legend">{gettext("Closed")}</span>
  63. </li>
  64. )
  65. } else {
  66. return null
  67. }
  68. }
  69. export function Replies(props) {
  70. const message = ngettext(
  71. "%(replies)s reply",
  72. "%(replies)s replies",
  73. props.thread.replies
  74. )
  75. const legend = interpolate(message, { replies: props.thread.replies }, true)
  76. return (
  77. <li className="thread-replies">
  78. <span className="material-icon">forum</span>
  79. <span className="icon-legend">{legend}</span>
  80. </li>
  81. )
  82. }
  83. export function LastReply(props) {
  84. let user = null
  85. if (props.thread.url.last_poster) {
  86. user = interpolate(
  87. LAST_POSTER_URL,
  88. {
  89. url: escapeHtml(props.thread.url.last_poster),
  90. user: escapeHtml(props.thread.last_poster_name)
  91. },
  92. true
  93. )
  94. } else {
  95. user = interpolate(
  96. LAST_POSTER_SPAN,
  97. {
  98. user: escapeHtml(props.thread.last_poster_name)
  99. },
  100. true
  101. )
  102. }
  103. const date = interpolate(
  104. LAST_REPLY,
  105. {
  106. absolute: escapeHtml(props.thread.last_post_on.format("LLL")),
  107. relative: escapeHtml(props.thread.last_post_on.fromNow())
  108. },
  109. true
  110. )
  111. const message = interpolate(
  112. escapeHtml(gettext("last reply by %(user)s %(date)s")),
  113. {
  114. date,
  115. user
  116. },
  117. true
  118. )
  119. return (
  120. <li
  121. className="thread-last-reply"
  122. dangerouslySetInnerHTML={{ __html: message }}
  123. />
  124. )
  125. }
  126. export default function(props) {
  127. return (
  128. <div className="header-stats">
  129. <div className="container">
  130. <ul className="list-inline">
  131. <Weight thread={props.thread} />
  132. <Unapproved thread={props.thread} />
  133. <IsHidden thread={props.thread} />
  134. <IsClosed thread={props.thread} />
  135. <Replies thread={props.thread} />
  136. <LastReply thread={props.thread} />
  137. </ul>
  138. </div>
  139. </div>
  140. )
  141. }