PageListMeta.tsx 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. import type { FC, JSX } from 'react';
  2. import type { IPageHasId } from '@growi/core';
  3. import { pagePathUtils, templateChecker } from '@growi/core/dist/utils';
  4. const { isTopPage } = pagePathUtils;
  5. const { checkTemplatePath } = templateChecker;
  6. const SEEN_USERS_HIDE_THRES__ACTIVE_USERS_COUNT = 5;
  7. const MAX_STRENGTH_LEVEL = 4;
  8. type SeenUsersCountProps = {
  9. count: number;
  10. basisViewersCount?: number;
  11. shouldSpaceOutIcon?: boolean;
  12. };
  13. const SeenUsersCount = (props: SeenUsersCountProps): JSX.Element => {
  14. const { count, shouldSpaceOutIcon, basisViewersCount } = props;
  15. if (count === 0) {
  16. return <></>;
  17. }
  18. if (
  19. basisViewersCount != null &&
  20. basisViewersCount <= SEEN_USERS_HIDE_THRES__ACTIVE_USERS_COUNT
  21. ) {
  22. return <></>;
  23. }
  24. const strengthLevel = Math.ceil(
  25. Math.min(0, Math.log(count / (basisViewersCount ?? count))) * // Max: 0
  26. 2 *
  27. -1,
  28. );
  29. if (strengthLevel > MAX_STRENGTH_LEVEL) {
  30. return <></>;
  31. }
  32. if (!(strengthLevel >= 0 && strengthLevel <= MAX_STRENGTH_LEVEL)) {
  33. throw new Error('strengthLevel out of range');
  34. } // [0, MAX_STRENGTH_LEVEL)
  35. const strengthClass = `strength-${strengthLevel}`; // strength-{0, 1, 2, 3, 4}
  36. return (
  37. <span
  38. className={`seen-users-count ${shouldSpaceOutIcon ? 'me-2' : ''} ${strengthClass}`}
  39. >
  40. <span className="material-symbols-outlined">footprint</span>
  41. {count}
  42. </span>
  43. );
  44. };
  45. type PageListMetaProps = {
  46. page: IPageHasId;
  47. likerCount?: number;
  48. bookmarkCount?: number;
  49. shouldSpaceOutIcon?: boolean;
  50. basisViewersCount?: number;
  51. };
  52. export const PageListMeta: FC<PageListMetaProps> = (
  53. props: PageListMetaProps,
  54. ) => {
  55. const { page, shouldSpaceOutIcon, basisViewersCount } = props;
  56. // top check
  57. let topLabel: JSX.Element | undefined;
  58. if (isTopPage(page.path)) {
  59. topLabel = (
  60. <span
  61. className={`badge bg-info ${shouldSpaceOutIcon ? 'me-2' : ''} top-label`}
  62. >
  63. TOP
  64. </span>
  65. );
  66. }
  67. // template check
  68. let templateLabel: JSX.Element | undefined;
  69. if (checkTemplatePath(page.path)) {
  70. templateLabel = (
  71. <span className={`badge bg-info ${shouldSpaceOutIcon ? 'me-2' : ''}`}>
  72. TMPL
  73. </span>
  74. );
  75. }
  76. let commentCount: JSX.Element | undefined;
  77. if (page.commentCount > 0) {
  78. commentCount = (
  79. <span className={`${shouldSpaceOutIcon ? 'me-2' : ''}`}>
  80. <span className="material-symbols-outlined">comment</span>
  81. {page.commentCount}
  82. </span>
  83. );
  84. }
  85. let likerCount: JSX.Element | undefined;
  86. if (props.likerCount != null && props.likerCount > 0) {
  87. likerCount = (
  88. <span className={`${shouldSpaceOutIcon ? 'me-2' : ''}`}>
  89. <span className="material-symbols-outlined">favorite</span>
  90. {props.likerCount}
  91. </span>
  92. );
  93. }
  94. let locked: JSX.Element | undefined;
  95. if (page.grant !== 1) {
  96. locked = (
  97. <span className={`${shouldSpaceOutIcon ? 'me-2' : ''}`}>
  98. <span className="material-symbols-outlined">lock</span>
  99. </span>
  100. );
  101. }
  102. let bookmarkCount: JSX.Element | undefined;
  103. if (props.bookmarkCount != null && props.bookmarkCount > 0) {
  104. bookmarkCount = (
  105. <span className={`${shouldSpaceOutIcon ? 'me-2' : ''}`}>
  106. <span className="material-symbols-outlined">bookmark</span>
  107. {props.bookmarkCount}
  108. </span>
  109. );
  110. }
  111. return (
  112. <span className="page-list-meta">
  113. {topLabel}
  114. {templateLabel}
  115. <SeenUsersCount
  116. count={page.seenUsers.length}
  117. basisViewersCount={basisViewersCount}
  118. shouldSpaceOutIcon={shouldSpaceOutIcon}
  119. />
  120. {commentCount}
  121. {likerCount}
  122. {locked}
  123. {bookmarkCount}
  124. </span>
  125. );
  126. };