From fc662dd2d58275247a10e67e4b33f95ae1344c13 Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Fri, 17 Mar 2023 11:22:34 +0800 Subject: [PATCH 1/6] Return existing slug if page exists --- .../src/services/create_page_save_request.ts | 27 +++++++-------- packages/api/src/services/save_page.ts | 34 +++++++++---------- 2 files changed, 29 insertions(+), 32 deletions(-) diff --git a/packages/api/src/services/create_page_save_request.ts b/packages/api/src/services/create_page_save_request.ts index b0d134fef..17727bd8d 100644 --- a/packages/api/src/services/create_page_save_request.ts +++ b/packages/api/src/services/create_page_save_request.ts @@ -1,18 +1,17 @@ +import normalizeUrl from 'normalize-url' +import * as privateIpLib from 'private-ip' import { v4 as uuidv4 } from 'uuid' -import { enqueueParseRequest } from '../utils/createTask' - -// TODO: switch to a proper Entity instead of using the old data models. -import { DataModels } from '../resolvers/types' +import { createPubSubClient, PubsubClient } from '../datalayer/pubsub' +import { countByCreatedAt, createPage, getPageByParam } from '../elastic/pages' +import { ArticleSavingRequestStatus, PageType } from '../elastic/types' import { ArticleSavingRequest, CreateArticleSavingRequestErrorCode, } from '../generated/graphql' +// TODO: switch to a proper Entity instead of using the old data models. +import { DataModels } from '../resolvers/types' +import { enqueueParseRequest } from '../utils/createTask' import { generateSlug, pageToArticleSavingRequest } from '../utils/helpers' -import * as privateIpLib from 'private-ip' -import { countByCreatedAt, createPage, getPageByParam } from '../elastic/pages' -import { ArticleSavingRequestStatus, PageType } from '../elastic/types' -import { createPubSubClient, PubsubClient } from '../datalayer/pubsub' -import normalizeUrl from 'normalize-url' const SAVING_CONTENT = 'Your link is being saved...' @@ -92,10 +91,8 @@ export const createPageSaveRequest = async ( userId, url: normalizedUrl, }) - if (page) { - console.log('Page already exists', page.id, page.url) - articleSavingRequestId = page.id - } else { + if (!page) { + console.log('Page not exists', normalizedUrl) page = { id: articleSavingRequestId, userId, @@ -106,7 +103,7 @@ export const createPageSaveRequest = async ( readingProgressPercent: 0, slug: generateSlug(url), title: url, - url, + url: normalizedUrl, state: ArticleSavingRequestStatus.Processing, createdAt: new Date(), savedAt: new Date(), @@ -123,7 +120,7 @@ export const createPageSaveRequest = async ( } // enqueue task to parse page - await enqueueParseRequest(url, userId, articleSavingRequestId, priority) + await enqueueParseRequest(url, userId, page.id, priority) return pageToArticleSavingRequest(user, page) } diff --git a/packages/api/src/services/save_page.ts b/packages/api/src/services/save_page.ts index d3270ce6e..cc8513678 100644 --- a/packages/api/src/services/save_page.ts +++ b/packages/api/src/services/save_page.ts @@ -1,4 +1,9 @@ +import { Readability } from '@omnivore/readability' +import normalizeUrl from 'normalize-url' import { PubsubClient } from '../datalayer/pubsub' +import { addHighlightToPage } from '../elastic/highlights' +import { createPage, getPageByParam, updatePage } from '../elastic/pages' +import { ArticleSavingRequestStatus, Page, PageType } from '../elastic/types' import { homePageURL } from '../env' import { Maybe, @@ -15,13 +20,7 @@ import { wordsCount, } from '../utils/helpers' import { parsePreparedContent } from '../utils/parser' - -import normalizeUrl from 'normalize-url' import { createPageSaveRequest } from './create_page_save_request' -import { ArticleSavingRequestStatus, Page, PageType } from '../elastic/types' -import { createPage, getPageByParam, updatePage } from '../elastic/pages' -import { addHighlightToPage } from '../elastic/highlights' -import { Readability } from '@omnivore/readability' type SaveContext = { pubsub: PubsubClient @@ -76,7 +75,6 @@ export const savePage = async ( saver: SaverUserData, input: SavePageInput ): Promise => { - const [slug, croppedPathname] = createSlug(input.url, input.title) const parseResult = await parsePreparedContent( input.url, { @@ -88,12 +86,14 @@ export const savePage = async ( }, input.parseResult ) - + const [newSlug, croppedPathname] = createSlug(input.url, input.title) + let slug = newSlug + let pageId = input.clientRequestId const articleToSave = parsedContentToPage({ url: input.url, title: input.title, userId: saver.userId, - pageId: input.clientRequestId, + pageId, slug, croppedPathname, parsedContent: parseResult.parsedContent, @@ -102,7 +102,6 @@ export const savePage = async ( canonicalUrl: parseResult.canonicalUrl, }) - let pageId: string | undefined = undefined const existingPage = await getPageByParam({ userId: saver.userId, url: articleToSave.url, @@ -110,7 +109,6 @@ export const savePage = async ( }) if (existingPage) { - pageId = existingPage.id if ( !(await updatePage( existingPage.id, @@ -126,12 +124,13 @@ export const savePage = async ( message: 'Failed to update existing page', } } - input.clientRequestId = existingPage.id + pageId = existingPage.id + slug = existingPage.slug } else if (shouldParseInBackend(input)) { try { await createPageSaveRequest( saver.userId, - input.url, + articleToSave.url, ctx.models, ctx.pubsub, input.clientRequestId @@ -143,16 +142,17 @@ export const savePage = async ( } } } else { - pageId = await createPage(articleToSave, ctx) - if (!pageId) { + const newPageId = await createPage(articleToSave, ctx) + if (!newPageId) { return { errorCodes: [SaveErrorCode.Unknown], message: 'Failed to create new page', } } + pageId = newPageId } - if (pageId && parseResult.highlightData) { + if (parseResult.highlightData) { const highlight = { updatedAt: new Date(), createdAt: new Date(), @@ -175,7 +175,7 @@ export const savePage = async ( } return { - clientRequestId: input.clientRequestId, + clientRequestId: pageId, url: `${homePageURL()}/${saver.username}/${slug}`, } } From f9396450a50002cc0778830712faebf29be2d709 Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Fri, 17 Mar 2023 11:23:10 +0800 Subject: [PATCH 2/6] Get saving request by url --- packages/api/src/generated/graphql.ts | 6 +++-- packages/api/src/generated/schema.graphql | 3 ++- .../resolvers/article_saving_request/index.ts | 25 ++++++++----------- packages/api/src/schema.ts | 3 ++- 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/packages/api/src/generated/graphql.ts b/packages/api/src/generated/graphql.ts index e1b744946..ae279e18a 100644 --- a/packages/api/src/generated/graphql.ts +++ b/packages/api/src/generated/graphql.ts @@ -168,6 +168,7 @@ export type ArticleSavingRequest = { slug: Scalars['String']; status: ArticleSavingRequestStatus; updatedAt: Scalars['Date']; + url: Scalars['String']; user: User; /** @deprecated userId has been replaced with user */ userId: Scalars['ID']; @@ -1751,7 +1752,7 @@ export type QueryArticleArgs = { export type QueryArticleSavingRequestArgs = { - id: Scalars['ID']; + url: Scalars['String']; }; @@ -4194,6 +4195,7 @@ export type ArticleSavingRequestResolvers; status?: Resolver; updatedAt?: Resolver; + url?: Resolver; user?: Resolver; userId?: Resolver; __isTypeOf?: IsTypeOfResolverFn; @@ -5077,7 +5079,7 @@ export type ProfileResolvers = { apiKeys?: Resolver; article?: Resolver>; - articleSavingRequest?: Resolver>; + articleSavingRequest?: Resolver>; articles?: Resolver>; deviceTokens?: Resolver; feedArticles?: Resolver>; diff --git a/packages/api/src/generated/schema.graphql b/packages/api/src/generated/schema.graphql index 76ba99cc7..895e67713 100644 --- a/packages/api/src/generated/schema.graphql +++ b/packages/api/src/generated/schema.graphql @@ -135,6 +135,7 @@ type ArticleSavingRequest { slug: String! status: ArticleSavingRequestStatus! updatedAt: Date! + url: String! user: User! userId: ID! @deprecated(reason: "userId has been replaced with user") } @@ -1245,7 +1246,7 @@ type Profile { type Query { apiKeys: ApiKeysResult! article(format: String, slug: String!, username: String!): ArticleResult! - articleSavingRequest(id: ID!): ArticleSavingRequestResult! + articleSavingRequest(url: String!): ArticleSavingRequestResult! articles(after: String, first: Int, includePending: Boolean, query: String, sharedOnly: Boolean, sort: SortParams): ArticlesResult! deviceTokens: DeviceTokensResult! feedArticles(after: String, first: Int, sharedByUser: ID, sort: SortParams): FeedArticlesResult! diff --git a/packages/api/src/resolvers/article_saving_request/index.ts b/packages/api/src/resolvers/article_saving_request/index.ts index 6a44d2b1a..38049bf91 100644 --- a/packages/api/src/resolvers/article_saving_request/index.ts +++ b/packages/api/src/resolvers/article_saving_request/index.ts @@ -1,4 +1,6 @@ /* eslint-disable prefer-const */ +import { getPageByParam } from '../../elastic/pages' +import { env } from '../../env' import { ArticleSavingRequestError, ArticleSavingRequestErrorCode, @@ -10,16 +12,14 @@ import { MutationCreateArticleSavingRequestArgs, QueryArticleSavingRequestArgs, } from '../../generated/graphql' +import { createPageSaveRequest } from '../../services/create_page_save_request' +import { analytics } from '../../utils/analytics' import { authorized, isParsingTimeout, pageToArticleSavingRequest, } from '../../utils/helpers' -import { createPageSaveRequest } from '../../services/create_page_save_request' -import { getPageById } from '../../elastic/pages' import { isErrorWithCode } from '../user' -import { analytics } from '../../utils/analytics' -import { env } from '../../env' export const createArticleSavingRequestResolver = authorized< CreateArticleSavingRequestSuccess, @@ -56,17 +56,12 @@ export const articleSavingRequestResolver = authorized< ArticleSavingRequestSuccess, ArticleSavingRequestError, QueryArticleSavingRequestArgs ->(async (_, { id }, { models }) => { - let page - let user - try { - page = await getPageById(id) - if (!page) { - return { errorCodes: [ArticleSavingRequestErrorCode.NotFound] } - } - user = await models.user.get(page.userId) - // eslint-disable-next-line no-empty - } catch (error) {} +>(async (_, { url }, { models, claims }) => { + const page = await getPageByParam({ url, userId: claims.uid }) + if (!page) { + return { errorCodes: [ArticleSavingRequestErrorCode.NotFound] } + } + const user = await models.user.get(page.userId) if (user && page) { if (isParsingTimeout(page)) { page.state = ArticleSavingRequestStatus.Succeeded diff --git a/packages/api/src/schema.ts b/packages/api/src/schema.ts index 48e9dff27..1b6b493a4 100755 --- a/packages/api/src/schema.ts +++ b/packages/api/src/schema.ts @@ -1074,6 +1074,7 @@ const schema = gql` errorCode: CreateArticleErrorCode createdAt: Date! updatedAt: Date! + url: String! } # Query: ArticleSavingRequest @@ -2525,7 +2526,7 @@ const schema = gql` getFollowers(userId: ID): GetFollowersResult! getFollowing(userId: ID): GetFollowingResult! getUserPersonalization: GetUserPersonalizationResult! - articleSavingRequest(id: ID!): ArticleSavingRequestResult! + articleSavingRequest(url: String!): ArticleSavingRequestResult! newsletterEmails: NewsletterEmailsResult! reminder(linkId: ID!): ReminderResult! labels: LabelsResult! From 9e492f7dcb8ce0777487907e1710d4b264e32950 Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Fri, 17 Mar 2023 11:23:17 +0800 Subject: [PATCH 3/6] Add test case --- .../resolvers/article_saving_request.test.ts | 50 ++++++++++--------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/packages/api/test/resolvers/article_saving_request.test.ts b/packages/api/test/resolvers/article_saving_request.test.ts index a0ea2c203..10e533ca8 100644 --- a/packages/api/test/resolvers/article_saving_request.test.ts +++ b/packages/api/test/resolvers/article_saving_request.test.ts @@ -1,22 +1,24 @@ -import { User } from '../../src/entity/user' +import { expect } from 'chai' +import 'mocha' +import sinon from 'sinon' +import { createPubSubClient } from '../../src/datalayer/pubsub' +import { deletePagesByParam, getPageByParam } from '../../src/elastic/pages' import { ArticleSavingRequestStatus, PageContext, } from '../../src/elastic/types' -import { createTestUser, deleteTestUser } from '../db' -import { graphqlRequest, request } from '../util' -import { createPubSubClient } from '../../src/datalayer/pubsub' -import { expect } from 'chai' -import { getPageById } from '../../src/elastic/pages' +import { User } from '../../src/entity/user' import { ArticleSavingRequestErrorCode, CreateArticleSavingRequestErrorCode, } from '../../src/generated/graphql' -import 'mocha' +import * as createTask from '../../src/utils/createTask' +import { createTestUser, deleteTestUser } from '../db' +import { graphqlRequest, request } from '../util' -const articleSavingRequestQuery = (id: string) => ` +const articleSavingRequestQuery = (url: string) => ` query { - articleSavingRequest(id: "${id}") { + articleSavingRequest(url: "${url}") { ... on ArticleSavingRequestSuccess { articleSavingRequest { id @@ -39,6 +41,7 @@ const createArticleSavingRequestMutation = (url: string) => ` articleSavingRequest { id status + url } } ... on CreateArticleSavingRequestError { @@ -67,11 +70,14 @@ describe('ArticleSavingRequest API', () => { refresh: true, uid: user.id, } + sinon.replace(createTask, 'enqueueParseRequest', sinon.fake.resolves('')) }) after(async () => { // clean up + await deletePagesByParam({ userId: user.id }, ctx) await deleteTestUser(user.id) + sinon.restore() }) describe('createArticleSavingRequest', () => { @@ -87,15 +93,14 @@ describe('ArticleSavingRequest API', () => { }) it('creates a page in elastic', async () => { - const res = await graphqlRequest( + const url = 'https://blog.omnivore.app/1' + await graphqlRequest( createArticleSavingRequestMutation('https://blog.omnivore.app/1'), authToken ).expect(200) - const page = await getPageById( - res.body.data.createArticleSavingRequest.articleSavingRequest.id - ) - expect(page?.content).to.eq('Your link is being saved...') + const page = await getPageByParam({ url }) + expect(page?.content).to.eql('Your link is being saved...') }) it('returns an error if the url is invalid', async () => { @@ -111,27 +116,26 @@ describe('ArticleSavingRequest API', () => { }) describe('articleSavingRequest', () => { - let articleSavingRequestId: string + let url: string before(async () => { + url = 'https://blog.omnivore.app/2' // create article saving request - const res = await graphqlRequest( - createArticleSavingRequestMutation('https://blog.omnivore.app/2'), + await graphqlRequest( + createArticleSavingRequestMutation(url), authToken ).expect(200) - articleSavingRequestId = - res.body.data.createArticleSavingRequest.articleSavingRequest.id }) it('returns the article saving request if exists', async () => { const res = await graphqlRequest( - articleSavingRequestQuery(articleSavingRequestId), + articleSavingRequestQuery(url), authToken ).expect(200) - expect(res.body.data.articleSavingRequest.articleSavingRequest.id).to.eql( - articleSavingRequestId - ) + expect( + res.body.data.articleSavingRequest.articleSavingRequest.status + ).to.eql(ArticleSavingRequestStatus.Processing) }) it('returns not_found if not exists', async () => { From cfb62a3995202065ea164c90286756402c218e43 Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Fri, 17 Mar 2023 11:36:34 +0800 Subject: [PATCH 4/6] Update frontend --- .../templates/homeFeed/HomeFeedContainer.tsx | 2 +- .../queries/useGetArticleSavingStatus.tsx | 10 +++---- .../[username]/links/{[id].tsx => [url].tsx} | 27 +++++++++---------- .../link-request/{[id].tsx => [url].tsx} | 22 +++++++-------- .../pages/article/sr/{[id].tsx => [url].tsx} | 27 +++++++++---------- 5 files changed, 43 insertions(+), 45 deletions(-) rename packages/web/pages/[username]/links/{[id].tsx => [url].tsx} (91%) rename packages/web/pages/app/[username]/link-request/{[id].tsx => [url].tsx} (87%) rename packages/web/pages/article/sr/{[id].tsx => [url].tsx} (91%) diff --git a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx index 5385d4417..5d5841fa1 100644 --- a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx +++ b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx @@ -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(`/${username}/links/${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..da09283eb 100644 --- a/packages/web/lib/networking/queries/useGetArticleSavingStatus.tsx +++ b/packages/web/lib/networking/queries/useGetArticleSavingStatus.tsx @@ -6,7 +6,7 @@ import { makeGqlFetcher } from '../networkHelpers' import { ArticleAttributes } from './useGetArticleQuery' type ArticleSavingStatusInput = { - id: string + url: string } type ArticleSavingStatusResponse = { @@ -48,11 +48,11 @@ type ArticleSavingStatusError = | 'unauthorized' export function useGetArticleSavingStatus({ - id, + url, }: ArticleSavingStatusInput): ArticleSavingStatusResponse { const query = gql` - query ArticleSavingRequest($id: ID!) { - articleSavingRequest(id: $id) { + query ArticleSavingRequest($url: String!) { + articleSavingRequest(url: $url) { ... on ArticleSavingRequestSuccess { articleSavingRequest { id @@ -85,7 +85,7 @@ export function useGetArticleSavingStatus({ ` // poll twice a second - const { data, error } = useSWR([query, id], makeGqlFetcher({ id }), { + const { data, error } = useSWR([query, url], makeGqlFetcher({ url }), { refreshInterval: 500, }) diff --git a/packages/web/pages/[username]/links/[id].tsx b/packages/web/pages/[username]/links/[url].tsx similarity index 91% rename from packages/web/pages/[username]/links/[id].tsx rename to packages/web/pages/[username]/links/[url].tsx index 3b7ca4109..1ad0d59b9 100644 --- a/packages/web/pages/[username]/links/[id].tsx +++ b/packages/web/pages/[username]/links/[url].tsx @@ -1,30 +1,29 @@ 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, + 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() const readerSettings = useReaderSettings() - const [articleId, setArticleId] = useState(undefined) + const [url, setUrl] = useState(undefined) applyStoredTheme(false) useEffect(() => { if (!router.isReady) return - setArticleId(router.query.id as string) - }, [router.isReady, router.query.id]) + setUrl(router.query.url as string) + }, [router.isReady, router.query.url]) return ( - {articleId ? : } + {url ? : } @@ -89,7 +88,7 @@ export default function ArticleSavingRequestPage(): JSX.Element { } type PrimaryContentProps = { - articleId: string + url: string } function PrimaryContent(props: PrimaryContentProps): JSX.Element { @@ -97,7 +96,7 @@ function PrimaryContent(props: PrimaryContentProps): JSX.Element { const [timedOut, setTimedOut] = useState(false) const { successRedirectPath, error } = useGetArticleSavingStatus({ - id: props.articleId, + url: props.url, }) useEffect(() => { diff --git a/packages/web/pages/app/[username]/link-request/[id].tsx b/packages/web/pages/app/[username]/link-request/[url].tsx similarity index 87% rename from packages/web/pages/app/[username]/link-request/[id].tsx rename to packages/web/pages/app/[username]/link-request/[url].tsx index 74ba9279c..b116b1de8 100644 --- a/packages/web/pages/app/[username]/link-request/[id].tsx +++ b/packages/web/pages/app/[username]/link-request/[url].tsx @@ -1,25 +1,25 @@ import { useRouter } from 'next/router' import { useEffect, useState } from 'react' -import { Box } from '../../../../components/elements/LayoutPrimitives' -import { useGetArticleSavingStatus } from '../../../../lib/networking/queries/useGetArticleSavingStatus' -import { ErrorComponent } from '../../../../components/templates/SavingRequest' import { useSWRConfig } from 'swr' -import { cacheArticle } from '../../../../lib/networking/queries/useGetArticleQuery' +import { Box } from '../../../../components/elements/LayoutPrimitives' import { PrimaryLayout } from '../../../../components/templates/PrimaryLayout' +import { ErrorComponent } from '../../../../components/templates/SavingRequest' +import { cacheArticle } from '../../../../lib/networking/queries/useGetArticleQuery' +import { useGetArticleSavingStatus } from '../../../../lib/networking/queries/useGetArticleSavingStatus' import { applyStoredTheme } from '../../../../lib/themeUpdater' export default function LinkRequestPage(): JSX.Element { applyStoredTheme(false) // false to skip server sync const router = useRouter() - const [requestID, setRequestID] = useState(undefined) + const [url, setUrl] = useState(undefined) const [username, setUsername] = useState(undefined) useEffect(() => { if (!router.isReady) return - setRequestID(router.query.id as string) + setUrl(router.query.url as string) setUsername(router.query.username as string) - }, [router.isReady, router.query.id, router.query.username]) + }, [router.isReady, router.query.url, router.query.username]) return ( - {requestID && username ? ( - + {url && username ? ( + ) : ( )} @@ -48,7 +48,7 @@ function Loader(): JSX.Element { } type PrimaryContentProps = { - requestID: string + url: string username: string } @@ -58,7 +58,7 @@ function PrimaryContent(props: PrimaryContentProps): JSX.Element { const [timedOut, setTimedOut] = useState(false) const { successRedirectPath, article, error } = useGetArticleSavingStatus({ - id: props.requestID, + url: props.url, }) useEffect(() => { diff --git a/packages/web/pages/article/sr/[id].tsx b/packages/web/pages/article/sr/[url].tsx similarity index 91% rename from packages/web/pages/article/sr/[id].tsx rename to packages/web/pages/article/sr/[url].tsx index 90ef594c8..1a5484c85 100644 --- a/packages/web/pages/article/sr/[id].tsx +++ b/packages/web/pages/article/sr/[url].tsx @@ -1,30 +1,29 @@ 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, + 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() const readerSettings = useReaderSettings() - const [articleId, setArticleId] = useState(undefined) + const [url, setUrl] = useState(undefined) applyStoredTheme(false) useEffect(() => { if (!router.isReady) return - setArticleId(router.query.id as string) - }, [router.isReady, router.query.id]) + setUrl(router.query.url as string) + }, [router.isReady, router.query.url]) return ( - {articleId ? : } + {url ? : } @@ -86,7 +85,7 @@ export default function ArticleSavingRequestPage(): JSX.Element { } type PrimaryContentProps = { - articleId: string + url: string } function PrimaryContent(props: PrimaryContentProps): JSX.Element { @@ -94,7 +93,7 @@ function PrimaryContent(props: PrimaryContentProps): JSX.Element { const [timedOut, setTimedOut] = useState(false) const { successRedirectPath, error } = useGetArticleSavingStatus({ - id: props.articleId, + url: props.url, }) useEffect(() => { From d3f3506c76f12b9b1d96e0a6758ddf88bf900a0d Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Fri, 17 Mar 2023 11:50:28 +0800 Subject: [PATCH 5/6] Encode url --- .../templates/homeFeed/AddLinkModal.tsx | 25 +++---- .../templates/homeFeed/HomeFeedContainer.tsx | 70 +++++++++---------- 2 files changed, 46 insertions(+), 49 deletions(-) diff --git a/packages/web/components/templates/homeFeed/AddLinkModal.tsx b/packages/web/components/templates/homeFeed/AddLinkModal.tsx index de17c92d7..cce30cd21 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 @@ -24,7 +21,7 @@ export function AddLinkModal(props: AddLinkModalProps): JSX.Element { async (link: string) => { const result = await saveUrlMutation(link) // const result = await saveUrlMutation(link) - if (result && result.jobId) { + if (result) { // eslint-disable-next-line @typescript-eslint/no-unused-vars toast( () => ( @@ -35,7 +32,7 @@ export function AddLinkModal(props: AddLinkModalProps): JSX.Element { style="ctaDarkYellow" autoFocus onClick={() => { - window.location.href = `/article/sr/${result.jobId}` + window.location.href = `/article/sr/${encodeURIComponent(link)}` // encode url }} > Read Now diff --git a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx index 5d5841fa1..a5e1c827c 100644 --- a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx +++ b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx @@ -1,51 +1,51 @@ -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, + LibraryItemsQueryInput } from '../../../lib/networking/queries/useGetLibraryItemsQuery' import { useGetLibraryItemsQuery } from '../../../lib/networking/queries/useGetLibraryItemsQuery' import { useGetViewerQuery, - UserBasicData, + 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.url}`) + router.push(`/${username}/links/${encodeURIComponent(item.node.url)}`) } else { const dl = item.node.pageType === PageType.HIGHLIGHTS From 7727a296241f07ccaa3aca52cb5a26803ab8e9e5 Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Fri, 17 Mar 2023 16:01:07 +0800 Subject: [PATCH 6/6] Revert a tsconfig change will cause the dist file structure not as expected --- packages/api/tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/api/tsconfig.json b/packages/api/tsconfig.json index ee1fb782a..7c8caecfd 100644 --- a/packages/api/tsconfig.json +++ b/packages/api/tsconfig.json @@ -7,5 +7,5 @@ "outDir": "dist" }, "include": ["src", "test"], - "exclude": ["./src/generated"] + "exclude": ["./src/generated", "./test"] }