version/0.5 #3

Merged
lamacchinadesiderante merged 6 commits from version/0.5 into master 2023-01-11 18:37:31 +00:00
5 changed files with 171 additions and 118 deletions

View File

@ -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. 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 ## Installazione rapida
E' possibile buildare il progetto via Docker. Occorre avere prima installato e configurato sia Docker che il tool `docker-compose`. 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 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 ## 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. 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.

View File

@ -19,12 +19,12 @@ interface IProps {
const BookDetailsCard: React.FC<IProps> = (props) => { const BookDetailsCard: React.FC<IProps> = (props) => {
// const downloadLinkFromIPFS = (gateway: string, book: Book) => { const downloadLinkFromIPFS = (gateway: string, book: Book) => {
// return ( return (
// `https://${gateway}/ipfs/${book.ipfs_cid}?filename=` + `https://${gateway}/ipfs/${book.ipfs_cid}?filename=` +
// encodeURIComponent(`${book.title}_${book.author}.${book.extension}`) encodeURIComponent(`${book.title}_${book.author}.${book.extension}`)
// ); );
// } }
const { t } = useTranslation(); const { t } = useTranslation();
@ -88,7 +88,7 @@ const BookDetailsCard: React.FC<IProps> = (props) => {
</CardBody> </CardBody>
<CardFooter flexDirection="column"> <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) => ( {ipfsGateways.map((gateway) => (
<Button <Button
as={ExternalLink} as={ExternalLink}
@ -100,9 +100,9 @@ const BookDetailsCard: React.FC<IProps> = (props) => {
</Button> </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"> <Flex justify="flex-end">
<Button <Button

View File

@ -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')} &nbsp; <Icon as={IoShareSocialOutline} />
</Button>
</Flex>
</Box>}
</>
);
};
export default CopyToClipboardButton;

View File

@ -1,5 +1,6 @@
import { GridItem, Icon, SimpleGrid } from '@chakra-ui/react'; import { Button, GridItem, Icon, SimpleGrid, Flex, Box, Card } from '@chakra-ui/react';
import React, { useState } from 'react'; import React, { useEffect, useState } from 'react';
import MediaQuery from 'react-responsive'
import { import {
TbBook2, TbBook2,
TbBuilding, TbBuilding,
@ -11,10 +12,13 @@ import {
import search, { Book } from '../scripts/searcher'; import search, { Book } from '../scripts/searcher';
import { IoLanguage } from 'react-icons/io5'; import { IoLanguage } from 'react-icons/io5';
import { IoShareSocialOutline } from 'react-icons/io5';
import SearchInput from './SearchInput'; import SearchInput from './SearchInput';
import { useDebounceEffect } from 'ahooks'; import { useDebounceEffect } from 'ahooks';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import SearchLanguage from './SearchLanguage'; 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 { function constructQuery(parts: Record<string, string>): string {
return Object.keys(parts) return Object.keys(parts)
@ -44,6 +48,27 @@ const Search: React.FC<SearchProps> = ({ setBooks }) => {
const [complexQuery, setComplexQuery] = useState<string>(''); const [complexQuery, setComplexQuery] = useState<string>('');
const [showLanguageDropdown, setShowLanguageDropdown] = useState<boolean>(true) 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) => { const handleLanguageChange = (language: string) => {
if (language == 'input') { if (language == 'input') {
setShowLanguageDropdown(false) setShowLanguageDropdown(false)
@ -57,6 +82,28 @@ const Search: React.FC<SearchProps> = ({ setBooks }) => {
setLanguage('') 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( useDebounceEffect(
() => { () => {
const query = complexQuery const query = complexQuery
@ -72,66 +119,71 @@ const Search: React.FC<SearchProps> = ({ setBooks }) => {
); );
return ( return (
<SimpleGrid <>
columns={{ sm: 1, md: 2, lg: 3 }} <SimpleGrid
spacing={{ base: 2, md: 4 }} columns={{ sm: 1, md: 2, lg: 3 }}
px={{ base: 4, md: 8 }} spacing={{ base: 2, md: 4 }}
> px={{ base: 4, md: 8 }}
<SearchInput >
icon={<Icon as={TbBook2} />}
placeholder={t('book.title')}
value={title}
onChange={setTitle}
/>
<SearchInput
icon={<Icon as={TbUserCircle} />}
placeholder={t('book.author')}
value={author}
onChange={setAuthor}
/>
<SearchInput
icon={<Icon as={TbBuilding} />}
placeholder={t('book.publisher')}
value={publisher}
onChange={setPublisher}
/>
<SearchInput
icon={<Icon as={TbFileDescription} />}
placeholder={t('book.extension')}
value={extension}
onChange={setExtension}
/>
{!showLanguageDropdown && (<SearchInput
icon={<Icon as={IoLanguage} />}
placeholder={t('input.select_language')}
value={language}
onChange={handleLanguageChange}
onClear={handleLanguageReset}
/>)}
{showLanguageDropdown && (<SearchLanguage
icon={<Icon as={IoLanguage} />}
placeholder={t('input.select_language')}
value={language}
onChange={handleLanguageChange}
/>)}
<SearchInput
icon={<Icon as={TbHash} />}
placeholder={t('book.isbn')}
value={isbn}
onChange={setISBN}
/>
<GridItem colSpan={{ sm: 1, md: 2, lg: 3 }}>
<SearchInput <SearchInput
icon={<Icon as={TbReportSearch} />} icon={<Icon as={TbBook2} />}
placeholder={t('search.complex')} placeholder={t('book.title')}
value={complexQuery} value={title}
onChange={setComplexQuery} onChange={setTitle}
/> />
</GridItem> <SearchInput
</SimpleGrid> icon={<Icon as={TbUserCircle} />}
placeholder={t('book.author')}
value={author}
onChange={setAuthor}
/>
<SearchInput
icon={<Icon as={TbBuilding} />}
placeholder={t('book.publisher')}
value={publisher}
onChange={setPublisher}
/>
<SearchInput
icon={<Icon as={TbFileDescription} />}
placeholder={t('book.extension')}
value={extension}
onChange={setExtension}
/>
{!showLanguageDropdown && (<SearchInput
icon={<Icon as={IoLanguage} />}
placeholder={t('input.select_language')}
value={language}
onChange={handleLanguageChange}
onClear={handleLanguageReset}
/>)}
{showLanguageDropdown && (<SearchLanguage
icon={<Icon as={IoLanguage} />}
placeholder={t('input.select_language')}
value={language}
onChange={handleLanguageChange}
/>)}
<SearchInput
icon={<Icon as={TbHash} />}
placeholder={t('book.isbn')}
value={isbn}
onChange={setISBN}
/>
<GridItem colSpan={{ sm: 1, md: 2, lg: 3 }}>
<SearchInput
icon={<Icon as={TbReportSearch} />}
placeholder={t('search.complex')}
value={complexQuery}
onChange={setComplexQuery}
/>
</GridItem>
</SimpleGrid>
<CopyToClipboardButton
show={hasAnySearchBeenMade()}
onClick={copyToClipboard} />
</>
); );
}; };

View File

@ -39,7 +39,9 @@
"collapse": "Collapse" "collapse": "Collapse"
}, },
"search": { "search": {
"complex": "Complex search" "complex": "Complex search",
"copy_search_link": "Copy to clipboard",
"copy_success": "Copied!"
}, },
"settings": { "settings": {
"title": "Settings", "title": "Settings",
@ -205,7 +207,9 @@
"collapse": "Richiudi" "collapse": "Richiudi"
}, },
"search": { "search": {
"complex": "Ricerca dettagliata" "complex": "Ricerca dettagliata",
"copy_search_link": "Copia link ricerca",
"copy_success": "Copiato negli appunti!"
}, },
"settings": { "settings": {
"title": "Impostazioni", "title": "Impostazioni",