mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
Merge pull request #1921 from omnivore-app/fix/save-page
Return correct page slug and get saving request by url
This commit is contained in:
commit
154d4aaa69
14 changed files with 163 additions and 168 deletions
|
|
@ -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<ContextType = ResolverContext, ParentT
|
|||
slug?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
status?: Resolver<ResolversTypes['ArticleSavingRequestStatus'], ParentType, ContextType>;
|
||||
updatedAt?: Resolver<ResolversTypes['Date'], ParentType, ContextType>;
|
||||
url?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
user?: Resolver<ResolversTypes['User'], ParentType, ContextType>;
|
||||
userId?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
|
|
@ -5077,7 +5079,7 @@ export type ProfileResolvers<ContextType = ResolverContext, ParentType extends R
|
|||
export type QueryResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['Query'] = ResolversParentTypes['Query']> = {
|
||||
apiKeys?: Resolver<ResolversTypes['ApiKeysResult'], ParentType, ContextType>;
|
||||
article?: Resolver<ResolversTypes['ArticleResult'], ParentType, ContextType, RequireFields<QueryArticleArgs, 'slug' | 'username'>>;
|
||||
articleSavingRequest?: Resolver<ResolversTypes['ArticleSavingRequestResult'], ParentType, ContextType, RequireFields<QueryArticleSavingRequestArgs, 'id'>>;
|
||||
articleSavingRequest?: Resolver<ResolversTypes['ArticleSavingRequestResult'], ParentType, ContextType, RequireFields<QueryArticleSavingRequestArgs, 'url'>>;
|
||||
articles?: Resolver<ResolversTypes['ArticlesResult'], ParentType, ContextType, Partial<QueryArticlesArgs>>;
|
||||
deviceTokens?: Resolver<ResolversTypes['DeviceTokensResult'], ParentType, ContextType>;
|
||||
feedArticles?: Resolver<ResolversTypes['FeedArticlesResult'], ParentType, ContextType, Partial<QueryFeedArticlesArgs>>;
|
||||
|
|
|
|||
|
|
@ -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!
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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!
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<SaveResult> => {
|
||||
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}`,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 () => {
|
||||
|
|
|
|||
|
|
@ -7,5 +7,5 @@
|
|||
"outDir": "dist"
|
||||
},
|
||||
"include": ["src", "test"],
|
||||
"exclude": ["./src/generated"]
|
||||
"exclude": ["./src/generated", "./test"]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.id}`)
|
||||
router.push(`/${username}/links/${encodeURIComponent(item.node.url)}`)
|
||||
} else {
|
||||
const dl =
|
||||
item.node.pageType === PageType.HIGHLIGHTS
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -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<string | undefined>(undefined)
|
||||
const [url, setUrl] = useState<string | undefined>(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 (
|
||||
<PrimaryLayout
|
||||
|
|
@ -81,7 +80,7 @@ export default function ArticleSavingRequestPage(): JSX.Element {
|
|||
fontSize={readerSettings.fontSize}
|
||||
lineHeight={readerSettings.lineHeight}
|
||||
>
|
||||
{articleId ? <PrimaryContent articleId={articleId} /> : <Loader />}
|
||||
{url ? <PrimaryContent url={url} /> : <Loader />}
|
||||
</SkeletonArticleContainer>
|
||||
</VStack>
|
||||
</PrimaryLayout>
|
||||
|
|
@ -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(() => {
|
||||
|
|
@ -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<string | undefined>(undefined)
|
||||
const [url, setUrl] = useState<string | undefined>(undefined)
|
||||
const [username, setUsername] = useState<string | undefined>(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 (
|
||||
<PrimaryLayout
|
||||
|
|
@ -33,8 +33,8 @@ export default function LinkRequestPage(): JSX.Element {
|
|||
<Box
|
||||
css={{ bg: '$grayBase', height: '100vh', width: '100vw', px: '16px' }}
|
||||
>
|
||||
{requestID && username ? (
|
||||
<PrimaryContent requestID={requestID} username={username} />
|
||||
{url && username ? (
|
||||
<PrimaryContent url={url} username={username} />
|
||||
) : (
|
||||
<Loader />
|
||||
)}
|
||||
|
|
@ -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(() => {
|
||||
|
|
@ -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<string | undefined>(undefined)
|
||||
const [url, setUrl] = useState<string | undefined>(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 (
|
||||
<PrimaryLayout
|
||||
|
|
@ -78,7 +77,7 @@ export default function ArticleSavingRequestPage(): JSX.Element {
|
|||
fontSize={readerSettings.fontSize}
|
||||
lineHeight={readerSettings.lineHeight}
|
||||
>
|
||||
{articleId ? <PrimaryContent articleId={articleId} /> : <Loader />}
|
||||
{url ? <PrimaryContent url={url} /> : <Loader />}
|
||||
</SkeletonArticleContainer>
|
||||
</VStack>
|
||||
</PrimaryLayout>
|
||||
|
|
@ -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(() => {
|
||||
Loading…
Reference in a new issue