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:
Jackson Harper
2022-03-22 13:58:19 -07:00
parent 42e2beb53d
commit 6daa599b76
13 changed files with 2239 additions and 103 deletions

View File

@ -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,

View File

@ -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

View File

@ -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 ?? '',
})

View 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>
}

View File

@ -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) {

View File

@ -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]
}

View File

@ -1,7 +1,7 @@
import { gql } from 'graphql-request'
import { gqlFetcher } from '../networkHelpers'
type ArticleReadingProgressMutationInput = {
export type ArticleReadingProgressMutationInput = {
id: string
readingProgressPercent: number
readingProgressAnchorIndex: number

View File

@ -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

View File

@ -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
}

View File

@ -1,7 +1,7 @@
import { gql } from 'graphql-request'
import { gqlFetcher } from '../networkHelpers'
type UpdateHighlightInput = {
export type UpdateHighlightInput = {
highlightId: string
annotation?: string
sharedAt?: string

View File

@ -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>
)}

View File

@ -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>

2263
yarn.lock

File diff suppressed because it is too large Load Diff