Skip to content

Commit d111dc3

Browse files
authored
chore(dev): backmerge from master (#1212)
2 parents f3b84e2 + 8d9fa7e commit d111dc3

File tree

9 files changed

+222
-520
lines changed

9 files changed

+222
-520
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@
238238
"react-localization": "^1.0.15",
239239
"react-markdown": "^7.1.1",
240240
"react-number-format": "^5.1.2",
241+
"react-reader": "^2.0.10",
241242
"react-redux": "^8.0.5",
242243
"react-router-dom": "^6.9.0",
243244
"react-textarea-autosize": "^8.2.0",

src/components/ContentItem/contentItem.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
// TODO: refactor needed
22
import { useEffect } from 'react';
33
import { Link } from 'react-router-dom';
4-
import { $TsFixMe } from 'src/types/tsfix';
5-
import type { IpfsContentType } from 'src/services/ipfs/types';
6-
import useParticle from 'src/hooks/useParticle';
7-
84
import { LinksType } from 'src/containers/Search/types';
5+
import useParticle from 'src/hooks/useParticle';
6+
import type { IpfsContentType } from 'src/services/ipfs/types';
7+
import { $TsFixMe } from 'src/types/tsfix';
98

109
import SearchItem from '../SearchItem/searchItem';
1110

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import React, {
2+
CSSProperties,
3+
ComponentProps,
4+
useCallback,
5+
useMemo,
6+
} from 'react';
7+
import { ReactReader } from 'react-reader';
8+
import useEPubLocation from 'src/hooks/useEPubLocation';
9+
10+
interface IProps {
11+
url: string;
12+
search?: boolean;
13+
style?: CSSProperties;
14+
}
15+
16+
type EpubInitOptions = ComponentProps<typeof ReactReader>['epubInitOptions'];
17+
const epubInitOptions: EpubInitOptions = { openAs: 'epub' };
18+
19+
function EPubView({ url, search, style }: IProps) {
20+
const [location, setLocation] = useEPubLocation(url);
21+
const currentLocation = location && !search ? location : 0;
22+
23+
const currentStyle = useMemo(
24+
() => ({ height: search ? '300px' : '60vh', ...style }),
25+
[style, search]
26+
);
27+
28+
const onLocationChange = useCallback(
29+
(loc: string) => {
30+
if (!search) {
31+
setLocation(loc);
32+
}
33+
},
34+
[search, setLocation]
35+
);
36+
37+
return (
38+
<div style={currentStyle}>
39+
<ReactReader
40+
url={url}
41+
location={currentLocation}
42+
locationChanged={onLocationChange}
43+
epubInitOptions={epubInitOptions}
44+
/>
45+
</div>
46+
);
47+
}
48+
49+
export default React.memo(EPubView);

src/components/contentIpfs/contentIpfs.tsx

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1-
import { IPFSContent, IPFSContentDetails } from 'src/services/ipfs/types';
21
import { CYBER_GATEWAY } from 'src/constants/config';
2+
import { CYBER_GATEWAY_URL } from 'src/services/ipfs/config';
3+
import { IPFSContent, IPFSContentDetails } from 'src/services/ipfs/types';
4+
import { Option } from 'src/types';
5+
import EPubView from '../EPubView/EPubView';
6+
import Pdf from '../PDF';
7+
import TextMarkdown from '../TextMarkdown';
38
import VideoPlayerGatewayOnly from '../VideoPlayer/VideoPlayerGatewayOnly';
9+
import Audio from './component/Audio/Audio';
410
import GatewayContent from './component/gateway';
5-
import TextMarkdown from '../TextMarkdown';
6-
import LinkHttp from './component/link';
7-
import Pdf from '../PDF';
811
import Img from './component/img';
9-
import Audio from './component/Audio/Audio';
10-
import { Option } from 'src/types';
12+
import LinkHttp from './component/link';
1113

1214
function OtherItem({
1315
content,
@@ -83,6 +85,12 @@ function ContentIpfs({ details, content, cid, search }: ContentTabProps) {
8385
<LinkHttp url={details.content!} preview={search} />
8486
)}
8587
{contentType === 'html' && <HtmlItem cid={content?.cid} />}
88+
{contentType === 'epub' && (
89+
<EPubView
90+
url={`${CYBER_GATEWAY_URL}/ipfs/${cid}`}
91+
search={search}
92+
/>
93+
)}
8694
{['other', 'cid'].some((i) => i === contentType) && (
8795
<OtherItem search={search} cid={cid} content={details.content} />
8896
)}
@@ -91,4 +99,5 @@ function ContentIpfs({ details, content, cid, search }: ContentTabProps) {
9199
</div>
92100
);
93101
}
102+
94103
export default ContentIpfs;

src/hooks/useEPubLocation.tsx

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/* eslint-disable import/prefer-default-export */
2+
import { useCallback } from 'react';
3+
import type { EpubView } from 'react-reader';
4+
5+
const epubKey = 'cyb:epub';
6+
7+
const getEPubMap = (): Record<string, EpubView['location']> => {
8+
try {
9+
const epubMapString = localStorage.getItem(epubKey) || '';
10+
const epubMap = JSON.parse(epubMapString);
11+
12+
return epubMap;
13+
} catch (error) {
14+
console.error('Failed to parse epub locations map:', error);
15+
16+
return {};
17+
}
18+
};
19+
20+
const getLocation = (url: string) => {
21+
const epubMap = getEPubMap();
22+
23+
return epubMap[url] ?? null;
24+
};
25+
26+
const getSetEPubLocation =
27+
(url: string) => (location: EpubView['location']) => {
28+
const epubMap = getEPubMap();
29+
30+
try {
31+
epubMap[url] = location;
32+
localStorage.setItem(epubKey, JSON.stringify(epubMap));
33+
} catch (error) {
34+
console.error('Failed to save EPub location:', error);
35+
}
36+
};
37+
38+
const useEPubLocation = (
39+
url: string
40+
): [EpubView['location'], (url: string) => void] => {
41+
const currentLocation = getLocation(url);
42+
const setEPubLocation = useCallback(getSetEPubLocation(url), [url]);
43+
44+
return [currentLocation, setEPubLocation];
45+
};
46+
47+
export default useEPubLocation;

src/index.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,10 @@
6969
src="https://metrics.cyb.ai/js/script.js"
7070
></script>
7171
</body>
72+
73+
<script
74+
defer
75+
data-domain="ready.cyb.ai"
76+
src="https://metrics.cyb.ai/js/script.js"
77+
></script>
7278
</html>

src/services/ipfs/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ export type Uint8ArrayLike = Uint8Array | AsyncIterator<Uint8Array>; // | Readab
6666

6767
export type IpfsContentSource = 'db' | 'node' | 'gateway';
6868

69-
export type IpfsGatewayContentType = 'video' | 'audio';
69+
export type IpfsGatewayContentType = 'video' | 'audio' | 'epub';
7070
export type MimeBasedContentType = 'image' | 'pdf' | 'text' | 'other';
7171
export type IpfsBaseContentType =
7272
| IpfsGatewayContentType

src/services/ipfs/utils/content.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ import isSvg from 'is-svg';
33
import { PATTERN_HTTP, PATTERN_IPFS_HASH } from 'src/constants/patterns';
44
import { Option } from 'src/types';
55

6+
import { shortenString } from 'src/utils/string';
67
import {
78
IPFSContentDetails,
89
IPFSContent,
910
IpfsContentType,
1011
IpfsGatewayContentType,
1112
} from '../types';
1213
import { getResponseResult, onProgressCallback } from './stream';
13-
import { shortenString } from 'src/utils/string';
1414

1515
function createObjectURL(rawData: Uint8Array, type: string) {
1616
const blob = new Blob([rawData], { type });
@@ -35,6 +35,10 @@ export const detectGatewayContentType = (
3535
if (mime.includes('audio')) {
3636
return 'audio';
3737
}
38+
39+
if (mime.includes('epub')) {
40+
return 'epub';
41+
}
3842
}
3943
return undefined;
4044
};

0 commit comments

Comments
 (0)