AuthorInfo.tsx 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. import React, { type JSX } from 'react';
  2. import Link from 'next/link';
  3. import type { IUserHasId } from '@growi/core';
  4. import { type IUser, isPopulated, type Ref } from '@growi/core';
  5. import { pagePathUtils } from '@growi/core/dist/utils';
  6. import { UserPicture } from '@growi/ui/dist/components';
  7. import { format } from 'date-fns/format';
  8. import { useTranslation } from 'next-i18next';
  9. import styles from './AuthorInfo.module.scss';
  10. const UserLabel = ({
  11. user,
  12. }: {
  13. user: IUserHasId | Ref<IUser>;
  14. }): JSX.Element => {
  15. if (isPopulated(user)) {
  16. return (
  17. <Link href={pagePathUtils.userHomepagePath(user)} prefetch={false}>
  18. {user.name}
  19. </Link>
  20. );
  21. }
  22. return <i>(anyone)</i>;
  23. };
  24. type AuthorInfoProps = {
  25. date: Date;
  26. user?: IUserHasId | Ref<IUser>;
  27. mode: 'create' | 'update';
  28. locate: 'pageSide' | 'footer';
  29. };
  30. export const AuthorInfo = (props: AuthorInfoProps): JSX.Element => {
  31. const { t } = useTranslation();
  32. const { date, user, mode = 'create', locate = 'pageSide' } = props;
  33. const formatType = 'yyyy/MM/dd HH:mm';
  34. const infoLabelForPageSide =
  35. mode === 'create'
  36. ? t('author_info.created_by')
  37. : t('author_info.updated_by');
  38. const nullinfoLabelForFooter =
  39. mode === 'create' ? 'Created by' : 'Updated by';
  40. const infoLabelForFooter =
  41. mode === 'create'
  42. ? t('author_info.created_at')
  43. : t('author_info.last_revision_posted_at');
  44. const userLabel = user != null ? <UserLabel user={user} /> : <i>Unknown</i>;
  45. if (locate === 'footer') {
  46. try {
  47. return (
  48. <p>
  49. {infoLabelForFooter} {format(new Date(date), formatType)} by{' '}
  50. <UserPicture user={user} size="sm" /> {userLabel}
  51. </p>
  52. );
  53. } catch (err) {
  54. if (err instanceof RangeError) {
  55. return (
  56. <p>
  57. {nullinfoLabelForFooter} <UserPicture user={user} size="sm" />{' '}
  58. {userLabel}
  59. </p>
  60. );
  61. }
  62. return <></>;
  63. }
  64. }
  65. const renderParsedDate = () => {
  66. try {
  67. return format(new Date(date), formatType);
  68. } catch (err) {
  69. return '';
  70. }
  71. };
  72. return (
  73. <div
  74. className={`grw-author-info ${styles['grw-author-info']} d-flex align-items-center mb-2`}
  75. >
  76. <div className="me-2 d-none d-lg-block">
  77. <UserPicture user={user} size="sm" />
  78. </div>
  79. <div>
  80. <div className="text-secondary mb-1">
  81. {infoLabelForPageSide} <br className="d-lg-none" />
  82. {userLabel}
  83. </div>
  84. <div className="text-secondary text-date" data-vrt-blackout-datetime>
  85. {renderParsedDate()}
  86. </div>
  87. </div>
  88. </div>
  89. );
  90. };