Merge pull request #1932 from omnivore-app/use-url-in-save-request
use url in save request
This commit is contained in:
@ -1,25 +1,25 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
import express from 'express'
|
||||
import { CreateArticleErrorCode } from '../generated/graphql'
|
||||
import { isSiteBlockedForParse } from '../utils/blocked'
|
||||
import cors from 'cors'
|
||||
import { buildLogger } from '../utils/logger'
|
||||
import { corsConfig } from '../utils/corsConfig'
|
||||
import { createPageSaveRequest } from '../services/create_page_save_request'
|
||||
import { initModels } from '../server'
|
||||
import { kx } from '../datalayer/knex_config'
|
||||
import { getClaimsByToken } from '../utils/auth'
|
||||
import * as jwt from 'jsonwebtoken'
|
||||
import { env } from '../env'
|
||||
import { Claims } from '../resolvers/types'
|
||||
import { getRepository } from '../entity/utils'
|
||||
import { Speech, SpeechState } from '../entity/speech'
|
||||
import { getPageById, updatePage } from '../elastic/pages'
|
||||
import { generateDownloadSignedUrl } from '../utils/uploads'
|
||||
import { enqueueTextToSpeech } from '../utils/createTask'
|
||||
import { createPubSubClient } from '../datalayer/pubsub'
|
||||
import { htmlToSpeechFile } from '@omnivore/text-to-speech-handler'
|
||||
import cors from 'cors'
|
||||
import express from 'express'
|
||||
import * as jwt from 'jsonwebtoken'
|
||||
import { kx } from '../datalayer/knex_config'
|
||||
import { createPubSubClient } from '../datalayer/pubsub'
|
||||
import { getPageById, updatePage } from '../elastic/pages'
|
||||
import { Speech, SpeechState } from '../entity/speech'
|
||||
import { getRepository } from '../entity/utils'
|
||||
import { env } from '../env'
|
||||
import { CreateArticleErrorCode } from '../generated/graphql'
|
||||
import { Claims } from '../resolvers/types'
|
||||
import { initModels } from '../server'
|
||||
import { createPageSaveRequest } from '../services/create_page_save_request'
|
||||
import { getClaimsByToken } from '../utils/auth'
|
||||
import { isSiteBlockedForParse } from '../utils/blocked'
|
||||
import { corsConfig } from '../utils/corsConfig'
|
||||
import { enqueueTextToSpeech } from '../utils/createTask'
|
||||
import { buildLogger } from '../utils/logger'
|
||||
import { generateDownloadSignedUrl } from '../utils/uploads'
|
||||
|
||||
interface SpeechInput {
|
||||
voice?: string
|
||||
@ -74,6 +74,7 @@ export function articleRouter() {
|
||||
|
||||
return res.send({
|
||||
articleSavingRequestId: result.id,
|
||||
url: result.url,
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
import {
|
||||
ModalRoot,
|
||||
ModalContent,
|
||||
ModalOverlay,
|
||||
ModalTitleBar,
|
||||
ModalButtonBar,
|
||||
} from '../../elements/ModalPrimitives'
|
||||
import { VStack, Box } from '../../elements/LayoutPrimitives'
|
||||
import { useCallback, useState } from 'react'
|
||||
import toast from 'react-hot-toast'
|
||||
import { saveUrlMutation } from '../../../lib/networking/mutations/saveUrlMutation'
|
||||
import { showErrorToast } from '../../../lib/toastHelpers'
|
||||
import { Button } from '../../elements/Button'
|
||||
import { FormInput } from '../../elements/FormElements'
|
||||
import { useState, useCallback } from 'react'
|
||||
import { saveUrlMutation } from '../../../lib/networking/mutations/saveUrlMutation'
|
||||
import toast from 'react-hot-toast'
|
||||
import { showErrorToast } from '../../../lib/toastHelpers'
|
||||
import { Box, VStack } from '../../elements/LayoutPrimitives'
|
||||
import {
|
||||
ModalButtonBar,
|
||||
ModalContent,
|
||||
ModalOverlay,
|
||||
ModalRoot,
|
||||
ModalTitleBar,
|
||||
} from '../../elements/ModalPrimitives'
|
||||
|
||||
type AddLinkModalProps = {
|
||||
onOpenChange: (open: boolean) => void
|
||||
@ -23,9 +23,7 @@ export function AddLinkModal(props: AddLinkModalProps): JSX.Element {
|
||||
const handleLinkSubmission = useCallback(
|
||||
async (link: string) => {
|
||||
const result = await saveUrlMutation(link)
|
||||
// const result = await saveUrlMutation(link)
|
||||
if (result && result.jobId) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
if (result) {
|
||||
toast(
|
||||
() => (
|
||||
<Box>
|
||||
@ -35,7 +33,9 @@ export function AddLinkModal(props: AddLinkModalProps): JSX.Element {
|
||||
style="ctaDarkYellow"
|
||||
autoFocus
|
||||
onClick={() => {
|
||||
window.location.href = `/article/sr/${result.jobId}`
|
||||
window.location.href = `/article?url=${encodeURIComponent(
|
||||
link
|
||||
)}`
|
||||
}}
|
||||
>
|
||||
Read Now
|
||||
|
||||
@ -1,6 +1,28 @@
|
||||
import { Box, HStack, VStack } from './../../elements/LayoutPrimitives'
|
||||
import Dropzone from 'react-dropzone'
|
||||
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'
|
||||
import { usePersistedState } from '../../../lib/hooks/usePersistedState'
|
||||
import { libraryListCommands } from '../../../lib/keyboardShortcuts/navigationShortcuts'
|
||||
import { useKeyboardShortcuts } from '../../../lib/keyboardShortcuts/useKeyboardShortcuts'
|
||||
import {
|
||||
PageType,
|
||||
State,
|
||||
} 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,
|
||||
typeaheadSearchQuery,
|
||||
} from '../../../lib/networking/queries/typeaheadSearch'
|
||||
import type {
|
||||
LibraryItem,
|
||||
LibraryItemsQueryInput,
|
||||
@ -10,42 +32,20 @@ import {
|
||||
useGetViewerQuery,
|
||||
UserBasicData,
|
||||
} from '../../../lib/networking/queries/useGetViewerQuery'
|
||||
import { Button } from '../../elements/Button'
|
||||
import { StyledText } from '../../elements/StyledText'
|
||||
import { ConfirmationModal } from '../../patterns/ConfirmationModal'
|
||||
import { LinkedItemCardAction } from '../../patterns/LibraryCards/CardTypes'
|
||||
import { LinkedItemCard } from '../../patterns/LibraryCards/LinkedItemCard'
|
||||
import { useRouter } from 'next/router'
|
||||
import { Button } from '../../elements/Button'
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { StyledText } from '../../elements/StyledText'
|
||||
import { AddLinkModal } from './AddLinkModal'
|
||||
import { styled, theme } from '../../tokens/stitches.config'
|
||||
import { libraryListCommands } from '../../../lib/keyboardShortcuts/navigationShortcuts'
|
||||
import { useKeyboardShortcuts } from '../../../lib/keyboardShortcuts/useKeyboardShortcuts'
|
||||
import { Toaster } from 'react-hot-toast'
|
||||
import { useFetchMore } from '../../../lib/hooks/useFetchMoreScroll'
|
||||
import { usePersistedState } from '../../../lib/hooks/usePersistedState'
|
||||
import { ConfirmationModal } from '../../patterns/ConfirmationModal'
|
||||
import { SetLabelsModal } from '../article/SetLabelsModal'
|
||||
import { Label } from '../../../lib/networking/fragments/labelFragment'
|
||||
import { EmptyLibrary } from './EmptyLibrary'
|
||||
import TopBarProgress from 'react-topbar-progress-indicator'
|
||||
import {
|
||||
PageType,
|
||||
State,
|
||||
} from '../../../lib/networking/fragments/articleFragment'
|
||||
import { Action, createAction, useKBar, useRegisterActions } from 'kbar'
|
||||
import { Box, HStack, VStack } from './../../elements/LayoutPrimitives'
|
||||
import { AddLinkModal } from './AddLinkModal'
|
||||
import { EditLibraryItemModal } from './EditItemModals'
|
||||
import debounce from 'lodash/debounce'
|
||||
import {
|
||||
SearchItem,
|
||||
TypeaheadSearchItemsData,
|
||||
typeaheadSearchQuery,
|
||||
} from '../../../lib/networking/queries/typeaheadSearch'
|
||||
import axios from 'axios'
|
||||
import { uploadFileRequestMutation } from '../../../lib/networking/mutations/uploadFileMutation'
|
||||
import { setLabelsMutation } from '../../../lib/networking/mutations/setLabelsMutation'
|
||||
import { LibraryHeader } from './LibraryHeader'
|
||||
import { LibraryFilterMenu } from './LibraryFilterMenu'
|
||||
import { EmptyLibrary } from './EmptyLibrary'
|
||||
import { HighlightItemsLayout } from './HighlightsLayout'
|
||||
import { LibraryFilterMenu } from './LibraryFilterMenu'
|
||||
import { LibraryHeader } from './LibraryHeader'
|
||||
|
||||
export type LayoutType = 'LIST_LAYOUT' | 'GRID_LAYOUT'
|
||||
export type LibraryMode = 'reads' | 'highlights'
|
||||
@ -288,7 +288,7 @@ export function HomeFeedContainer(): JSX.Element {
|
||||
if (username) {
|
||||
setActiveCardId(item.node.id)
|
||||
if (item.node.state === State.PROCESSING) {
|
||||
router.push(`/${username}/links/${item.node.id}`)
|
||||
router.push(`/article?url=${encodeURIComponent(item.node.url)}`)
|
||||
} else {
|
||||
const dl =
|
||||
item.node.pageType === PageType.HIGHLIGHTS
|
||||
|
||||
@ -6,7 +6,8 @@ import { makeGqlFetcher } from '../networkHelpers'
|
||||
import { ArticleAttributes } from './useGetArticleQuery'
|
||||
|
||||
type ArticleSavingStatusInput = {
|
||||
id: string
|
||||
id?: string
|
||||
url?: string
|
||||
}
|
||||
|
||||
type ArticleSavingStatusResponse = {
|
||||
@ -49,10 +50,11 @@ type ArticleSavingStatusError =
|
||||
|
||||
export function useGetArticleSavingStatus({
|
||||
id,
|
||||
url,
|
||||
}: ArticleSavingStatusInput): ArticleSavingStatusResponse {
|
||||
const query = gql`
|
||||
query ArticleSavingRequest($id: ID!) {
|
||||
articleSavingRequest(id: $id) {
|
||||
query ArticleSavingRequest($id: ID, $url: String) {
|
||||
articleSavingRequest(id: $id, url: $url) {
|
||||
... on ArticleSavingRequestSuccess {
|
||||
articleSavingRequest {
|
||||
id
|
||||
@ -83,9 +85,9 @@ export function useGetArticleSavingStatus({
|
||||
${articleFragment}
|
||||
${highlightFragment}
|
||||
`
|
||||
|
||||
const key = id ? [query, id] : [query, url]
|
||||
// poll twice a second
|
||||
const { data, error } = useSWR([query, id], makeGqlFetcher({ id }), {
|
||||
const { data, error } = useSWR(key, makeGqlFetcher({ id, url }), {
|
||||
refreshInterval: 500,
|
||||
})
|
||||
|
||||
@ -129,7 +131,7 @@ export function useGetArticleSavingStatus({
|
||||
}
|
||||
}
|
||||
|
||||
if (status === 'PROCESSING') {
|
||||
if (status === 'PROCESSING' || status === 'DELETED') {
|
||||
return {}
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { SaveResponseData } from '../../lib/networking/mutations/saveUrlMutation'
|
||||
import { ssrFetcher } from '../../lib/networking/networkHelpers'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
const saveUrl = async (req: NextApiRequest, url: URL) => {
|
||||
const clientRequestId = uuidv4()
|
||||
@ -50,8 +50,8 @@ export default async (
|
||||
const url = new URL(urlStr as string)
|
||||
const saveResult = await saveUrl(req, url)
|
||||
console.log('saveResult: ', saveResult)
|
||||
if (saveResult?.jobId) {
|
||||
res.redirect(`/sr/${saveResult?.jobId}`)
|
||||
if (saveResult?.url) {
|
||||
res.redirect(`?url=${encodeURIComponent(url.toString())}`)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
86
packages/web/pages/article/index.tsx
Normal file
86
packages/web/pages/article/index.tsx
Normal file
@ -0,0 +1,86 @@
|
||||
import { useRouter } from 'next/router'
|
||||
import { useEffect, useState } from 'react'
|
||||
import TopBarProgress from 'react-topbar-progress-indicator'
|
||||
import { VStack } from '../../components/elements/LayoutPrimitives'
|
||||
import { ArticleActionsMenu } from '../../components/templates/article/ArticleActionsMenu'
|
||||
import { SkeletonArticleContainer } from '../../components/templates/article/SkeletonArticleContainer'
|
||||
import { PrimaryLayout } from '../../components/templates/PrimaryLayout'
|
||||
import { Loader } from '../../components/templates/SavingRequest'
|
||||
import { theme } from '../../components/tokens/stitches.config'
|
||||
import { useReaderSettings } from '../../lib/hooks/useReaderSettings'
|
||||
import { applyStoredTheme } from '../../lib/themeUpdater'
|
||||
import { PrimaryContent } from '../article/sr/[id]'
|
||||
|
||||
export default function ArticleSavingRequestPage(): JSX.Element {
|
||||
const router = useRouter()
|
||||
const readerSettings = useReaderSettings()
|
||||
const [url, setUrl] = useState<string | undefined>(undefined)
|
||||
|
||||
applyStoredTheme(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (!router.isReady) return
|
||||
setUrl(router.query.url as string)
|
||||
}, [router.isReady, router.query.url])
|
||||
|
||||
return (
|
||||
<PrimaryLayout
|
||||
pageTestId="home-page-tag"
|
||||
headerToolbarControl={
|
||||
<ArticleActionsMenu
|
||||
article={undefined}
|
||||
layout="top"
|
||||
showReaderDisplaySettings={true}
|
||||
articleActionHandler={readerSettings.actionHandler}
|
||||
/>
|
||||
}
|
||||
alwaysDisplayToolbar={false}
|
||||
pageMetaDataProps={{
|
||||
title: 'Saving link',
|
||||
path: router.pathname,
|
||||
}}
|
||||
>
|
||||
<TopBarProgress />
|
||||
<VStack
|
||||
distribution="between"
|
||||
alignment="center"
|
||||
css={{
|
||||
position: 'fixed',
|
||||
flexDirection: 'row-reverse',
|
||||
top: '-120px',
|
||||
left: 8,
|
||||
height: '100%',
|
||||
width: '35px',
|
||||
'@lgDown': {
|
||||
display: 'none',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ArticleActionsMenu
|
||||
article={undefined}
|
||||
layout="side"
|
||||
showReaderDisplaySettings={true}
|
||||
articleActionHandler={readerSettings.actionHandler}
|
||||
/>
|
||||
</VStack>
|
||||
<VStack
|
||||
alignment="center"
|
||||
distribution="center"
|
||||
className="disable-webkit-callout"
|
||||
css={{
|
||||
'@smDown': {
|
||||
background: theme.colors.grayBg.toString(),
|
||||
},
|
||||
}}
|
||||
>
|
||||
<SkeletonArticleContainer
|
||||
margin={readerSettings.marginWidth}
|
||||
fontSize={readerSettings.fontSize}
|
||||
lineHeight={readerSettings.lineHeight}
|
||||
>
|
||||
{url ? <PrimaryContent url={url} /> : <Loader />}
|
||||
</SkeletonArticleContainer>
|
||||
</VStack>
|
||||
</PrimaryLayout>
|
||||
)
|
||||
}
|
||||
@ -1,18 +1,18 @@
|
||||
import { useRouter } from 'next/router'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useGetArticleSavingStatus } from '../../../lib/networking/queries/useGetArticleSavingStatus'
|
||||
import TopBarProgress from 'react-topbar-progress-indicator'
|
||||
import { VStack } from '../../../components/elements/LayoutPrimitives'
|
||||
import { ArticleActionsMenu } from '../../../components/templates/article/ArticleActionsMenu'
|
||||
import { SkeletonArticleContainer } from '../../../components/templates/article/SkeletonArticleContainer'
|
||||
import { PrimaryLayout } from '../../../components/templates/PrimaryLayout'
|
||||
import {
|
||||
Loader,
|
||||
ErrorComponent,
|
||||
Loader,
|
||||
} from '../../../components/templates/SavingRequest'
|
||||
import { ArticleActionsMenu } from '../../../components/templates/article/ArticleActionsMenu'
|
||||
import { VStack } from '../../../components/elements/LayoutPrimitives'
|
||||
import { theme } from '../../../components/tokens/stitches.config'
|
||||
import { applyStoredTheme } from '../../../lib/themeUpdater'
|
||||
import { useReaderSettings } from '../../../lib/hooks/useReaderSettings'
|
||||
import { SkeletonArticleContainer } from '../../../components/templates/article/SkeletonArticleContainer'
|
||||
import TopBarProgress from 'react-topbar-progress-indicator'
|
||||
import { useGetArticleSavingStatus } from '../../../lib/networking/queries/useGetArticleSavingStatus'
|
||||
import { applyStoredTheme } from '../../../lib/themeUpdater'
|
||||
|
||||
export default function ArticleSavingRequestPage(): JSX.Element {
|
||||
const router = useRouter()
|
||||
@ -32,7 +32,7 @@ export default function ArticleSavingRequestPage(): JSX.Element {
|
||||
headerToolbarControl={
|
||||
<ArticleActionsMenu
|
||||
article={undefined}
|
||||
layout='top'
|
||||
layout="top"
|
||||
showReaderDisplaySettings={true}
|
||||
articleActionHandler={readerSettings.actionHandler}
|
||||
/>
|
||||
@ -44,52 +44,56 @@ export default function ArticleSavingRequestPage(): JSX.Element {
|
||||
}}
|
||||
>
|
||||
<TopBarProgress />
|
||||
<VStack distribution="between" alignment="center" css={{
|
||||
position: 'fixed',
|
||||
flexDirection: 'row-reverse',
|
||||
top: '-120px',
|
||||
left: 8,
|
||||
height: '100%',
|
||||
width: '35px',
|
||||
'@lgDown': {
|
||||
display: 'none',
|
||||
},
|
||||
<VStack
|
||||
distribution="between"
|
||||
alignment="center"
|
||||
css={{
|
||||
position: 'fixed',
|
||||
flexDirection: 'row-reverse',
|
||||
top: '-120px',
|
||||
left: 8,
|
||||
height: '100%',
|
||||
width: '35px',
|
||||
'@lgDown': {
|
||||
display: 'none',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ArticleActionsMenu
|
||||
article={undefined}
|
||||
layout='side'
|
||||
layout="side"
|
||||
showReaderDisplaySettings={true}
|
||||
articleActionHandler={readerSettings.actionHandler}
|
||||
/>
|
||||
</VStack>
|
||||
<VStack
|
||||
alignment="center"
|
||||
distribution="center"
|
||||
className="disable-webkit-callout"
|
||||
css={{
|
||||
'@smDown': {
|
||||
background: theme.colors.grayBg.toString(),
|
||||
}
|
||||
}}
|
||||
<VStack
|
||||
alignment="center"
|
||||
distribution="center"
|
||||
className="disable-webkit-callout"
|
||||
css={{
|
||||
'@smDown': {
|
||||
background: theme.colors.grayBg.toString(),
|
||||
},
|
||||
}}
|
||||
>
|
||||
<SkeletonArticleContainer
|
||||
margin={readerSettings.marginWidth}
|
||||
fontSize={readerSettings.fontSize}
|
||||
lineHeight={readerSettings.lineHeight}
|
||||
>
|
||||
<SkeletonArticleContainer
|
||||
margin={readerSettings.marginWidth}
|
||||
fontSize={readerSettings.fontSize}
|
||||
lineHeight={readerSettings.lineHeight}
|
||||
>
|
||||
{articleId ? <PrimaryContent articleId={articleId} /> : <Loader />}
|
||||
</SkeletonArticleContainer>
|
||||
{articleId ? <PrimaryContent articleId={articleId} /> : <Loader />}
|
||||
</SkeletonArticleContainer>
|
||||
</VStack>
|
||||
</PrimaryLayout>
|
||||
)
|
||||
}
|
||||
|
||||
type PrimaryContentProps = {
|
||||
articleId: string
|
||||
articleId?: string
|
||||
url?: string
|
||||
}
|
||||
|
||||
function PrimaryContent(props: PrimaryContentProps): JSX.Element {
|
||||
export function PrimaryContent(props: PrimaryContentProps): JSX.Element {
|
||||
const router = useRouter()
|
||||
const [timedOut, setTimedOut] = useState(false)
|
||||
|
||||
@ -121,7 +125,5 @@ function PrimaryContent(props: PrimaryContentProps): JSX.Element {
|
||||
router.replace(successRedirectPath)
|
||||
}
|
||||
|
||||
return (
|
||||
<Loader />
|
||||
)
|
||||
return <Loader />
|
||||
}
|
||||
|
||||
@ -93,8 +93,8 @@
|
||||
}).then(function (response) {
|
||||
if (response.status === 200) {
|
||||
return response.json().then((responseJson) => {
|
||||
const savingRequestId = responseJson.articleSavingRequestId;
|
||||
return currentOrigin + '/article/sr/' + savingRequestId;
|
||||
const url = encodeURIComponent(responseJson.url);
|
||||
return currentOrigin + '/article?url=' + url;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user