Mark images as submitted on client after successful mutation

This commit is contained in:
ekzyis 2023-10-19 18:16:25 +02:00 committed by ekzyis
parent f79792cd4d
commit 086e1aea3a
8 changed files with 66 additions and 6 deletions

View File

@ -12,6 +12,7 @@ import { useCallback } from 'react'
import { normalizeForwards } from '../lib/form' import { normalizeForwards } from '../lib/form'
import { MAX_TITLE_LENGTH } from '../lib/constants' import { MAX_TITLE_LENGTH } from '../lib/constants'
import { useMe } from './me' import { useMe } from './me'
import { useImages } from './image'
export function BountyForm ({ export function BountyForm ({
item, item,
@ -27,6 +28,7 @@ export function BountyForm ({
const router = useRouter() const router = useRouter()
const client = useApolloClient() const client = useApolloClient()
const me = useMe() const me = useMe()
const { markImagesAsSubmitted } = useImages()
const schema = bountySchema({ client, me, existingBoost: item?.boost }) const schema = bountySchema({ client, me, existingBoost: item?.boost })
const [upsertBounty] = useMutation( const [upsertBounty] = useMutation(
gql` gql`
@ -55,7 +57,11 @@ export function BountyForm ({
id id
} }
} }
` `, {
onCompleted ({ upsertBounty: { text } }) {
markImagesAsSubmitted(text)
}
}
) )
const onSubmit = useCallback( const onSubmit = useCallback(

View File

@ -5,8 +5,10 @@ import { EditFeeButton } from './fee-button'
import Button from 'react-bootstrap/Button' import Button from 'react-bootstrap/Button'
import Delete from './delete' import Delete from './delete'
import { commentSchema } from '../lib/validate' import { commentSchema } from '../lib/validate'
import { useImages } from './image'
export default function CommentEdit ({ comment, editThreshold, onSuccess, onCancel }) { export default function CommentEdit ({ comment, editThreshold, onSuccess, onCancel }) {
const { markImagesAsSubmitted } = useImages()
const [upsertComment] = useMutation( const [upsertComment] = useMutation(
gql` gql`
mutation upsertComment($id: ID! $text: String!) { mutation upsertComment($id: ID! $text: String!) {
@ -14,6 +16,9 @@ export default function CommentEdit ({ comment, editThreshold, onSuccess, onCanc
text text
} }
}`, { }`, {
onCompleted ({ upsertComment: { text } }) {
markImagesAsSubmitted(text)
},
update (cache, { data: { upsertComment } }) { update (cache, { data: { upsertComment } }) {
cache.modify({ cache.modify({
id: `Item:${comment.id}`, id: `Item:${comment.id}`,

View File

@ -17,6 +17,7 @@ import { normalizeForwards } from '../lib/form'
import { MAX_TITLE_LENGTH } from '../lib/constants' import { MAX_TITLE_LENGTH } from '../lib/constants'
import { useMe } from './me' import { useMe } from './me'
import useCrossposter from './use-crossposter' import useCrossposter from './use-crossposter'
import { useImages } from './image'
export function DiscussionForm ({ export function DiscussionForm ({
item, sub, editThreshold, titleLabel = 'title', item, sub, editThreshold, titleLabel = 'title',
@ -30,14 +31,21 @@ export function DiscussionForm ({
// if Web Share Target API was used // if Web Share Target API was used
const shareTitle = router.query.title const shareTitle = router.query.title
const crossposter = useCrossposter() const crossposter = useCrossposter()
const { markImagesAsSubmitted } = useImages()
const [upsertDiscussion] = useMutation( const [upsertDiscussion] = useMutation(
gql` gql`
mutation upsertDiscussion($sub: String, $id: ID, $title: String!, $text: String, $boost: Int, $forward: [ItemForwardInput], $hash: String, $hmac: String) { mutation upsertDiscussion($sub: String, $id: ID, $title: String!, $text: String, $boost: Int, $forward: [ItemForwardInput], $hash: String, $hmac: String) {
upsertDiscussion(sub: $sub, id: $id, title: $title, text: $text, boost: $boost, forward: $forward, hash: $hash, hmac: $hmac) { upsertDiscussion(sub: $sub, id: $id, title: $title, text: $text, boost: $boost, forward: $forward, hash: $hash, hmac: $hmac) {
id id
text
} }
}` }`,
{
onCompleted ({ upsertDiscussion: { text } }) {
markImagesAsSubmitted(text)
}
}
) )
const onSubmit = useCallback( const onSubmit = useCallback(

View File

@ -8,6 +8,7 @@ import { UPLOAD_TYPES_ALLOW } from '../lib/constants'
import { useToast } from './toast' import { useToast } from './toast'
import gql from 'graphql-tag' import gql from 'graphql-tag'
import { useMutation, useQuery } from '@apollo/client' import { useMutation, useQuery } from '@apollo/client'
import { extractUrls } from '../lib/md'
const ImageContext = createContext({ unsubmitted: [] }) const ImageContext = createContext({ unsubmitted: [] })
@ -44,6 +45,18 @@ export function ImageProvider ({ me, children }) {
}) })
const [unsubmittedImages, setUnsubmittedImages] = useState([]) const [unsubmittedImages, setUnsubmittedImages] = useState([])
const markImagesAsSubmitted = useCallback((text) => {
// mark images from S3 included in the text as submitted on the client
const urls = extractUrls(text)
const s3UrlPrefix = `https://${process.env.NEXT_PUBLIC_AWS_UPLOAD_BUCKET}.s3.amazonaws.com/`
urls
.filter(url => url.startsWith(s3UrlPrefix))
.forEach(url => {
const s3Key = url.split('/').pop()
setUnsubmittedImages(prev => prev.filter(img => img.id !== s3Key))
})
}, [setUnsubmittedImages])
useEffect(() => { useEffect(() => {
const images = data?.me?.images const images = data?.me?.images
if (images) { if (images) {
@ -54,7 +67,8 @@ export function ImageProvider ({ me, children }) {
const contextValue = { const contextValue = {
unsubmittedImages, unsubmittedImages,
setUnsubmittedImages, setUnsubmittedImages,
deleteImage deleteImage,
markImagesAsSubmitted
} }
return ( return (

View File

@ -18,6 +18,7 @@ import ActionTooltip from './action-tooltip'
import { jobSchema } from '../lib/validate' import { jobSchema } from '../lib/validate'
import CancelButton from './cancel-button' import CancelButton from './cancel-button'
import { MAX_TITLE_LENGTH } from '../lib/constants' import { MAX_TITLE_LENGTH } from '../lib/constants'
import { useImages } from './image'
function satsMin2Mo (minute) { function satsMin2Mo (minute) {
return minute * 30 * 24 * 60 return minute * 30 * 24 * 60
@ -40,6 +41,7 @@ export default function JobForm ({ item, sub }) {
const storageKeyPrefix = item ? undefined : `${sub.name}-job` const storageKeyPrefix = item ? undefined : `${sub.name}-job`
const router = useRouter() const router = useRouter()
const [logoId, setLogoId] = useState(item?.uploadId) const [logoId, setLogoId] = useState(item?.uploadId)
const { markImagesAsSubmitted } = useImages()
const [upsertJob] = useMutation(gql` const [upsertJob] = useMutation(gql`
mutation upsertJob($sub: String!, $id: ID, $title: String!, $company: String!, $location: String, mutation upsertJob($sub: String!, $id: ID, $title: String!, $company: String!, $location: String,
$remote: Boolean, $text: String!, $url: String!, $maxBid: Int!, $status: String, $logo: Int, $hash: String, $hmac: String) { $remote: Boolean, $text: String!, $url: String!, $maxBid: Int!, $status: String, $logo: Int, $hash: String, $hmac: String) {
@ -47,8 +49,13 @@ export default function JobForm ({ item, sub }) {
location: $location, remote: $remote, text: $text, location: $location, remote: $remote, text: $text,
url: $url, maxBid: $maxBid, status: $status, logo: $logo, hash: $hash, hmac: $hmac) { url: $url, maxBid: $maxBid, status: $status, logo: $logo, hash: $hash, hmac: $hmac) {
id id
text
} }
}` }`, {
onCompleted ({ upsertJob: { text } }) {
markImagesAsSubmitted(text)
}
}
) )
const onSubmit = useCallback( const onSubmit = useCallback(

View File

@ -17,6 +17,7 @@ import CancelButton from './cancel-button'
import { normalizeForwards } from '../lib/form' import { normalizeForwards } from '../lib/form'
import { MAX_TITLE_LENGTH } from '../lib/constants' import { MAX_TITLE_LENGTH } from '../lib/constants'
import { useMe } from './me' import { useMe } from './me'
import { useImages } from './image'
export function LinkForm ({ item, sub, editThreshold, children }) { export function LinkForm ({ item, sub, editThreshold, children }) {
const router = useRouter() const router = useRouter()
@ -52,6 +53,7 @@ export function LinkForm ({ item, sub, editThreshold, children }) {
} }
} }
}`) }`)
const { markImagesAsSubmitted } = useImages
const related = [] const related = []
for (const item of relatedData?.related?.items || []) { for (const item of relatedData?.related?.items || []) {
@ -73,8 +75,14 @@ export function LinkForm ({ item, sub, editThreshold, children }) {
mutation upsertLink($sub: String, $id: ID, $title: String!, $url: String!, $text: String, $boost: Int, $forward: [ItemForwardInput], $hash: String, $hmac: String) { mutation upsertLink($sub: String, $id: ID, $title: String!, $url: String!, $text: String, $boost: Int, $forward: [ItemForwardInput], $hash: String, $hmac: String) {
upsertLink(sub: $sub, id: $id, title: $title, url: $url, text: $text, boost: $boost, forward: $forward, hash: $hash, hmac: $hmac) { upsertLink(sub: $sub, id: $id, title: $title, url: $url, text: $text, boost: $boost, forward: $forward, hash: $hash, hmac: $hmac) {
id id
text
} }
}` }`,
{
onCompleted ({ upsertLink: { text } }) {
markImagesAsSubmitted(text)
}
}
) )
const onSubmit = useCallback( const onSubmit = useCallback(

View File

@ -13,12 +13,14 @@ import CancelButton from './cancel-button'
import { useCallback } from 'react' import { useCallback } from 'react'
import { normalizeForwards } from '../lib/form' import { normalizeForwards } from '../lib/form'
import { useMe } from './me' import { useMe } from './me'
import { useImages } from './image'
export function PollForm ({ item, sub, editThreshold, children }) { export function PollForm ({ item, sub, editThreshold, children }) {
const router = useRouter() const router = useRouter()
const client = useApolloClient() const client = useApolloClient()
const me = useMe() const me = useMe()
const schema = pollSchema({ client, me, existingBoost: item?.boost }) const schema = pollSchema({ client, me, existingBoost: item?.boost })
const { markImagesAsSubmitted } = useImages()
const [upsertPoll] = useMutation( const [upsertPoll] = useMutation(
gql` gql`
@ -27,8 +29,13 @@ export function PollForm ({ item, sub, editThreshold, children }) {
upsertPoll(sub: $sub, id: $id, title: $title, text: $text, upsertPoll(sub: $sub, id: $id, title: $title, text: $text,
options: $options, boost: $boost, forward: $forward, hash: $hash, hmac: $hmac) { options: $options, boost: $boost, forward: $forward, hash: $hash, hmac: $hmac) {
id id
text
} }
}` }`, {
onCompleted ({ upsertPoll: { text } }) {
markImagesAsSubmitted(text)
}
}
) )
const onSubmit = useCallback( const onSubmit = useCallback(

View File

@ -11,6 +11,7 @@ import { commentSchema } from '../lib/validate'
import Info from './info' import Info from './info'
import { quote } from '../lib/md' import { quote } from '../lib/md'
import { COMMENT_DEPTH_LIMIT } from '../lib/constants' import { COMMENT_DEPTH_LIMIT } from '../lib/constants'
import { useImages } from './image'
export function ReplyOnAnotherPage ({ item }) { export function ReplyOnAnotherPage ({ item }) {
const path = item.path.split('.') const path = item.path.split('.')
@ -49,6 +50,7 @@ export default forwardRef(function Reply ({ item, onSuccess, replyOpen, children
const parentId = item.id const parentId = item.id
const replyInput = useRef(null) const replyInput = useRef(null)
const formInnerRef = useRef() const formInnerRef = useRef()
const { markImagesAsSubmitted } = useImages()
// Start block to handle iOS Safari's weird selection clearing behavior // Start block to handle iOS Safari's weird selection clearing behavior
const savedRange = useRef() const savedRange = useRef()
@ -116,6 +118,9 @@ export default forwardRef(function Reply ({ item, onSuccess, replyOpen, children
} }
} }
}`, { }`, {
onCompleted ({ upsertComment: { text } }) {
markImagesAsSubmitted(text)
},
update (cache, { data: { upsertComment } }) { update (cache, { data: { upsertComment } }) {
cache.modify({ cache.modify({
id: `Item:${parentId}`, id: `Item:${parentId}`,