From fb945f279199591dbe516f90394f4a8a163d23b7 Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Mon, 20 Mar 2023 16:48:50 +0800 Subject: [PATCH 1/5] Return url in save API --- packages/api/src/routers/article_router.ts | 39 +++++++++++----------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/packages/api/src/routers/article_router.ts b/packages/api/src/routers/article_router.ts index f20a4d29a..d3543ab0f 100644 --- a/packages/api/src/routers/article_router.ts +++ b/packages/api/src/routers/article_router.ts @@ -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, }) }) From c315e8be0404bd9857468969cf922495055c1206 Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Mon, 20 Mar 2023 16:49:18 +0800 Subject: [PATCH 2/5] Return article's url in saveUrl api --- packages/api/src/services/save_url.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/api/src/services/save_url.ts b/packages/api/src/services/save_url.ts index ed26080dc..4414c33f3 100644 --- a/packages/api/src/services/save_url.ts +++ b/packages/api/src/services/save_url.ts @@ -1,6 +1,5 @@ import { PubsubClient } from '../datalayer/pubsub' import { UserData } from '../datalayer/user/model' -import { homePageURL } from '../env' import { SaveErrorCode, SaveResult, SaveUrlInput } from '../generated/graphql' import { DataModels } from '../resolvers/types' import { createPageSaveRequest } from './create_page_save_request' @@ -26,9 +25,7 @@ export const saveUrl = async ( return { clientRequestId: pageSaveRequest.id, - url: `${homePageURL()}/${saver.profile.username}/links/${ - pageSaveRequest.id - }`, + url: pageSaveRequest.url, } } catch (error) { console.log('error enqueuing request', error) From 163dd62b582909a1d7336ba861f2ef4bfbc754cb Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Mon, 20 Mar 2023 17:02:30 +0800 Subject: [PATCH 3/5] Use url in save request --- .../templates/homeFeed/AddLinkModal.tsx | 27 +++--- .../templates/homeFeed/HomeFeedContainer.tsx | 66 +++++++------- .../queries/useGetArticleSavingStatus.tsx | 14 +-- packages/web/pages/api/save.ts | 6 +- packages/web/pages/article/index.tsx | 86 +++++++++++++++++++ packages/web/pages/article/sr/[id].tsx | 82 +++++++++--------- packages/web/public/sw.js | 4 +- 7 files changed, 185 insertions(+), 100 deletions(-) create mode 100644 packages/web/pages/article/index.tsx diff --git a/packages/web/components/templates/homeFeed/AddLinkModal.tsx b/packages/web/components/templates/homeFeed/AddLinkModal.tsx index de17c92d7..ea8fb1cbe 100644 --- a/packages/web/components/templates/homeFeed/AddLinkModal.tsx +++ b/packages/web/components/templates/homeFeed/AddLinkModal.tsx @@ -1,17 +1,14 @@ -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 +20,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 && result.url) { toast( () => ( @@ -35,7 +30,7 @@ export function AddLinkModal(props: AddLinkModalProps): JSX.Element { style="ctaDarkYellow" autoFocus onClick={() => { - window.location.href = `/article/sr/${result.jobId}` + window.location.href = `/article?url=${encodeURIComponent(result.url || link)}` }} > Read Now diff --git a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx index c4b0134f6..70d39ba04 100644 --- a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx +++ b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx @@ -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 diff --git a/packages/web/lib/networking/queries/useGetArticleSavingStatus.tsx b/packages/web/lib/networking/queries/useGetArticleSavingStatus.tsx index ff170293c..9ab686311 100644 --- a/packages/web/lib/networking/queries/useGetArticleSavingStatus.tsx +++ b/packages/web/lib/networking/queries/useGetArticleSavingStatus.tsx @@ -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 {} } diff --git a/packages/web/pages/api/save.ts b/packages/web/pages/api/save.ts index 71d4735d9..5d4e36439 100644 --- a/packages/web/pages/api/save.ts +++ b/packages/web/pages/api/save.ts @@ -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(saveResult?.url)}`) return } diff --git a/packages/web/pages/article/index.tsx b/packages/web/pages/article/index.tsx new file mode 100644 index 000000000..52f96e887 --- /dev/null +++ b/packages/web/pages/article/index.tsx @@ -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(undefined) + + applyStoredTheme(false) + + useEffect(() => { + if (!router.isReady) return + setUrl(router.query.url as string) + }, [router.isReady, router.query.url]) + + return ( + + } + alwaysDisplayToolbar={false} + pageMetaDataProps={{ + title: 'Saving link', + path: router.pathname, + }} + > + + + + + + + {url ? : } + + + + ) +} diff --git a/packages/web/pages/article/sr/[id].tsx b/packages/web/pages/article/sr/[id].tsx index 90ef594c8..ce256a270 100644 --- a/packages/web/pages/article/sr/[id].tsx +++ b/packages/web/pages/article/sr/[id].tsx @@ -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={ @@ -44,52 +44,56 @@ export default function ArticleSavingRequestPage(): JSX.Element { }} > - - + - - {articleId ? : } - + {articleId ? : } + ) } 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 ( - - ) + return } diff --git a/packages/web/public/sw.js b/packages/web/public/sw.js index 11aa1bc31..ceed2950c 100644 --- a/packages/web/public/sw.js +++ b/packages/web/public/sw.js @@ -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; }); } From 265c050f5c9171c95b5dc18e6495905748eac269 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Mon, 20 Mar 2023 17:28:55 +0800 Subject: [PATCH 4/5] Return readerURL on save URL requests --- packages/api/src/services/save_url.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/api/src/services/save_url.ts b/packages/api/src/services/save_url.ts index 4414c33f3..ed26080dc 100644 --- a/packages/api/src/services/save_url.ts +++ b/packages/api/src/services/save_url.ts @@ -1,5 +1,6 @@ import { PubsubClient } from '../datalayer/pubsub' import { UserData } from '../datalayer/user/model' +import { homePageURL } from '../env' import { SaveErrorCode, SaveResult, SaveUrlInput } from '../generated/graphql' import { DataModels } from '../resolvers/types' import { createPageSaveRequest } from './create_page_save_request' @@ -25,7 +26,9 @@ export const saveUrl = async ( return { clientRequestId: pageSaveRequest.id, - url: pageSaveRequest.url, + url: `${homePageURL()}/${saver.profile.username}/links/${ + pageSaveRequest.id + }`, } } catch (error) { console.log('error enqueuing request', error) From d2de0a290699cbb313e60c9614b08f8a01766466 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Mon, 20 Mar 2023 17:32:49 +0800 Subject: [PATCH 5/5] Dont use result url, always use passed in url --- .../components/templates/homeFeed/AddLinkModal.tsx | 13 +++++++++---- packages/web/pages/api/save.ts | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/web/components/templates/homeFeed/AddLinkModal.tsx b/packages/web/components/templates/homeFeed/AddLinkModal.tsx index ea8fb1cbe..282033cd6 100644 --- a/packages/web/components/templates/homeFeed/AddLinkModal.tsx +++ b/packages/web/components/templates/homeFeed/AddLinkModal.tsx @@ -6,8 +6,11 @@ import { Button } from '../../elements/Button' import { FormInput } from '../../elements/FormElements' import { Box, VStack } from '../../elements/LayoutPrimitives' import { - ModalButtonBar, ModalContent, - ModalOverlay, ModalRoot, ModalTitleBar + ModalButtonBar, + ModalContent, + ModalOverlay, + ModalRoot, + ModalTitleBar, } from '../../elements/ModalPrimitives' type AddLinkModalProps = { @@ -20,7 +23,7 @@ export function AddLinkModal(props: AddLinkModalProps): JSX.Element { const handleLinkSubmission = useCallback( async (link: string) => { const result = await saveUrlMutation(link) - if (result && result.url) { + if (result) { toast( () => ( @@ -30,7 +33,9 @@ export function AddLinkModal(props: AddLinkModalProps): JSX.Element { style="ctaDarkYellow" autoFocus onClick={() => { - window.location.href = `/article?url=${encodeURIComponent(result.url || link)}` + window.location.href = `/article?url=${encodeURIComponent( + link + )}` }} > Read Now diff --git a/packages/web/pages/api/save.ts b/packages/web/pages/api/save.ts index 5d4e36439..a43e6ccb7 100644 --- a/packages/web/pages/api/save.ts +++ b/packages/web/pages/api/save.ts @@ -51,7 +51,7 @@ export default async ( const saveResult = await saveUrl(req, url) console.log('saveResult: ', saveResult) if (saveResult?.url) { - res.redirect(`?url=${encodeURIComponent(saveResult?.url)}`) + res.redirect(`?url=${encodeURIComponent(url.toString())}`) return }