From 2a8a280a03fb2e0c5a65e735cfdd9f0d8fbafc5e Mon Sep 17 00:00:00 2001 From: Jennifer Chen Date: Wed, 4 Dec 2024 22:38:05 -0800 Subject: [PATCH] one click copy default (AGU) citation in results and abstracts --- package.json | 5 ++ pnpm-lock.yaml | 78 +++++++++++++++++++ .../ResultList/Item/ItemResourceDropdowns.tsx | 14 +++- src/pages/abs/[id]/abstract.tsx | 57 +++++++++++--- 4 files changed, 140 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 5e8b779aa..55a47294d 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,9 @@ "@dnd-kit/utilities": "^3.2.2", "@emotion/react": "11.8.2", "@emotion/styled": "^11.10.5", + "@fortawesome/fontawesome-svg-core": "^6.7.0", + "@fortawesome/free-solid-svg-icons": "^6.7.0", + "@fortawesome/react-fontawesome": "^0.2.2", "@heroicons/react": "^2.0.16", "@hookform/resolvers": "^3.3.1", "@next/bundle-analyzer": "^10.2.3", @@ -90,6 +93,8 @@ "ramda-adjunct": "^3.3.0", "react": "^18.3.1", "react-compound-slider": "^3.4.0", + "react-copy-html-to-clipboard": "^6.0.4", + "react-copy-to-clipboard": "^5.1.0", "react-device-detect": "^2.2.3", "react-error-boundary": "^4.0.4", "react-google-recaptcha-v3": "^1.10.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0625ff8d7..0a06d156f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -41,6 +41,15 @@ importers: '@emotion/styled': specifier: ^11.10.5 version: 11.10.5(@babel/core@7.22.10)(@emotion/react@11.8.2(@babel/core@7.22.10)(@types/react@17.0.43)(react@18.3.1))(@types/react@17.0.43)(react@18.3.1) + '@fortawesome/fontawesome-svg-core': + specifier: ^6.7.0 + version: 6.7.0 + '@fortawesome/free-solid-svg-icons': + specifier: ^6.7.0 + version: 6.7.0 + '@fortawesome/react-fontawesome': + specifier: ^0.2.2 + version: 0.2.2(@fortawesome/fontawesome-svg-core@6.7.0)(react@18.3.1) '@heroicons/react': specifier: ^2.0.16 version: 2.0.16(react@18.3.1) @@ -206,6 +215,12 @@ importers: react-compound-slider: specifier: ^3.4.0 version: 3.4.0(react@18.3.1) + react-copy-html-to-clipboard: + specifier: ^6.0.4 + version: 6.0.4(react@18.3.1) + react-copy-to-clipboard: + specifier: ^5.1.0 + version: 5.1.0(react@18.3.1) react-device-detect: specifier: ^2.2.3 version: 2.2.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -2649,6 +2664,24 @@ packages: '@floating-ui/utils@0.1.6': resolution: {integrity: sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==} + '@fortawesome/fontawesome-common-types@6.7.0': + resolution: {integrity: sha512-AUetZXU6cQdAe21p8j3mg2aD40MMDKfFNUNgq/G7gR3HMDp0BsQskAudLDSgq6d0SbCY0QKP0g4s5Y02S1kkhw==} + engines: {node: '>=6'} + + '@fortawesome/fontawesome-svg-core@6.7.0': + resolution: {integrity: sha512-v6YZjSPuxriC7lYxCzKFbgZ1iaf60AVX2CsfZXSc0U9+mqVd8VGVtMEqDqz5GxDpNUQ8bMDfW+gspVMYGlRpUA==} + engines: {node: '>=6'} + + '@fortawesome/free-solid-svg-icons@6.7.0': + resolution: {integrity: sha512-9ww5hQ3OzEehUrSXAlPTJ73xDub73fnxr+se5PU0MFQor2nZBO0m7HNm5Q4KD9XMYjwRqh2BnBNR2/9EFbGqmg==} + engines: {node: '>=6'} + + '@fortawesome/react-fontawesome@0.2.2': + resolution: {integrity: sha512-EnkrprPNqI6SXJl//m29hpaNzOp1bruISWaOiRtkMi/xSvHJlzc2j2JAYS7egxt/EbjSNV/k6Xy0AQI6vB2+1g==} + peerDependencies: + '@fortawesome/fontawesome-svg-core': ~1 || ~6 + react: '>=16.3' + '@heroicons/react@2.0.16': resolution: {integrity: sha512-x89rFxH3SRdYaA+JCXwfe+RkE1SFTo9GcOkZettHer71Y3T7V+ogKmfw5CjTazgS3d0ClJ7p1NA+SP7VQLQcLw==} peerDependencies: @@ -5800,6 +5833,9 @@ packages: resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==} engines: {node: '>=12.13'} + copy-html-to-clipboard@4.0.1: + resolution: {integrity: sha512-uvEr7smwMQReydH7XzSgpxM1SywZfxS+1NLhlSvzLIGz8esPbY248y1Tg7LTYsr/yVP/1kUyf8Ypgd9ljJJkBw==} + copy-to-clipboard@3.3.1: resolution: {integrity: sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw==} @@ -8799,6 +8835,16 @@ packages: peerDependencies: react: '>=16.9' + react-copy-html-to-clipboard@6.0.4: + resolution: {integrity: sha512-lUV84N9OiL3exfM+lK7XEN30xgK7XLRqAqcr+8m9ZtQ82xGUjT7fCWwvOS88YH/TaANqokWWZRtrgSMK1m8huw==} + peerDependencies: + react: ^15.3.0 + + react-copy-to-clipboard@5.1.0: + resolution: {integrity: sha512-k61RsNgAayIJNoy9yDsYzDe/yAZAzEbEgcz3DZMhF686LEyukcE1hzurxe85JandPUG+yTfGVFzuEw3xt8WP/A==} + peerDependencies: + react: ^15.3.0 || 16 || 17 || 18 + react-device-detect@2.2.3: resolution: {integrity: sha512-buYY3qrCnQVlIFHrC5UcUoAj7iANs/+srdkwsnNjI7anr3Tt7UY6MqNxtMLlr0tMBied0O49UZVK8XKs3ZIiPw==} peerDependencies: @@ -13128,6 +13174,22 @@ snapshots: '@floating-ui/utils@0.1.6': {} + '@fortawesome/fontawesome-common-types@6.7.0': {} + + '@fortawesome/fontawesome-svg-core@6.7.0': + dependencies: + '@fortawesome/fontawesome-common-types': 6.7.0 + + '@fortawesome/free-solid-svg-icons@6.7.0': + dependencies: + '@fortawesome/fontawesome-common-types': 6.7.0 + + '@fortawesome/react-fontawesome@0.2.2(@fortawesome/fontawesome-svg-core@6.7.0)(react@18.3.1)': + dependencies: + '@fortawesome/fontawesome-svg-core': 6.7.0 + prop-types: 15.8.1 + react: 18.3.1 + '@heroicons/react@2.0.16(react@18.3.1)': dependencies: react: 18.3.1 @@ -17240,6 +17302,10 @@ snapshots: dependencies: is-what: 4.1.15 + copy-html-to-clipboard@4.0.1: + dependencies: + toggle-selection: 1.0.6 + copy-to-clipboard@3.3.1: dependencies: toggle-selection: 1.0.6 @@ -20821,6 +20887,18 @@ snapshots: react: 18.3.1 warning: 4.0.3 + react-copy-html-to-clipboard@6.0.4(react@18.3.1): + dependencies: + copy-html-to-clipboard: 4.0.1 + prop-types: 15.8.1 + react: 18.3.1 + + react-copy-to-clipboard@5.1.0(react@18.3.1): + dependencies: + copy-to-clipboard: 3.3.1 + prop-types: 15.8.1 + react: 18.3.1 + react-device-detect@2.2.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: react: 18.3.1 diff --git a/src/components/ResultList/Item/ItemResourceDropdowns.tsx b/src/components/ResultList/Item/ItemResourceDropdowns.tsx index e7857dd05..2b250e531 100644 --- a/src/components/ResultList/Item/ItemResourceDropdowns.tsx +++ b/src/components/ResultList/Item/ItemResourceDropdowns.tsx @@ -13,6 +13,7 @@ import { useGetExportCitation } from '@/api/export/export'; import { useSettings } from '@/lib/useSettings'; import { exportFormats } from '@/components/CitationExporter/models'; import { ExportApiFormatKey } from '@/api/export/types'; +import CopyToClipboard from 'react-copy-html-to-clipboard'; export interface IItemResourceDropdownsProps { doc: IDocsEntity; @@ -170,8 +171,12 @@ export const ItemResourceDropdowns = ({ doc }: IItemResourceDropdownsProps): Rea setValue(`${process.env.NEXT_PUBLIC_BASE_CANONICAL_URL}/abs/${doc.bibcode}/abstract`); }; - const handleCopyCitation = () => { - setValue(citationData.export); + const handleCitationCopied = () => { + if (citationData?.export) { + toast({ status: 'info', title: 'Copied to Clipboard' }); + } else { + toast({ status: 'error', title: 'There was a problem fetching citation' }); + } }; return ( @@ -294,7 +299,10 @@ export const ItemResourceDropdowns = ({ doc }: IItemResourceDropdownsProps): Rea /> Copy URL - Copy Default Citation + + + Copy Citation + diff --git a/src/pages/abs/[id]/abstract.tsx b/src/pages/abs/[id]/abstract.tsx index 08b28b01f..63ff94c3d 100644 --- a/src/pages/abs/[id]/abstract.tsx +++ b/src/pages/abs/[id]/abstract.tsx @@ -21,6 +21,7 @@ import { Tr, useDisclosure, VisuallyHidden, + useToast, } from '@chakra-ui/react'; import { EditIcon, ExternalLinkIcon, TriangleDownIcon } from '@chakra-ui/icons'; @@ -56,6 +57,11 @@ import { parseAPIError } from '@/utils/common/parseAPIError'; import { fetchSearchSSR, searchKeys, useGetAbstract } from '@/api/search/search'; import { IADSApiSearchParams, IDocsEntity } from '@/api/search/types'; import { getAbstractParams } from '@/api/search/models'; +import { useGetExportCitation } from '@/api/export/export'; +import { exportFormats } from '@/components/CitationExporter'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faQuoteLeft } from '@fortawesome/free-solid-svg-icons'; +import CopyToClipboard from 'react-copy-html-to-clipboard'; const AllAuthorsModal = dynamic( () => @@ -144,17 +150,20 @@ const AbstractPage: NextPage = () => { - {isAuthenticated && ( - - } - variant="ghost" - onClick={onOpenAddToLibrary} - /> - - )} + + {isAuthenticated && ( + + } + variant="ghost" + onClick={onOpenAddToLibrary} + /> + + )} + + Abstract @@ -187,6 +196,21 @@ interface IDetailsProps { const Details = ({ doc }: IDetailsProps): ReactElement => { const arxiv = (doc.identifier ?? ([] as string[])).find((v) => /^arxiv/i.exec(v)); + const { data: citationData, isLoading: isLoadingCitation } = useGetExportCitation({ + format: exportFormats.agu.id, + bibcode: [doc.bibcode], + }); + + const toast = useToast({ duration: 2000 }); + + const handleCitationCopied = () => { + if (citationData?.export) { + toast({ status: 'info', title: 'Copied to Clipboard' }); + } else { + toast({ status: 'error', title: 'There was a problem fetching citation' }); + } + }; + return ( @@ -195,7 +219,18 @@ const Details = ({ doc }: IDetailsProps): ReactElement => { - {(pub_raw) => } + {(pub_raw) => ( + <> + + {!isLoadingCitation && ( + + + + )} + + )}