RichAttachment.tsx 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. import React, { useCallback } from 'react';
  2. import { UserPicture } from '@growi/ui/dist/components/User/UserPicture';
  3. import { useTranslation } from 'next-i18next';
  4. import prettyBytes from 'pretty-bytes';
  5. import { useSWRxAttachment } from '~/stores/attachment';
  6. import { useDeleteAttachmentModal } from '~/stores/modal';
  7. import styles from './RichAttachment.module.scss';
  8. export const RichAttachment: React.FC<{
  9. attachmentId: string,
  10. url: string,
  11. attachmentName: string
  12. }> = React.memo(({ attachmentId, url, attachmentName }) => {
  13. const { t } = useTranslation();
  14. const { data: attachment, remove } = useSWRxAttachment(attachmentId);
  15. const { open: openDeleteAttachmentModal } = useDeleteAttachmentModal();
  16. const onClickTrashButtonHandler = useCallback(() => {
  17. if (attachment == null) {
  18. return;
  19. }
  20. openDeleteAttachmentModal(attachment, remove);
  21. }, [attachment, openDeleteAttachmentModal, remove]);
  22. if (attachment == null) {
  23. return <span className='text-muted'>{t('rich_attachment.attachment_not_be_found')}</span>;
  24. }
  25. const {
  26. filePathProxied,
  27. originalName,
  28. downloadPathProxied,
  29. creator,
  30. createdAt,
  31. fileSize,
  32. } = attachment;
  33. // Guard here because attachment properties might be deleted in turn when an attachment is removed
  34. if (filePathProxied == null
  35. || originalName == null
  36. || downloadPathProxied == null
  37. || creator == null
  38. || createdAt == null
  39. || fileSize == null
  40. ) {
  41. return <span className='text-muted'>{t('rich_attachment.attachment_not_be_found')}</span>;
  42. }
  43. return (
  44. <div className={`${styles.attachment} d-inline-block`}>
  45. <div className="my-2 p-2 card">
  46. <div className="p-1 card-body d-flex align-items-center">
  47. <div className='mr-2 px-0 d-flex align-items-center justify-content-center'>
  48. <img src='/images/icons/editor/attachment.svg' className="attachment-icon" alt='attachment icon'/>
  49. </div>
  50. <div className='pl-0'>
  51. <div className='d-inline-block'>
  52. <a target="_blank" rel="noopener noreferrer" href={filePathProxied}>
  53. {attachmentName || originalName}
  54. </a>
  55. <a className="ml-2 attachment-download" href={downloadPathProxied}>
  56. <i className="icon-cloud-download"/>
  57. </a>
  58. <a className="ml-2 text-danger attachment-delete" onClick={onClickTrashButtonHandler}>
  59. <i className="icon-trash"/>
  60. </a>
  61. </div>
  62. <div className='d-flex align-items-center'>
  63. <UserPicture user={creator} size="sm"/>
  64. <span className='ml-2 text-muted'>
  65. {new Date(createdAt).toLocaleString('en-US')}
  66. </span>
  67. <span className='ml-2 pl-2 border-left text-muted'>{prettyBytes(fileSize)}</span>
  68. </div>
  69. </div>
  70. </div>
  71. </div>
  72. </div>
  73. );
  74. });
  75. RichAttachment.displayName = 'RichAttachment';