Show uploaded items in the upload modal
This commit is contained in:
@ -159,6 +159,14 @@ export const Button = styled('button', {
|
||||
border: '1px solid $grayBorderHover',
|
||||
},
|
||||
},
|
||||
link: {
|
||||
color: '$grayText',
|
||||
border: 'none',
|
||||
bg: 'transparent',
|
||||
'&:hover': {
|
||||
opacity: 0.8,
|
||||
},
|
||||
},
|
||||
circularIcon: {
|
||||
mx: '$1',
|
||||
display: 'flex',
|
||||
|
||||
347
packages/web/components/templates/UploadModal.tsx
Normal file
347
packages/web/components/templates/UploadModal.tsx
Normal file
@ -0,0 +1,347 @@
|
||||
import { useRef, useCallback, useState } from 'react'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { Box, HStack, SpanBox, VStack } from '../elements/LayoutPrimitives'
|
||||
import {
|
||||
ModalContent,
|
||||
ModalOverlay,
|
||||
ModalRoot,
|
||||
ModalTitleBar,
|
||||
} from '../elements/ModalPrimitives'
|
||||
import { styled } from '@stitches/react'
|
||||
import Dropzone, { DropEvent, DropzoneRef, FileRejection } from 'react-dropzone'
|
||||
import * as Progress from '@radix-ui/react-progress'
|
||||
import { theme } from '../tokens/stitches.config'
|
||||
import { uploadFileRequestMutation } from '../../lib/networking/mutations/uploadFileMutation'
|
||||
import axios from 'axios'
|
||||
import { CheckCircle, File } from 'phosphor-react'
|
||||
import { showErrorToast } from '../../lib/toastHelpers'
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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) => {
|
||||
if (dropzoneRef.current) {
|
||||
dropzoneRef.current.open()
|
||||
}
|
||||
event?.preventDefault()
|
||||
},
|
||||
[dropzoneRef]
|
||||
)
|
||||
|
||||
const handleAcceptedFiles = useCallback(
|
||||
(acceptedFiles: any, event: DropEvent) => {
|
||||
setInDragOperation(false)
|
||||
|
||||
const addedFiles = acceptedFiles.map((file: { name: any }) => {
|
||||
return {
|
||||
id: uuidv4(),
|
||||
file: file,
|
||||
name: file.name,
|
||||
percent: 0,
|
||||
status: 'inprogress',
|
||||
}
|
||||
})
|
||||
|
||||
const allFiles = [...uploadFiles, ...addedFiles]
|
||||
|
||||
setUploadFiles(allFiles)
|
||||
;(async () => {
|
||||
for (const file of addedFiles) {
|
||||
try {
|
||||
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: file.file.type,
|
||||
createPageEntry: true,
|
||||
})
|
||||
|
||||
if (!request?.uploadSignedUrl) {
|
||||
showErrorToast('No upload URL available')
|
||||
return
|
||||
}
|
||||
|
||||
const uploadResult = await axios.request({
|
||||
method: 'PUT',
|
||||
url: request?.uploadSignedUrl,
|
||||
data: file.file,
|
||||
withCredentials: false,
|
||||
headers: {
|
||||
'Content-Type': 'application/pdf',
|
||||
},
|
||||
onUploadProgress: (p) => {
|
||||
if (!p.total) {
|
||||
console.warn('No total available for upload progress')
|
||||
return
|
||||
}
|
||||
const progress = (p.loaded / p.total) * 100
|
||||
file.progress = progress
|
||||
file.openUrl = `/article/sr/${request.createdPageId}`
|
||||
|
||||
setUploadFiles([...allFiles])
|
||||
},
|
||||
})
|
||||
|
||||
file.status = 'success'
|
||||
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={() => {
|
||||
// remove focus from modal
|
||||
;(document.activeElement as HTMLElement).blur()
|
||||
}}
|
||||
>
|
||||
<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)
|
||||
event.preventDefault()
|
||||
}}
|
||||
preventDropOnDocument={true}
|
||||
noClick={true}
|
||||
accept={{
|
||||
'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) => {
|
||||
console.log('fileL ', file.name, file.progress)
|
||||
return (
|
||||
<HStack
|
||||
key={file.id}
|
||||
css={{
|
||||
width: '100%',
|
||||
height: '54px',
|
||||
border: '1px solid $grayBorder',
|
||||
borderRadius: '5px',
|
||||
padding: '15px',
|
||||
gap: '10px',
|
||||
}}
|
||||
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.progress == 100 ? (
|
||||
<HStack
|
||||
alignment="center"
|
||||
css={{ marginLeft: 'auto', fontSize: '14px' }}
|
||||
>
|
||||
{file.status == 'success' && (
|
||||
<a href={file.openUrl}>Read Now</a>
|
||||
)}
|
||||
{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>
|
||||
)
|
||||
}
|
||||
@ -1,10 +1,8 @@
|
||||
import * as Progress from '@radix-ui/react-progress'
|
||||
import axios from 'axios'
|
||||
import { Action, createAction, useKBar, useRegisterActions } from 'kbar'
|
||||
import debounce from 'lodash/debounce'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import Dropzone from 'react-dropzone'
|
||||
import { Toaster } from 'react-hot-toast'
|
||||
import TopBarProgress from 'react-topbar-progress-indicator'
|
||||
import { useFetchMore } from '../../../lib/hooks/useFetchMoreScroll'
|
||||
@ -17,7 +15,6 @@ import {
|
||||
} from '../../../lib/networking/fragments/articleFragment'
|
||||
import { Label } from '../../../lib/networking/fragments/labelFragment'
|
||||
import { setLabelsMutation } from '../../../lib/networking/mutations/setLabelsMutation'
|
||||
import { uploadFileRequestMutation } from '../../../lib/networking/mutations/uploadFileMutation'
|
||||
import {
|
||||
SearchItem,
|
||||
TypeaheadSearchItemsData,
|
||||
@ -37,7 +34,6 @@ import { StyledText } from '../../elements/StyledText'
|
||||
import { ConfirmationModal } from '../../patterns/ConfirmationModal'
|
||||
import { LinkedItemCardAction } from '../../patterns/LibraryCards/CardTypes'
|
||||
import { LinkedItemCard } from '../../patterns/LibraryCards/LinkedItemCard'
|
||||
import { styled, theme } from '../../tokens/stitches.config'
|
||||
import { SetLabelsModal } from '../article/SetLabelsModal'
|
||||
import { Box, HStack, VStack } from './../../elements/LayoutPrimitives'
|
||||
import { AddLinkModal } from './AddLinkModal'
|
||||
@ -46,6 +42,7 @@ import { EmptyLibrary } from './EmptyLibrary'
|
||||
import { HighlightItemsLayout } from './HighlightsLayout'
|
||||
import { LibraryFilterMenu } from './LibraryFilterMenu'
|
||||
import { LibraryHeader } from './LibraryHeader'
|
||||
import { UploadModal } from '../UploadModal'
|
||||
|
||||
export type LayoutType = 'LIST_LAYOUT' | 'GRID_LAYOUT'
|
||||
export type LibraryMode = 'reads' | 'highlights'
|
||||
@ -78,8 +75,9 @@ export function HomeFeedContainer(): JSX.Element {
|
||||
|
||||
const gridContainerRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
const [labelsTarget, setLabelsTarget] =
|
||||
useState<LibraryItem | undefined>(undefined)
|
||||
const [labelsTarget, setLabelsTarget] = useState<LibraryItem | undefined>(
|
||||
undefined
|
||||
)
|
||||
|
||||
const [showAddLinkModal, setShowAddLinkModal] = useState(false)
|
||||
const [showEditTitleModal, setShowEditTitleModal] = useState(false)
|
||||
@ -611,33 +609,6 @@ type HomeFeedContentProps = {
|
||||
) => Promise<void>
|
||||
}
|
||||
|
||||
const DragnDropContainer = styled('div', {
|
||||
width: '100%',
|
||||
height: '80%',
|
||||
position: 'absolute',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
zIndex: '1',
|
||||
alignSelf: 'center',
|
||||
left: 0,
|
||||
})
|
||||
|
||||
const DragnDropStyle = styled('div', {
|
||||
border: '3px dashed gray',
|
||||
backgroundColor: 'aliceblue',
|
||||
borderRadius: '5px',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
opacity: '0.9',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
alignSelf: 'center',
|
||||
left: 0,
|
||||
margin: '16px',
|
||||
})
|
||||
|
||||
function HomeFeedGrid(props: HomeFeedContentProps): JSX.Element {
|
||||
const { viewerData } = useGetViewerQuery()
|
||||
const [layout, setLayout] = usePersistedState<LayoutType>({
|
||||
@ -715,14 +686,11 @@ type LibraryItemsLayoutProps = {
|
||||
} & HomeFeedContentProps
|
||||
|
||||
function LibraryItemsLayout(props: LibraryItemsLayoutProps): JSX.Element {
|
||||
const [uploadingFiles, setUploadingFiles] = useState([])
|
||||
const [inDragOperation, setInDragOperation] = useState(false)
|
||||
const [uploadProgress, setUploadProgress] = useState(0)
|
||||
|
||||
const [showRemoveLinkConfirmation, setShowRemoveLinkConfirmation] =
|
||||
useState(false)
|
||||
const [showUnsubscribeConfirmation, setShowUnsubscribeConfirmation] =
|
||||
useState(false)
|
||||
const [showUploadModal, setShowUploadModal] = useState(true)
|
||||
const [, updateState] = useState({})
|
||||
|
||||
const removeItem = () => {
|
||||
@ -744,52 +712,6 @@ function LibraryItemsLayout(props: LibraryItemsLayoutProps): JSX.Element {
|
||||
setShowUnsubscribeConfirmation(false)
|
||||
}
|
||||
|
||||
const handleDrop = async (acceptedFiles: any) => {
|
||||
setInDragOperation(false)
|
||||
setUploadingFiles(acceptedFiles.map((file: { name: any }) => file.name))
|
||||
|
||||
for (const file of acceptedFiles) {
|
||||
try {
|
||||
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.path}`,
|
||||
contentType: file.type,
|
||||
createPageEntry: true,
|
||||
})
|
||||
if (!request?.uploadSignedUrl) {
|
||||
throw 'No upload URL available'
|
||||
}
|
||||
|
||||
const uploadResult = await axios.request({
|
||||
method: 'PUT',
|
||||
url: request?.uploadSignedUrl,
|
||||
data: file,
|
||||
withCredentials: false,
|
||||
headers: {
|
||||
'Content-Type': 'application/pdf',
|
||||
},
|
||||
onUploadProgress: (p) => {
|
||||
if (!p.total) {
|
||||
console.warn('No total available for upload progress')
|
||||
return
|
||||
}
|
||||
const progress = (p.loaded / p.total) * 100
|
||||
console.log('upload progress: ', progress)
|
||||
setUploadProgress(progress)
|
||||
},
|
||||
})
|
||||
|
||||
console.log('result of uploading: ', uploadResult)
|
||||
} catch (error) {
|
||||
console.log('ERROR', error)
|
||||
}
|
||||
}
|
||||
|
||||
setUploadingFiles([])
|
||||
props.reloadItems()
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<VStack
|
||||
@ -803,121 +725,53 @@ function LibraryItemsLayout(props: LibraryItemsLayoutProps): JSX.Element {
|
||||
<Toaster />
|
||||
|
||||
{props.isValidating && props.items.length == 0 && <TopBarProgress />}
|
||||
|
||||
<Dropzone
|
||||
onDrop={handleDrop}
|
||||
onDragEnter={() => {
|
||||
setInDragOperation(true)
|
||||
}}
|
||||
onDragLeave={() => {
|
||||
setInDragOperation(false)
|
||||
}}
|
||||
preventDropOnDocument={true}
|
||||
noClick={true}
|
||||
accept={{
|
||||
'application/pdf': ['.pdf'],
|
||||
<div
|
||||
onDragEnter={(event) => {
|
||||
setShowUploadModal(true)
|
||||
}}
|
||||
style={{ height: '100%', width: '100%' }}
|
||||
>
|
||||
{({ getRootProps, getInputProps, acceptedFiles, fileRejections }) => (
|
||||
<div
|
||||
{...getRootProps({ className: 'dropzone' })}
|
||||
style={{ height: '100%', width: '100%' }}
|
||||
>
|
||||
{inDragOperation && uploadingFiles.length < 1 && (
|
||||
<DragnDropContainer>
|
||||
<DragnDropStyle>
|
||||
<Box
|
||||
css={{
|
||||
color: '$utilityTextDefault',
|
||||
fontWeight: '800',
|
||||
fontSize: '$4',
|
||||
}}
|
||||
>
|
||||
Drop PDF document to to upload and add to your library
|
||||
</Box>
|
||||
</DragnDropStyle>
|
||||
</DragnDropContainer>
|
||||
)}
|
||||
{uploadingFiles.length > 0 && (
|
||||
<DragnDropContainer>
|
||||
<DragnDropStyle>
|
||||
<Box
|
||||
css={{
|
||||
color: '$utilityTextDefault',
|
||||
fontWeight: '800',
|
||||
fontSize: '$4',
|
||||
width: '80%',
|
||||
}}
|
||||
>
|
||||
<Progress.Root
|
||||
className="ProgressRoot"
|
||||
value={uploadProgress}
|
||||
>
|
||||
<Progress.Indicator
|
||||
className="ProgressIndicator"
|
||||
style={{
|
||||
transform: `translateX(-${100 - uploadProgress}%)`,
|
||||
}}
|
||||
/>
|
||||
</Progress.Root>
|
||||
<StyledText
|
||||
style="boldText"
|
||||
css={{
|
||||
color: theme.colors.omnivoreGray.toString(),
|
||||
}}
|
||||
>
|
||||
Uploading file
|
||||
</StyledText>
|
||||
</Box>
|
||||
</DragnDropStyle>
|
||||
</DragnDropContainer>
|
||||
)}
|
||||
<input {...getInputProps()} />
|
||||
{!props.isValidating && props.items.length == 0 ? (
|
||||
<EmptyLibrary
|
||||
onAddLinkClicked={() => {
|
||||
props.setShowAddLinkModal(true)
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<LibraryItems
|
||||
items={props.items}
|
||||
layout={props.layout}
|
||||
viewer={props.viewer}
|
||||
gridContainerRef={props.gridContainerRef}
|
||||
setShowEditTitleModal={props.setShowEditTitleModal}
|
||||
setLinkToEdit={props.setLinkToEdit}
|
||||
setShowUnsubscribeConfirmation={
|
||||
setShowUnsubscribeConfirmation
|
||||
}
|
||||
setLinkToRemove={props.setLinkToRemove}
|
||||
setLinkToUnsubscribe={props.setLinkToUnsubscribe}
|
||||
setShowRemoveLinkConfirmation={setShowRemoveLinkConfirmation}
|
||||
actionHandler={props.actionHandler}
|
||||
/>
|
||||
)}
|
||||
<HStack
|
||||
distribution="center"
|
||||
css={{ width: '100%', mt: '$2', mb: '$4' }}
|
||||
>
|
||||
{props.hasMore ? (
|
||||
<Button
|
||||
style="ctaGray"
|
||||
css={{
|
||||
cursor: props.isValidating ? 'not-allowed' : 'pointer',
|
||||
}}
|
||||
onClick={props.loadMore}
|
||||
disabled={props.isValidating}
|
||||
>
|
||||
{props.isValidating ? 'Loading' : 'Load More'}
|
||||
</Button>
|
||||
) : (
|
||||
<StyledText style="caption"></StyledText>
|
||||
)}
|
||||
</HStack>
|
||||
</div>
|
||||
{!props.isValidating && props.items.length == 0 ? (
|
||||
<EmptyLibrary
|
||||
onAddLinkClicked={() => {
|
||||
props.setShowAddLinkModal(true)
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<LibraryItems
|
||||
items={props.items}
|
||||
layout={props.layout}
|
||||
viewer={props.viewer}
|
||||
gridContainerRef={props.gridContainerRef}
|
||||
setShowEditTitleModal={props.setShowEditTitleModal}
|
||||
setLinkToEdit={props.setLinkToEdit}
|
||||
setShowUnsubscribeConfirmation={setShowUnsubscribeConfirmation}
|
||||
setLinkToRemove={props.setLinkToRemove}
|
||||
setLinkToUnsubscribe={props.setLinkToUnsubscribe}
|
||||
setShowRemoveLinkConfirmation={setShowRemoveLinkConfirmation}
|
||||
actionHandler={props.actionHandler}
|
||||
/>
|
||||
)}
|
||||
</Dropzone>
|
||||
<HStack
|
||||
distribution="center"
|
||||
css={{ width: '100%', mt: '$2', mb: '$4' }}
|
||||
>
|
||||
{props.hasMore ? (
|
||||
<Button
|
||||
style="ctaGray"
|
||||
css={{
|
||||
cursor: props.isValidating ? 'not-allowed' : 'pointer',
|
||||
}}
|
||||
onClick={props.loadMore}
|
||||
disabled={props.isValidating}
|
||||
>
|
||||
{props.isValidating ? 'Loading' : 'Load More'}
|
||||
</Button>
|
||||
) : (
|
||||
<StyledText style="caption"></StyledText>
|
||||
)}
|
||||
</HStack>
|
||||
</div>
|
||||
</VStack>
|
||||
{props.showEditTitleModal && (
|
||||
<EditLibraryItemModal
|
||||
@ -995,6 +849,9 @@ function LibraryItemsLayout(props: LibraryItemsLayoutProps): JSX.Element {
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{showUploadModal && (
|
||||
<UploadModal onOpenChange={() => setShowUploadModal(false)} />
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ import { StyledText } from '../../elements/StyledText'
|
||||
import { Box, HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives'
|
||||
import { Dropdown, DropdownOption } from '../../elements/DropdownElements'
|
||||
import { Button } from '../../elements/Button'
|
||||
import { CaretRight, Circle, DotsThree, Plus } from 'phosphor-react'
|
||||
import { CaretRight, Circle, DotsThree, Plus, Upload } from 'phosphor-react'
|
||||
import { useGetSubscriptionsQuery } from '../../../lib/networking/queries/useGetSubscriptionsQuery'
|
||||
import { useGetLabelsQuery } from '../../../lib/networking/queries/useGetLabelsQuery'
|
||||
import { Label } from '../../../lib/networking/fragments/labelFragment'
|
||||
@ -444,33 +444,65 @@ function AddLinkButton(props: AddLinkButtonProps): JSX.Element {
|
||||
}}
|
||||
distribution="center"
|
||||
>
|
||||
<Button
|
||||
css={{
|
||||
height: '40px',
|
||||
p: '15px',
|
||||
pr: '20px',
|
||||
fontSize: '14px',
|
||||
verticalAlign: 'center',
|
||||
<HStack css={{ gap: '15px' }}>
|
||||
<Button
|
||||
css={{
|
||||
height: '40px',
|
||||
p: '15px',
|
||||
pr: '20px',
|
||||
fontSize: '14px',
|
||||
verticalAlign: 'center',
|
||||
|
||||
color: isDark
|
||||
? theme.colors.thHighContrast.toString()
|
||||
: theme.colors.thTextContrast2.toString(),
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
fontWeight: '600',
|
||||
bg: isDark ? 'transparent' : theme.colors.thBackground2.toString(),
|
||||
border: `1px solid ${
|
||||
isDark ? theme.colors.thHighContrast.toString() : 'transparent'
|
||||
}`,
|
||||
}}
|
||||
onClick={(e) => {
|
||||
props.showAddLinkModal()
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
<Plus size={16} weight="bold" />
|
||||
<SpanBox css={{ width: '10px' }}></SpanBox>Add Link
|
||||
</Button>
|
||||
color: isDark
|
||||
? theme.colors.thHighContrast.toString()
|
||||
: theme.colors.thTextContrast2.toString(),
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
fontWeight: '600',
|
||||
bg: isDark
|
||||
? 'transparent'
|
||||
: theme.colors.thBackground2.toString(),
|
||||
border: `1px solid ${
|
||||
isDark ? theme.colors.thHighContrast.toString() : 'transparent'
|
||||
}`,
|
||||
}}
|
||||
onClick={(e) => {
|
||||
props.showAddLinkModal()
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
<Plus size={16} weight="bold" />
|
||||
<SpanBox css={{ width: '10px' }}></SpanBox>Add Link
|
||||
</Button>
|
||||
<Button
|
||||
css={{
|
||||
height: '40px',
|
||||
p: '15px',
|
||||
pr: '20px',
|
||||
fontSize: '14px',
|
||||
verticalAlign: 'center',
|
||||
|
||||
color: isDark
|
||||
? theme.colors.thHighContrast.toString()
|
||||
: theme.colors.thTextContrast2.toString(),
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
fontWeight: '600',
|
||||
bg: isDark
|
||||
? 'transparent'
|
||||
: theme.colors.thBackground2.toString(),
|
||||
border: `1px solid ${
|
||||
isDark ? theme.colors.thHighContrast.toString() : 'transparent'
|
||||
}`,
|
||||
}}
|
||||
onClick={(e) => {
|
||||
props.showAddLinkModal()
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
<Upload size={16} weight="bold" />
|
||||
</Button>
|
||||
</HStack>
|
||||
</VStack>
|
||||
</>
|
||||
)
|
||||
|
||||
@ -16,6 +16,7 @@ type UploadFileResponseData = {
|
||||
type UploadFileData = {
|
||||
id: string
|
||||
uploadSignedUrl: string
|
||||
createdPageId: string
|
||||
}
|
||||
|
||||
export async function uploadFileRequestMutation(
|
||||
@ -30,6 +31,7 @@ export async function uploadFileRequestMutation(
|
||||
... on UploadFileRequestSuccess {
|
||||
id
|
||||
uploadSignedUrl
|
||||
createdPageId
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
@ -375,6 +375,7 @@ div#appleid-signin {
|
||||
select {
|
||||
color: var(--colors-grayTextContrast);
|
||||
background-color: transparent;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
select option {
|
||||
@ -412,18 +413,3 @@ button {
|
||||
background: grey;
|
||||
margin-bottom: 16px;
|
||||
} */
|
||||
|
||||
.ProgressRoot {
|
||||
overflow: hidden;
|
||||
background: var(--colors-omnivoreGray);
|
||||
border-radius: 99999px;
|
||||
height: 25px;
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
.ProgressIndicator {
|
||||
background-color: var(--colors-omnivoreCtaYellow) ;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
transition: transform 660ms cubic-bezier(0.65, 0, 0.35, 1);
|
||||
}
|
||||
Reference in New Issue
Block a user