AuthorInfo.tsx 2.3 KB

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