import React, {useContext, useState} from "react"; import { CardContent, CardDescription, CardHeader, CardMeta, Checkbox, Container, Dropdown, Form, Image, PlaceholderLine, Step, TableCell, TableHeaderCell, } from "semantic-ui-react"; import { CardGroupCentered, CardPoster, encodeMediaPath, ErrorMessage, ExternalCardLink, FileIcon, findPosterPath, HandPointMessage, humanFileSize, isoDatetimeToAgoPopup, mimetypeColor, PageContainer, Paginator, PreviewLink, TagIcon, textEllipsis, useTitle } from "./Common"; import { usePages, useSearchFiles, useSearchFilter, useSearchOrder, useSearchView, useStatusFlag, useWROLMode } from "../hooks/customHooks"; import {useFileWorkerStatus} from "../contexts/FileWorkerStatusContext"; import {Link, Route, Routes, useSearchParams} from "react-router"; import {CardPlaceholder} from "./Placeholder"; import {ArchiveCard, ArchiveRowCells} from "./Archive"; import Grid from "semantic-ui-react/dist/commonjs/collections/Grid"; import {Media, ThemeContext} from "../contexts/contexts"; import { Button, Card, Icon, Modal, Placeholder, Popup, Progress, Segment } from "./Theme"; import {SelectableTable} from "./Tables"; import {VideoCard, VideoRowCells} from "./Videos"; import {FileBrowser} from "./FileBrowser"; import {refreshFiles} from "../api"; import {TagsSelector} from "../Tags"; import {Headlines} from "./Headline"; import {useSearch} from "./Search"; import {FILES_MEDIA_URI} from "./Vars"; // Split a ts_headline snippet on our sentinel markers and wrap each match in . // We use sentinels instead of ts_headline's default tags so the snippet can be // safely rendered as React children without dangerouslySetInnerHTML. function renderHighlightedSnippet(snippet, s) { const parts = snippet.split(/\[\[WROLPI_HL\]\]|\[\[\/WROLPI_HL\]\]/); // Even indices are plain text, odd indices are highlighted matches. return parts.map((part, i) => i % 2 === 1 ? {part} : {part}); } function EbookCard({file, sortField}) { const {s, t} = useContext(ThemeContext); const [searchParams] = useSearchParams(); const query = searchParams.get('q') || ''; const downloadUrl = `/download/${encodeMediaPath(file.primary_path)}`; const isEpub = file['mimetype'] && file['mimetype'].startsWith('application/epub'); const viewerUrl = isEpub ? `/epub/epub.html?url=${downloadUrl}` : null; // Link to doc detail page if this is a modeled doc, augmenting with // the section hint from search so the viewer opens at the right chapter/page. const hint = file.section_hint; let detailUrl = file.model === 'doc' ? `/docs/${file.id}` : null; if (detailUrl && hint) { const params = new URLSearchParams(); if (hint.kind === 'epub_spine') params.set('loc', String(hint.ordinal)); if (hint.kind === 'pdf_page') params.set('page', String(hint.ordinal)); if (query) params.set('q', query); detailUrl = `${detailUrl}?${params.toString()}`; } const color = mimetypeColor(file.mimetype, file.primary_path); const title = file.title || file.stem || file.name; const header = detailUrl ? {title} : {title}; return {file.author ? {file.author} : null} {sortField === 'published_datetime' ?

{file.published_datetime ? isoDatetimeToAgoPopup(file.published_datetime, false) : null}

: file.size &&

{humanFileSize(file.size)}

} {hint &&

{hint.label} {hint.snippet && {renderHighlightedSnippet(hint.snippet, s)} }

}
} function ImageCard({file}) { const {s} = useContext(ThemeContext); const url = `/media/${encodeMediaPath(file.primary_path)}`; const title = file.title || file.stem || file.primary_path; const header =

{textEllipsis(title)}

; const dt = file.published_datetime || file.published_modified_datetime || file.modified; return

{isoDatetimeToAgoPopup(dt, false)}

{humanFileSize(file.size)}

} function FileCard({file}) { const {s} = useContext(ThemeContext); const {sort} = useSearchOrder(); const sortField = sort ? sort.replace(/^-/, '') : null; const isDocType = file.model === 'doc' || (file.mimetype && ( file.mimetype.startsWith('application/epub') || file.mimetype.startsWith('application/x-mobipocket-ebook') )); // Default doc sort is published_datetime when no order query param is set. const docSortField = sortField || 'published_datetime'; if (file.model === 'video' && 'video' in file) { return ; } else if (file.model === 'archive') { return ; } else if (file.mimetype && file.mimetype.startsWith('image/')) { return ; } else if (isDocType) { return ; } const author = file.author; const color = mimetypeColor(file.mimetype, file.primary_path); const size = file.size !== null && file.size !== undefined ? humanFileSize(file.size) : null; const title = file.title || file.name || file.primary_path; const dt = file.published_datetime || file.published_modified_datetime || file.modified; return {title}} content={title}/> {author && {author}} {sortField === 'published_datetime' ?

{file.published_datetime ? isoDatetimeToAgoPopup(file.published_datetime, false) : null}

: <>

{isoDatetimeToAgoPopup(dt, false)}

{size}

}
} export function FileCards({files}) { if (files && files.length >= 1) { return {files.map(i => )} } else if (files && files.length === 0) { return No results! } else if (files === undefined) { return Could not search! } // Response is pending. return } function ImageRowCells({file}) { const url = `/media/${encodeMediaPath(file.primary_path)}`; let poster = ; if (file.size && file.size < 50000000) { // Image is less than 5mb, use it. poster = ; } // Fragment for SelectableRow return
{poster}

{textEllipsis(file.title || file.stem || file.primary_path)}

{humanFileSize(file.size)}
} export function EbookRowCells({file}) { let cover = ; const posterPath = findPosterPath(file); if (posterPath) { const coverSrc = `/media/${encodeMediaPath(posterPath)}`; cover = ; } // Fragment for SelectableRow return
{cover}
{textEllipsis(file.title || file.stem)} {humanFileSize(file.size)}
} function FileRow({file}) { const isDocType = file.model === 'doc' || (file.mimetype && ( file.mimetype.startsWith('application/epub') || file.mimetype.startsWith('application/x-mobipocket-ebook') )); if (file.model === 'video' && 'video' in file) { return ; } else if (file.model === 'archive') { return ; } else if (file.mimetype && file.mimetype.startsWith('image/')) { return ; } else if (isDocType) { return ; } // Fragment for SelectableRow return
{textEllipsis(file.title || file.name || file.stem)}
} export function FileTable({files, selectOn, onSelect, footer, selectedKeys}) { if (files && files.length > 0) { const headerContents = ['Poster', 'Title', 'Data']; const rows = files.map(i => ); return ; } else if (files) { return No results! } else if (files === undefined) { return Could not search! } return } export function FileRowTagIcon({file}) { if (file.tags && file.tags.length) { return ; } } export function SearchViewButton({headlines}) { const {view, setView} = useSearchView(); if (headlines) { // Cycle: cards -> headline -> list if (view === 'headline') { return