Separate the mutations out of lower level components
This will let us handle the mutations differently on native iOS and should help in avoiding making web calls directly from the web view, so we can avoid CORs and introduce a caching layer.
This commit is contained in:
@ -12,11 +12,11 @@ import {
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { articleReadingProgressMutation } from '../../../lib/networking/mutations/articleReadingProgressMutation'
|
||||
import { Tweet } from 'react-twitter-widgets'
|
||||
import { render } from 'react-dom'
|
||||
import { isDarkTheme } from '../../../lib/themeUpdater'
|
||||
import { debounce } from 'lodash'
|
||||
import { ArticleMutations } from '../../../lib/articleActions'
|
||||
|
||||
export type ArticleProps = {
|
||||
articleId: string
|
||||
@ -24,6 +24,7 @@ export type ArticleProps = {
|
||||
initialAnchorIndex: number
|
||||
initialReadingProgress?: number
|
||||
scrollElementRef: MutableRefObject<HTMLDivElement | null>
|
||||
articleMutations: ArticleMutations
|
||||
}
|
||||
|
||||
export function Article(props: ArticleProps): JSX.Element {
|
||||
@ -64,7 +65,7 @@ export function Article(props: ArticleProps): JSX.Element {
|
||||
useEffect(() => {
|
||||
;(async () => {
|
||||
if (!readingProgress) return
|
||||
await articleReadingProgressMutation({
|
||||
await props.articleMutations.articleReadingProgressMutation({
|
||||
id: props.articleId,
|
||||
// round reading progress to 100% if more than that
|
||||
readingProgressPercent: readingProgress > 100 ? 100 : readingProgress,
|
||||
|
||||
@ -19,10 +19,12 @@ import { updateThemeLocally } from '../../../lib/themeUpdater'
|
||||
import { EditLabelsModal } from './EditLabelsModal'
|
||||
import Script from 'next/script'
|
||||
import { useRouter } from 'next/router'
|
||||
import { ArticleMutations } from '../../../lib/articleActions'
|
||||
|
||||
type ArticleContainerProps = {
|
||||
viewerUsername: string
|
||||
article: ArticleAttributes
|
||||
articleMutations: ArticleMutations
|
||||
scrollElementRef: MutableRefObject<HTMLDivElement | null>
|
||||
isAppleAppEmbed: boolean
|
||||
highlightBarDisabled: boolean
|
||||
@ -187,6 +189,7 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
|
||||
content={props.article.content}
|
||||
initialAnchorIndex={props.article.readingProgressAnchorIndex}
|
||||
scrollElementRef={props.scrollElementRef}
|
||||
articleMutations={props.articleMutations}
|
||||
/>
|
||||
<Button
|
||||
style="ghost"
|
||||
@ -216,6 +219,7 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
|
||||
showNotesSidebar={showNotesSidebar}
|
||||
highlightsBaseURL={props.highlightsBaseURL}
|
||||
setShowNotesSidebar={setShowNotesSidebar}
|
||||
articleMutations={props.articleMutations}
|
||||
/>
|
||||
{showReportIssuesModal ? (
|
||||
<ReportIssuesModal
|
||||
|
||||
@ -3,7 +3,6 @@ import { makeHighlightStartEndOffset } from '../../../lib/highlights/highlightGe
|
||||
import type { HighlightLocation } from '../../../lib/highlights/highlightGenerator'
|
||||
import { useSelection } from '../../../lib/highlights/useSelection'
|
||||
import type { Highlight } from '../../../lib/networking/fragments/highlightFragment'
|
||||
import { deleteHighlightMutation } from '../../../lib/networking/mutations/deleteHighlightMutation'
|
||||
import { shareHighlightToFeedMutation } from '../../../lib/networking/mutations/shareHighlightToFeedMutation'
|
||||
import { shareHighlightCommentMutation } from '../../../lib/networking/mutations/updateShareHighlightCommentMutation'
|
||||
import {
|
||||
@ -18,9 +17,9 @@ import { HighlightNoteModal } from './HighlightNoteModal'
|
||||
import { ShareHighlightModal } from './ShareHighlightModal'
|
||||
import { HighlightPostToFeedModal } from './HighlightPostToFeedModal'
|
||||
import { HighlightsModal } from './HighlightsModal'
|
||||
import { updateHighlightMutation } from '../../../lib/networking/mutations/updateHighlightMutation'
|
||||
import { useCanShareNative } from '../../../lib/hooks/useCanShareNative'
|
||||
import toast from 'react-hot-toast'
|
||||
import { ArticleMutations } from '../../../lib/articleActions'
|
||||
|
||||
type HighlightsLayerProps = {
|
||||
viewerUsername: string
|
||||
@ -33,6 +32,7 @@ type HighlightsLayerProps = {
|
||||
showNotesSidebar: boolean
|
||||
highlightsBaseURL: string
|
||||
setShowNotesSidebar: React.Dispatch<React.SetStateAction<boolean>>
|
||||
articleMutations: ArticleMutations
|
||||
}
|
||||
|
||||
type HighlightModalAction = 'none' | 'addComment' | 'postToFeed' | 'share'
|
||||
@ -88,7 +88,7 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
|
||||
const highlightId = id || focusedHighlight?.id
|
||||
if (!highlightId) return
|
||||
|
||||
const didDeleteHighlight = await deleteHighlightMutation(highlightId)
|
||||
const didDeleteHighlight = await props.articleMutations.deleteHighlightMutation(highlightId)
|
||||
|
||||
if (didDeleteHighlight) {
|
||||
removeHighlights(
|
||||
@ -191,7 +191,7 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
|
||||
existingHighlights: highlights,
|
||||
highlightStartEndOffsets: highlightLocations,
|
||||
annotation: note,
|
||||
})
|
||||
}, props.articleMutations)
|
||||
|
||||
if (!result.highlights || result.highlights.length == 0) {
|
||||
// TODO: show an error message
|
||||
@ -407,7 +407,7 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
|
||||
if (focusedHighlight) {
|
||||
const annotation = event.annotation ?? ''
|
||||
|
||||
const result = await updateHighlightMutation({
|
||||
const result = await props.articleMutations.updateHighlightMutation({
|
||||
highlightId: focusedHighlight.id,
|
||||
annotation: event.annotation ?? '',
|
||||
})
|
||||
|
||||
14
packages/web/lib/articleActions.tsx
Normal file
14
packages/web/lib/articleActions.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
import { Highlight } from "./networking/fragments/highlightFragment"
|
||||
import { ArticleReadingProgressMutationInput } from "./networking/mutations/articleReadingProgressMutation"
|
||||
import { CreateHighlightInput } from "./networking/mutations/createHighlightMutation"
|
||||
import { MergeHighlightInput, MergeHighlightOutput } from "./networking/mutations/mergeHighlightMutation"
|
||||
import { UpdateHighlightInput } from "./networking/mutations/updateHighlightMutation"
|
||||
|
||||
|
||||
export type ArticleMutations = {
|
||||
createHighlightMutation: (input: CreateHighlightInput) => Promise<Highlight | undefined>
|
||||
deleteHighlightMutation: (highlightId: string) => Promise<boolean>
|
||||
mergeHighlightMutation: (input: MergeHighlightInput) => Promise<MergeHighlightOutput | undefined>
|
||||
updateHighlightMutation: (input: UpdateHighlightInput) => Promise<string | undefined>
|
||||
articleReadingProgressMutation: (input: ArticleReadingProgressMutationInput) => Promise<boolean>
|
||||
}
|
||||
@ -9,9 +9,8 @@ import {
|
||||
import type { HighlightLocation } from './highlightGenerator'
|
||||
import { extendRangeToWordBoundaries } from './normalizeHighlightRange'
|
||||
import type { Highlight } from '../networking/fragments/highlightFragment'
|
||||
import { createHighlightMutation } from '../networking/mutations/createHighlightMutation'
|
||||
import { removeHighlights } from './deleteHighlight'
|
||||
import { mergeHighlightMutation } from '../networking/mutations/mergeHighlightMutation'
|
||||
import { ArticleMutations } from '../articleActions'
|
||||
|
||||
type CreateHighlightInput = {
|
||||
selection: SelectionAttributes
|
||||
@ -28,7 +27,8 @@ type CreateHighlightOutput = {
|
||||
}
|
||||
|
||||
export async function createHighlight(
|
||||
input: CreateHighlightInput
|
||||
input: CreateHighlightInput,
|
||||
articleMutations: ArticleMutations
|
||||
): Promise<CreateHighlightOutput> {
|
||||
|
||||
if (!input.selection.selection) {
|
||||
@ -89,7 +89,7 @@ export async function createHighlight(
|
||||
let keptHighlights = input.existingHighlights
|
||||
|
||||
if (shouldMerge) {
|
||||
const result = await mergeHighlightMutation({
|
||||
const result = await articleMutations.mergeHighlightMutation({
|
||||
...newHighlightAttributes,
|
||||
overlapHighlightIdList: input.selection.overlapHighlights,
|
||||
})
|
||||
@ -99,7 +99,7 @@ export async function createHighlight(
|
||||
($0) => !input.selection.overlapHighlights.includes($0.id)
|
||||
)
|
||||
} else {
|
||||
highlight = await createHighlightMutation(newHighlightAttributes)
|
||||
highlight = await articleMutations.createHighlightMutation(newHighlightAttributes)
|
||||
}
|
||||
|
||||
if (highlight) {
|
||||
|
||||
@ -136,7 +136,7 @@ export function useSelection(
|
||||
document.removeEventListener('touchend', handleFinishTouch)
|
||||
document.removeEventListener('contextmenu', handleFinishTouch)
|
||||
}
|
||||
}, [JSON.stringify(highlightLocations), handleFinishTouch, disabled])
|
||||
}, [highlightLocations, handleFinishTouch, disabled])
|
||||
|
||||
return [selectionAttributes, setSelectionAttributes]
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { gql } from 'graphql-request'
|
||||
import { gqlFetcher } from '../networkHelpers'
|
||||
|
||||
type ArticleReadingProgressMutationInput = {
|
||||
export type ArticleReadingProgressMutationInput = {
|
||||
id: string
|
||||
readingProgressPercent: number
|
||||
readingProgressAnchorIndex: number
|
||||
|
||||
@ -2,7 +2,7 @@ import { gql } from 'graphql-request'
|
||||
import { gqlFetcher } from '../networkHelpers'
|
||||
import { Highlight } from './../fragments/highlightFragment'
|
||||
|
||||
type CreateHighlightInput = {
|
||||
export type CreateHighlightInput = {
|
||||
prefix: string
|
||||
suffix: string
|
||||
quote: string
|
||||
|
||||
@ -2,7 +2,7 @@ import { gql } from 'graphql-request'
|
||||
import { gqlFetcher } from '../networkHelpers'
|
||||
import { Highlight } from './../fragments/highlightFragment'
|
||||
|
||||
type MergeHighlightInput = {
|
||||
export type MergeHighlightInput = {
|
||||
id: string
|
||||
shortId: string
|
||||
articleId: string
|
||||
@ -14,7 +14,7 @@ type MergeHighlightInput = {
|
||||
overlapHighlightIdList: string[]
|
||||
}
|
||||
|
||||
type MergeHighlightOutput = {
|
||||
export type MergeHighlightOutput = {
|
||||
mergeHighlight: InnerMergeHighlightOutput
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { gql } from 'graphql-request'
|
||||
import { gqlFetcher } from '../networkHelpers'
|
||||
|
||||
type UpdateHighlightInput = {
|
||||
export type UpdateHighlightInput = {
|
||||
highlightId: string
|
||||
annotation?: string
|
||||
sharedAt?: string
|
||||
|
||||
@ -13,6 +13,11 @@ import dynamic from 'next/dynamic'
|
||||
import { useGetUserPreferences } from '../../../lib/networking/queries/useGetUserPreferences'
|
||||
import { webBaseURL } from '../../../lib/appConfig'
|
||||
import { Toaster } from 'react-hot-toast'
|
||||
import { createHighlightMutation } from '../../../lib/networking/mutations/createHighlightMutation'
|
||||
import { deleteHighlightMutation } from '../../../lib/networking/mutations/deleteHighlightMutation'
|
||||
import { mergeHighlightMutation } from '../../../lib/networking/mutations/mergeHighlightMutation'
|
||||
import { articleReadingProgressMutation } from '../../../lib/networking/mutations/articleReadingProgressMutation'
|
||||
import { updateHighlightMutation } from '../../../lib/networking/mutations/updateHighlightMutation'
|
||||
|
||||
const PdfArticleContainerNoSSR = dynamic<PdfArticleContainerProps>(
|
||||
() => import('./../../../components/templates/article/PdfArticleContainer'),
|
||||
@ -70,6 +75,13 @@ export default function Home(): JSX.Element {
|
||||
viewerUsername={viewerData.me?.profile?.username}
|
||||
highlightsBaseURL={`${webBaseURL}/${viewerData.me?.profile?.username}/${slug}/highlights`}
|
||||
fontSize={preferencesData?.fontSize}
|
||||
articleMutations={{
|
||||
createHighlightMutation,
|
||||
deleteHighlightMutation,
|
||||
mergeHighlightMutation,
|
||||
updateHighlightMutation,
|
||||
articleReadingProgressMutation,
|
||||
}}
|
||||
/>
|
||||
</VStack>
|
||||
)}
|
||||
|
||||
@ -7,6 +7,11 @@ import { webBaseURL } from '../../../../lib/appConfig'
|
||||
import { LoadingView } from '../../../../components/patterns/LoadingView'
|
||||
import { cookieValue } from '../../../../lib/cookieHelpers'
|
||||
import { applyStoredTheme } from '../../../../lib/themeUpdater'
|
||||
import { createHighlightMutation } from '../../../../lib/networking/mutations/createHighlightMutation'
|
||||
import { deleteHighlightMutation } from '../../../../lib/networking/mutations/deleteHighlightMutation'
|
||||
import { mergeHighlightMutation } from '../../../../lib/networking/mutations/mergeHighlightMutation'
|
||||
import { updateHighlightMutation } from '../../../../lib/networking/mutations/updateHighlightMutation'
|
||||
import { articleReadingProgressMutation } from '../../../../lib/networking/mutations/articleReadingProgressMutation'
|
||||
|
||||
type AppArticleEmbedContentProps = {
|
||||
slug: string
|
||||
@ -88,6 +93,13 @@ function AppArticleEmbedContent(
|
||||
fontSize={props.fontSize}
|
||||
margin={props.margin}
|
||||
fontFamily={props.fontFamily}
|
||||
articleMutations={{
|
||||
createHighlightMutation,
|
||||
deleteHighlightMutation,
|
||||
mergeHighlightMutation,
|
||||
updateHighlightMutation,
|
||||
articleReadingProgressMutation,
|
||||
}}
|
||||
/>
|
||||
</VStack>
|
||||
</Box>
|
||||
|
||||
Reference in New Issue
Block a user