version/0.5 #3
47
README.md
47
README.md
|
@ -10,20 +10,6 @@ Il progetto è partito come un fork di [Book Searcher](https://github.com/book-s
|
|||
|
||||
L'obiettivo è trasformarlo in una piattaforma partecipativa: gli utenti avranno la possibilità di richiedere l'aggiunta di libri, oltre a fare ricerche.
|
||||
|
||||
## Copyright e pirateria
|
||||
|
||||
Nessun materiale protetto da copyright è ospitato su questo repo.
|
||||
|
||||
Nessun link a materiale protetto da copyright è ospitato su questo repo.
|
||||
|
||||
Il motore fa ricerche su degli indici, e fornisce dei risultati.
|
||||
|
||||
Non abbiamo nulla contro la pirateria, ma vogliamo evitare di trovarci in situazioni spiacevoli.
|
||||
|
||||
Non riusciamo a capire in quali occasioni la condivisione di link esterni a materiale protetto da copyright sia reato e in quali no. Quindi, per tutelarci, abbiamo disabilitato la generazione automatica di link IPFS alle risorse. Tuttavia è possibile riabilitarla modificando il codice sorgente (spiegato sotto).
|
||||
|
||||
Viene fornita solo un'informazione parziale. Sta all'utente mettere insieme i pezzi.
|
||||
|
||||
## Installazione rapida
|
||||
|
||||
E' possibile buildare il progetto via Docker. Occorre avere prima installato e configurato sia Docker che il tool `docker-compose`.
|
||||
|
@ -48,39 +34,6 @@ npm run build
|
|||
npm run dev
|
||||
```
|
||||
|
||||
## Riabilitare la generazione dei link IPFS
|
||||
|
||||
Per riabilitare i bottoni/link ai Gateway IPFS, aprire il file `frontend/src/components/BooksDetailsCard.tsx` e de-commentare le seguenti linee di codice:
|
||||
|
||||
(da riga 22)
|
||||
|
||||
```
|
||||
// const downloadLinkFromIPFS = (gateway: string, book: Book) => {
|
||||
// return (
|
||||
// `https://${gateway}/ipfs/${book.ipfs_cid}?filename=` +
|
||||
// encodeURIComponent(`${book.title}_${book.author}.${book.extension}`)
|
||||
// );
|
||||
// }
|
||||
```
|
||||
|
||||
(da riga 91)
|
||||
|
||||
```
|
||||
{/* <SimpleGrid columns={{ sm: 2, md: 3, lg: 4, xl: 5 }} spacing={{ base: 2, md: 4 }}>
|
||||
{ipfsGateways.map((gateway) => (
|
||||
<Button
|
||||
as={ExternalLink}
|
||||
href={downloadLinkFromIPFS(gateway, row.original)}
|
||||
key={gateway}
|
||||
variant="outline"
|
||||
>
|
||||
{gateway}
|
||||
</Button>
|
||||
))}
|
||||
|
||||
</SimpleGrid> */}
|
||||
```
|
||||
|
||||
## Licenza
|
||||
|
||||
Book Searcher è rilasciato sotto licenza [BSD-3-Clause](https://github.com/book-searcher-org/book-searcher/blob/master/LICENSE). Millelibri eredita la licenza da Book Searcher.
|
|
@ -19,12 +19,12 @@ interface IProps {
|
|||
|
||||
const BookDetailsCard: React.FC<IProps> = (props) => {
|
||||
|
||||
// const downloadLinkFromIPFS = (gateway: string, book: Book) => {
|
||||
// return (
|
||||
// `https://${gateway}/ipfs/${book.ipfs_cid}?filename=` +
|
||||
// encodeURIComponent(`${book.title}_${book.author}.${book.extension}`)
|
||||
// );
|
||||
// }
|
||||
const downloadLinkFromIPFS = (gateway: string, book: Book) => {
|
||||
return (
|
||||
`https://${gateway}/ipfs/${book.ipfs_cid}?filename=` +
|
||||
encodeURIComponent(`${book.title}_${book.author}.${book.extension}`)
|
||||
);
|
||||
}
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
|
@ -88,7 +88,7 @@ const BookDetailsCard: React.FC<IProps> = (props) => {
|
|||
</CardBody>
|
||||
<CardFooter flexDirection="column">
|
||||
|
||||
{/* <SimpleGrid columns={{ sm: 2, md: 3, lg: 4, xl: 5 }} spacing={{ base: 2, md: 4 }}>
|
||||
<SimpleGrid columns={{ sm: 2, md: 3, lg: 4, xl: 5 }} spacing={{ base: 2, md: 4 }}>
|
||||
{ipfsGateways.map((gateway) => (
|
||||
<Button
|
||||
as={ExternalLink}
|
||||
|
@ -100,9 +100,9 @@ const BookDetailsCard: React.FC<IProps> = (props) => {
|
|||
</Button>
|
||||
))}
|
||||
|
||||
</SimpleGrid> */}
|
||||
</SimpleGrid>
|
||||
|
||||
<Flex><Text fontWeight={'bold'}>{t('disclaimer.nolink_warning')}</Text></Flex>
|
||||
{/* <Flex><Text fontWeight={'bold'}>{t('disclaimer.nolink_warning')}</Text></Flex> */}
|
||||
|
||||
<Flex justify="flex-end">
|
||||
<Button
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
import { Box, Button, Card, Flex, Icon, Spacer } from '@chakra-ui/react';
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { IoShareSocialOutline } from 'react-icons/io5';
|
||||
import MediaQuery from 'react-responsive';
|
||||
import { MEDIA_QUERY_DESKTOP_STARTS, MEDIA_QUERY_MOBILE_ENDS } from '../constants/mediaquery';
|
||||
|
||||
export interface IProps {
|
||||
show: boolean
|
||||
onClick(): void
|
||||
}
|
||||
|
||||
const CopyToClipboardButton: React.FC<IProps> = (props) => {
|
||||
|
||||
const { show, onClick } = props
|
||||
|
||||
const [showSuccess, setShowSuccess] = useState<boolean>(false)
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleClick = () => {
|
||||
onClick()
|
||||
setShowSuccess(true)
|
||||
|
||||
setTimeout(() => {
|
||||
setShowSuccess(false)
|
||||
}, 3000)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{show &&
|
||||
<Box p='4' mx={{ base: 0, md: 4 }}>
|
||||
<Flex justifyContent={'flex-end'} alignItems={'center'}>
|
||||
<Button bgColor={showSuccess ? 'green.400' : ''} onClick={handleClick}>
|
||||
{showSuccess ? t('search.copy_success') : t('search.copy_search_link')} <Icon as={IoShareSocialOutline} />
|
||||
</Button>
|
||||
</Flex>
|
||||
</Box>}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default CopyToClipboardButton;
|
|
@ -1,5 +1,6 @@
|
|||
import { GridItem, Icon, SimpleGrid } from '@chakra-ui/react';
|
||||
import React, { useState } from 'react';
|
||||
import { Button, GridItem, Icon, SimpleGrid, Flex, Box, Card } from '@chakra-ui/react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import MediaQuery from 'react-responsive'
|
||||
import {
|
||||
TbBook2,
|
||||
TbBuilding,
|
||||
|
@ -11,10 +12,13 @@ import {
|
|||
import search, { Book } from '../scripts/searcher';
|
||||
|
||||
import { IoLanguage } from 'react-icons/io5';
|
||||
import { IoShareSocialOutline } from 'react-icons/io5';
|
||||
import SearchInput from './SearchInput';
|
||||
import { useDebounceEffect } from 'ahooks';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import SearchLanguage from './SearchLanguage';
|
||||
import { MEDIA_QUERY_DESKTOP_STARTS, MEDIA_QUERY_MOBILE_ENDS } from '../constants/mediaquery';
|
||||
import CopyToClipboardButton from './CopyToClipboardButton';
|
||||
|
||||
function constructQuery(parts: Record<string, string>): string {
|
||||
return Object.keys(parts)
|
||||
|
@ -44,6 +48,27 @@ const Search: React.FC<SearchProps> = ({ setBooks }) => {
|
|||
const [complexQuery, setComplexQuery] = useState<string>('');
|
||||
const [showLanguageDropdown, setShowLanguageDropdown] = useState<boolean>(true)
|
||||
|
||||
useEffect(() => {
|
||||
const params = new Proxy(new URLSearchParams(decodeURIComponent(window.location.search)), {
|
||||
//@ts-ignore
|
||||
get: (searchParams, prop) => searchParams.get(prop),
|
||||
});
|
||||
|
||||
//@ts-ignore
|
||||
params.title && setTitle(String(params.title))
|
||||
|
||||
//@ts-ignore
|
||||
params.author && setAuthor(String(params.author))
|
||||
|
||||
//@ts-ignore
|
||||
if (params.language) {
|
||||
//@ts-ignore
|
||||
setLanguage(String(params.language))
|
||||
setShowLanguageDropdown(false)
|
||||
}
|
||||
|
||||
}, [])
|
||||
|
||||
const handleLanguageChange = (language: string) => {
|
||||
if (language == 'input') {
|
||||
setShowLanguageDropdown(false)
|
||||
|
@ -57,6 +82,28 @@ const Search: React.FC<SearchProps> = ({ setBooks }) => {
|
|||
setLanguage('')
|
||||
}
|
||||
|
||||
const hasAnySearchBeenMade = () => {
|
||||
return (
|
||||
title !== ''
|
||||
|| author !== ''
|
||||
|| language !== ''
|
||||
|| publisher !== ''
|
||||
|| extension !== ''
|
||||
|| isbn !== ''
|
||||
)
|
||||
}
|
||||
|
||||
const copyToClipboard = () => {
|
||||
const searchBase = `${window.location.host}/?`;
|
||||
const searchQuery = encodeURIComponent(`title=${title}&author=${author}&language=${language}&publisher=${publisher}&isbn=${isbn}&extension=${extension}`)
|
||||
|
||||
navigator.clipboard.writeText(`${searchBase}${searchQuery}`).then(() => {
|
||||
//console.log('Async: Copying to clipboard was successful!');
|
||||
}, (err) => {
|
||||
//console.error('Async: Could not copy text: ', err);
|
||||
});
|
||||
}
|
||||
|
||||
useDebounceEffect(
|
||||
() => {
|
||||
const query = complexQuery
|
||||
|
@ -72,6 +119,7 @@ const Search: React.FC<SearchProps> = ({ setBooks }) => {
|
|||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SimpleGrid
|
||||
columns={{ sm: 1, md: 2, lg: 3 }}
|
||||
spacing={{ base: 2, md: 4 }}
|
||||
|
@ -132,6 +180,10 @@ const Search: React.FC<SearchProps> = ({ setBooks }) => {
|
|||
/>
|
||||
</GridItem>
|
||||
</SimpleGrid>
|
||||
<CopyToClipboardButton
|
||||
show={hasAnySearchBeenMade()}
|
||||
onClick={copyToClipboard} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -39,7 +39,9 @@
|
|||
"collapse": "Collapse"
|
||||
},
|
||||
"search": {
|
||||
"complex": "Complex search"
|
||||
"complex": "Complex search",
|
||||
"copy_search_link": "Copy to clipboard",
|
||||
"copy_success": "Copied!"
|
||||
},
|
||||
"settings": {
|
||||
"title": "Settings",
|
||||
|
@ -205,7 +207,9 @@
|
|||
"collapse": "Richiudi"
|
||||
},
|
||||
"search": {
|
||||
"complex": "Ricerca dettagliata"
|
||||
"complex": "Ricerca dettagliata",
|
||||
"copy_search_link": "Copia link ricerca",
|
||||
"copy_success": "Copiato negli appunti!"
|
||||
},
|
||||
"settings": {
|
||||
"title": "Impostazioni",
|
||||
|
|
Loading…
Reference in New Issue