mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
Bump image-size from 1.2.0 to 1.2.1 (#4567) Bumps [image-size](https://github.com/image-size/image-size) from 1.2.0 to 1.2.1. - [Release notes](https://github.com/image-size/image-size/releases) - [Commits](https://github.com/image-size/image-size/compare/v1.2.0...v1.2.1) --- updated-dependencies: - dependency-name: image-size dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> fix(): Add network timeout to all yarn installs fix(): remove node-buffer Bump brace-expansion from 1.1.11 to 1.1.12 in /pkg/admin (#4593) Bumps [brace-expansion](https://github.com/juliangruber/brace-expansion) from 1.1.11 to 1.1.12. - [Release notes](https://github.com/juliangruber/brace-expansion/releases) - [Commits](https://github.com/juliangruber/brace-expansion/compare/1.1.11...v1.1.12) --- updated-dependencies: - dependency-name: brace-expansion dependency-version: 1.1.12 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Bump @babel/runtime-corejs3 from 7.26.0 to 7.28.2 (#4592) Bumps [@babel/runtime-corejs3](https://github.com/babel/babel/tree/HEAD/packages/babel-runtime-corejs3) from 7.26.0 to 7.28.2. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.28.2/packages/babel-runtime-corejs3) --- updated-dependencies: - dependency-name: "@babel/runtime-corejs3" dependency-version: 7.28.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Bump next from 13.5.8 to 14.2.30 (#4591) Bumps [next](https://github.com/vercel/next.js) from 13.5.8 to 14.2.30. - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](https://github.com/vercel/next.js/compare/v13.5.8...v14.2.30) --- updated-dependencies: - dependency-name: next dependency-version: 14.2.30 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Bump axios from 0.27.2 to 0.30.0 (#4590) Bumps [axios](https://github.com/axios/axios) from 0.27.2 to 0.30.0. - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v0.30.0/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v0.27.2...v0.30.0) --- updated-dependencies: - dependency-name: axios dependency-version: 0.30.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Bump @babel/runtime from 7.14.6 to 7.28.2 in /pkg/admin (#4589) Bumps [@babel/runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-runtime) from 7.14.6 to 7.28.2. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.28.2/packages/babel-runtime) --- updated-dependencies: - dependency-name: "@babel/runtime" dependency-version: 7.28.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Bump @radix-ui/react-separator from 0.1.4 to 1.1.6 (#4579) Bumps [@radix-ui/react-separator](https://github.com/radix-ui/primitives) from 0.1.4 to 1.1.6. - [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md) - [Commits](https://github.com/radix-ui/primitives/commits) --- updated-dependencies: - dependency-name: "@radix-ui/react-separator" dependency-version: 1.1.6 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Bump http-proxy-middleware from 2.0.7 to 2.0.9 (#4574) Bumps [http-proxy-middleware](https://github.com/chimurai/http-proxy-middleware) from 2.0.7 to 2.0.9. - [Release notes](https://github.com/chimurai/http-proxy-middleware/releases) - [Changelog](https://github.com/chimurai/http-proxy-middleware/blob/v2.0.9/CHANGELOG.md) - [Commits](https://github.com/chimurai/http-proxy-middleware/compare/v2.0.7...v2.0.9) --- updated-dependencies: - dependency-name: http-proxy-middleware dependency-version: 2.0.9 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Bump @babel/helpers from 7.14.6 to 7.28.2 in /pkg/admin (#4594) Bumps [@babel/helpers](https://github.com/babel/babel/tree/HEAD/packages/babel-helpers) from 7.14.6 to 7.28.2. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.28.2/packages/babel-helpers) --- updated-dependencies: - dependency-name: "@babel/helpers" dependency-version: 7.28.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Bump pg and @types/pg (#4547) Bumps [pg](https://github.com/brianc/node-postgres/tree/HEAD/packages/pg) and [@types/pg](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/pg). These dependencies needed to be updated together. Updates `pg` from 8.13.1 to 8.13.3 - [Changelog](https://github.com/brianc/node-postgres/blob/master/CHANGELOG.md) - [Commits](https://github.com/brianc/node-postgres/commits/pg@8.13.3/packages/pg) Updates `@types/pg` from 8.11.10 to 8.11.11 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/pg) --- updated-dependencies: - dependency-name: pg dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: "@types/pg" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Bump @graphql-tools/utils from 9.2.1 to 10.8.6 (#4560) Bumps [@graphql-tools/utils](https://github.com/ardatan/graphql-tools/tree/HEAD/packages/utils) from 9.2.1 to 10.8.6. - [Release notes](https://github.com/ardatan/graphql-tools/releases) - [Changelog](https://github.com/ardatan/graphql-tools/blob/master/packages/utils/CHANGELOG.md) - [Commits](https://github.com/ardatan/graphql-tools/commits/@graphql-tools/utils@10.8.6/packages/utils) --- updated-dependencies: - dependency-name: "@graphql-tools/utils" dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> chore(): update versions chore(): remove unused workload fix(): kbar dependency fix(): kbar dependency chore(): fix packages fix(): dependencies fix(): dependencies fix(): dependencies fix(): dependencies api fix(): dependencies api fix(): dependencies api chore(): fix order of github fix(): fix tsconfig for liqe
457 lines
14 KiB
TypeScript
457 lines
14 KiB
TypeScript
import * as Progress from '@radix-ui/react-progress'
|
|
import { styled } from '@stitches/react'
|
|
import axios from 'axios'
|
|
import { File } from '@phosphor-icons/react'
|
|
import { useCallback, useRef, useState } from 'react'
|
|
import Dropzone, { DropEvent, DropzoneRef, FileRejection } from 'react-dropzone'
|
|
import { v4 as uuidv4 } from 'uuid'
|
|
import { uploadFileRequestMutation } from '../../lib/networking/mutations/uploadFileMutation'
|
|
import {
|
|
uploadImportFileRequestMutation,
|
|
UploadImportFileType,
|
|
} from '../../lib/networking/mutations/uploadImportFileMutation'
|
|
import { showErrorToast } from '../../lib/toastHelpers'
|
|
import { validateCsvFile } from '../../utils/csvValidator'
|
|
import { Box, HStack, SpanBox, VStack } from '../elements/LayoutPrimitives'
|
|
import {
|
|
ModalContent,
|
|
ModalOverlay,
|
|
ModalRoot,
|
|
ModalTitleBar,
|
|
} from '../elements/ModalPrimitives'
|
|
import { theme } from '../tokens/stitches.config'
|
|
|
|
const DragnDropContainer = styled('div', {
|
|
width: '100%',
|
|
height: '80%',
|
|
position: 'absolute',
|
|
display: 'flex',
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
zIndex: '1',
|
|
alignSelf: 'center',
|
|
left: 0,
|
|
flexDirection: 'column',
|
|
padding: '25px',
|
|
})
|
|
|
|
const DragnDropStyle = styled('div', {
|
|
border: '1px solid $grayBorder',
|
|
borderRadius: '5px',
|
|
width: '100%',
|
|
height: '100%',
|
|
display: 'flex',
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
alignSelf: 'center',
|
|
color: '$thTextSubtle2',
|
|
padding: '10px',
|
|
})
|
|
|
|
const DragnDropIndicator = styled('div', {
|
|
display: 'flex',
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
alignSelf: 'center',
|
|
width: '100%',
|
|
height: '100%',
|
|
borderRadius: '5px',
|
|
})
|
|
|
|
const ProgressIndicator = styled(Progress.Indicator, {
|
|
backgroundColor: '$omnivoreCtaYellow',
|
|
width: '100%',
|
|
height: '100%',
|
|
})
|
|
|
|
const ProgressRoot = styled(Progress.Root, {
|
|
position: 'relative',
|
|
overflow: 'hidden',
|
|
background: '$omnivoreGray',
|
|
borderRadius: '99999px',
|
|
width: '100%',
|
|
height: '5px',
|
|
transform: 'translateZ(0)',
|
|
})
|
|
|
|
type UploadModalProps = {
|
|
onOpenChange: (open: boolean) => void
|
|
}
|
|
|
|
type UploadingFile = {
|
|
id: string
|
|
file: any
|
|
name: string
|
|
progress: number
|
|
status: 'inprogress' | 'success' | 'error'
|
|
openUrl: string | undefined
|
|
contentType: string
|
|
message?: string
|
|
}
|
|
|
|
type UploadInfo = {
|
|
uploadSignedUrl?: string
|
|
requestId?: string
|
|
message?: string
|
|
}
|
|
|
|
export function UploadModal(props: UploadModalProps): JSX.Element {
|
|
const [uploadFiles, setUploadFiles] = useState<UploadingFile[]>([
|
|
// {
|
|
// id: uuidv4(),
|
|
// file: '',
|
|
// name: 'test file',
|
|
// status: 'inprogress',
|
|
// progress: (371712 / 864476) * 100,
|
|
// openUrl: '',
|
|
// },
|
|
])
|
|
const [inDragOperation, setInDragOperation] = useState(false)
|
|
const dropzoneRef = useRef<DropzoneRef | null>(null)
|
|
|
|
const openDialog = useCallback(
|
|
(event: React.MouseEvent) => {
|
|
if (dropzoneRef.current) {
|
|
dropzoneRef.current.open()
|
|
}
|
|
event?.preventDefault()
|
|
},
|
|
[dropzoneRef]
|
|
)
|
|
|
|
const uploadSignedUrlForFile = async (
|
|
file: UploadingFile
|
|
): Promise<UploadInfo> => {
|
|
let { contentType } = file
|
|
if (
|
|
contentType == 'application/vnd.ms-excel' &&
|
|
file.name.endsWith('.csv')
|
|
) {
|
|
contentType = 'text/csv'
|
|
}
|
|
switch (contentType) {
|
|
case 'text/csv': {
|
|
let urlCount = 0
|
|
try {
|
|
const csvData = await validateCsvFile(file.file)
|
|
urlCount = csvData.data.length
|
|
if (urlCount > 5000) {
|
|
return {
|
|
message:
|
|
'Due to an increase in traffic we are limiting CSV imports to 5000 items.',
|
|
}
|
|
}
|
|
if (csvData.inValidData.length > 0) {
|
|
return {
|
|
message: csvData.inValidData[0].message,
|
|
}
|
|
}
|
|
if (urlCount === 0) {
|
|
return {
|
|
message: 'No URLs found in CSV file.',
|
|
}
|
|
}
|
|
} catch (error) {
|
|
return {
|
|
message: 'Invalid CSV file.',
|
|
}
|
|
}
|
|
|
|
try {
|
|
const result = await uploadImportFileRequestMutation(
|
|
UploadImportFileType.URL_LIST,
|
|
contentType
|
|
)
|
|
return {
|
|
uploadSignedUrl: result?.uploadSignedUrl,
|
|
message: `Importing ${urlCount} URLs`,
|
|
}
|
|
} catch (error) {
|
|
console.log('caught error', error)
|
|
if (error == 'UPLOAD_DAILY_LIMIT_EXCEEDED') {
|
|
return {
|
|
message: 'You have exceeded your maximum daily upload limit.',
|
|
}
|
|
}
|
|
}
|
|
}
|
|
case 'application/zip': {
|
|
const result = await uploadImportFileRequestMutation(
|
|
UploadImportFileType.MATTER,
|
|
contentType
|
|
)
|
|
return {
|
|
uploadSignedUrl: result?.uploadSignedUrl,
|
|
}
|
|
}
|
|
case 'application/pdf':
|
|
case 'application/epub+zip': {
|
|
const request = await uploadFileRequestMutation({
|
|
// This will tell the backend not to save the URL
|
|
// and give it the local filename as the title.
|
|
url: `file://local/${file.id}/${file.file.path}`,
|
|
contentType: contentType,
|
|
createPageEntry: true,
|
|
})
|
|
return {
|
|
uploadSignedUrl: request?.uploadSignedUrl,
|
|
requestId: request?.createdPageId,
|
|
}
|
|
}
|
|
}
|
|
return {
|
|
message: `Invalid content type: ${contentType}`,
|
|
}
|
|
}
|
|
|
|
const handleAcceptedFiles = useCallback(
|
|
(acceptedFiles: any, event: DropEvent) => {
|
|
setInDragOperation(false)
|
|
|
|
const addedFiles = acceptedFiles.map(
|
|
(file: { name: any; type: string }) => {
|
|
return {
|
|
id: uuidv4(),
|
|
file: file,
|
|
name: file.name,
|
|
progress: 0,
|
|
status: 'inprogress',
|
|
contentType: file.type,
|
|
}
|
|
}
|
|
)
|
|
|
|
const allFiles = [...uploadFiles, ...addedFiles]
|
|
|
|
setUploadFiles(allFiles)
|
|
;(async () => {
|
|
for (const file of addedFiles) {
|
|
try {
|
|
const uploadInfo = await uploadSignedUrlForFile(file)
|
|
if (!uploadInfo.uploadSignedUrl) {
|
|
const message = uploadInfo.message || 'No upload URL available'
|
|
showErrorToast(message, { duration: 10000 })
|
|
file.status = 'error'
|
|
setUploadFiles([...allFiles])
|
|
return
|
|
}
|
|
|
|
const uploadResult = await axios.request({
|
|
method: 'PUT',
|
|
url: uploadInfo.uploadSignedUrl,
|
|
data: file.file,
|
|
withCredentials: false,
|
|
headers: {
|
|
'Content-Type': file.file.type,
|
|
},
|
|
onUploadProgress: (p) => {
|
|
if (!p.total) {
|
|
console.warn('No total available for upload progress')
|
|
return
|
|
}
|
|
const progress = (p.loaded / p.total) * 100
|
|
file.progress = progress
|
|
|
|
setUploadFiles([...allFiles])
|
|
},
|
|
})
|
|
|
|
file.progress = 100
|
|
file.status = 'success'
|
|
file.openUrl = uploadInfo.requestId
|
|
? `/article/sr/${uploadInfo.requestId}`
|
|
: undefined
|
|
file.message = uploadInfo.message
|
|
|
|
setUploadFiles([...allFiles])
|
|
} catch (error) {
|
|
file.status = 'error'
|
|
setUploadFiles([...allFiles])
|
|
}
|
|
}
|
|
})()
|
|
},
|
|
[uploadFiles]
|
|
)
|
|
|
|
return (
|
|
<ModalRoot defaultOpen onOpenChange={props.onOpenChange}>
|
|
<ModalOverlay />
|
|
<ModalContent
|
|
css={{
|
|
bg: '$grayBg',
|
|
px: '24px',
|
|
minWidth: '650px',
|
|
minHeight: '430px',
|
|
}}
|
|
onInteractOutside={(event) => {
|
|
event.preventDefault()
|
|
}}
|
|
>
|
|
<VStack distribution="start">
|
|
<ModalTitleBar
|
|
title="Upload file"
|
|
onOpenChange={props.onOpenChange}
|
|
/>
|
|
<Dropzone
|
|
ref={dropzoneRef}
|
|
onDragEnter={() => {
|
|
setInDragOperation(true)
|
|
}}
|
|
onDragLeave={() => {
|
|
setInDragOperation(false)
|
|
}}
|
|
onDropAccepted={handleAcceptedFiles}
|
|
onDropRejected={(
|
|
fileRejections: FileRejection[],
|
|
event: DropEvent
|
|
) => {
|
|
console.log('onDropRejected: ', fileRejections, event)
|
|
alert('You can only upload PDF files to your Omnivore Library.')
|
|
setInDragOperation(false)
|
|
if ((event as any).preventDefault) {
|
|
(event as any).preventDefault();
|
|
}
|
|
}}
|
|
preventDropOnDocument={true}
|
|
noClick={true}
|
|
accept={{
|
|
'text/csv': ['.csv'],
|
|
'application/zip': ['.zip'],
|
|
'application/pdf': ['.pdf'],
|
|
'application/epub+zip': ['.epub'],
|
|
}}
|
|
>
|
|
{({
|
|
getRootProps,
|
|
getInputProps,
|
|
acceptedFiles,
|
|
fileRejections,
|
|
}) => (
|
|
<div
|
|
{...getRootProps({ className: 'dropzone' })}
|
|
style={{ height: '100%', width: '100%' }}
|
|
>
|
|
<DragnDropContainer>
|
|
<DragnDropStyle>
|
|
<DragnDropIndicator
|
|
css={{
|
|
border: inDragOperation ? '2px dashed blue' : 'unset',
|
|
}}
|
|
>
|
|
<VStack alignment="center" css={{ gap: '10px' }}>
|
|
<File
|
|
size={48}
|
|
color={theme.colors.thTextSubtle2.toString()}
|
|
/>
|
|
{inDragOperation ? (
|
|
<>
|
|
<Box
|
|
css={{
|
|
fontWeight: '800',
|
|
fontSize: '20px',
|
|
}}
|
|
>
|
|
Drop to upload your file
|
|
</Box>
|
|
</>
|
|
) : (
|
|
<>
|
|
<Box
|
|
css={{
|
|
fontWeight: '800',
|
|
fontSize: '20px',
|
|
}}
|
|
>
|
|
Drag files here to add them to your library
|
|
</Box>
|
|
<Box
|
|
css={{
|
|
fontSize: '14px',
|
|
}}
|
|
>
|
|
Or{' '}
|
|
<a href="" onClick={openDialog}>
|
|
choose your files
|
|
</a>
|
|
</Box>
|
|
</>
|
|
)}
|
|
</VStack>
|
|
</DragnDropIndicator>
|
|
</DragnDropStyle>
|
|
<VStack css={{ width: '100%', mt: '25px', gap: '5px' }}>
|
|
{uploadFiles.map((file) => {
|
|
return (
|
|
<HStack
|
|
key={file.id}
|
|
css={{
|
|
width: '100%',
|
|
height: '54px',
|
|
border: '1px solid $grayBorder',
|
|
borderRadius: '5px',
|
|
padding: '15px',
|
|
gap: '10px',
|
|
color: '$thTextContrast',
|
|
}}
|
|
alignment="center"
|
|
distribution="start"
|
|
>
|
|
<Box
|
|
css={{
|
|
width: '280px',
|
|
maxLines: '1',
|
|
textOverflow: 'ellipsis',
|
|
whiteSpace: 'nowrap',
|
|
maxWidth: '200px',
|
|
overflow: 'hidden',
|
|
fontSize: '14px',
|
|
fontWeight: 'bold',
|
|
}}
|
|
>
|
|
{file.name}
|
|
</Box>
|
|
{file.status != 'inprogress' ? (
|
|
<HStack
|
|
alignment="center"
|
|
css={{ marginLeft: 'auto', fontSize: '14px' }}
|
|
>
|
|
{file.status == 'success' && file.openUrl && (
|
|
<a href={file.openUrl}>Read Now</a>
|
|
)}
|
|
{file.status == 'success' && !file.openUrl && (
|
|
<span>
|
|
{file.message || 'Your import has started'}
|
|
</span>
|
|
)}
|
|
{file.status == 'error' && (
|
|
<SpanBox css={{ color: 'red' }}>
|
|
Error Uploading
|
|
</SpanBox>
|
|
)}
|
|
</HStack>
|
|
) : (
|
|
<ProgressRoot value={file.progress} max={100}>
|
|
<ProgressIndicator
|
|
style={{
|
|
transform: `translateX(-${
|
|
100 - file.progress
|
|
}%)`,
|
|
}}
|
|
/>{' '}
|
|
</ProgressRoot>
|
|
)}
|
|
</HStack>
|
|
)
|
|
})}
|
|
</VStack>
|
|
</DragnDropContainer>
|
|
<input {...getInputProps()} />
|
|
</div>
|
|
)}
|
|
</Dropzone>
|
|
</VStack>
|
|
</ModalContent>
|
|
</ModalRoot>
|
|
)
|
|
}
|