Skip to content

Commit 001442f

Browse files
committed
iterate
1 parent 1438200 commit 001442f

File tree

12 files changed

+253
-88
lines changed

12 files changed

+253
-88
lines changed

app/API/graphql_queries.ts

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -242,27 +242,6 @@ export const SEARCH_SPEAKERS_QUERY = gql`
242242
}
243243
`
244244

245-
export const ADD_SPEAKER_TO_VIDEO_MUTATION = gql`
246-
mutation AddSpeakerToVideo($videoId: ID!, $speakerId: ID!) {
247-
addSpeakerToVideo(videoId: $videoId, speakerId: $speakerId) {
248-
id
249-
fullName
250-
slug
251-
picture
252-
}
253-
}
254-
`
255-
256-
export const CREATE_SPEAKER_MUTATION = gql`
257-
mutation CreateSpeaker($videoId: ID!, $fullName: String!) {
258-
createSpeaker(videoId: $videoId, fullName: $fullName) {
259-
id
260-
fullName
261-
slug
262-
picture
263-
}
264-
}
265-
`
266245

267246
export const REMOVE_SPEAKER_FROM_VIDEO_MUTATION = gql`
268247
mutation RemoveSpeakerFromVideo($videoId: ID!, $speakerId: ID!) {
Lines changed: 34 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* eslint-disable jsx-a11y/anchor-has-content */
22
/* eslint-disable jsx-a11y/heading-has-content */
3-
import PropTypes from 'prop-types'
3+
44
import React, { useEffect, useState } from 'react'
55
import Markdown from 'react-markdown'
66
import { Link } from 'react-router-dom'
@@ -14,7 +14,7 @@ import { LoadingFrame } from '../Utils/LoadingFrame'
1414

1515
const ERROR_NOT_FOUND = 'not_found'
1616

17-
const HelpPageContent = ({ page, onLinkClick }) => {
17+
const HelpPageContent = ({ page, onLinkClick }: { page: string; onLinkClick: () => void }) => {
1818
const { locale } = useUserPreferences()
1919
const [markdownContent, setMarkdownContent] = useState('')
2020
const [isLoading, setIsLoading] = useState(false)
@@ -28,11 +28,11 @@ const HelpPageContent = ({ page, onLinkClick }) => {
2828
try {
2929
const response = await fetch(`/assets/help/${locale}/${page}.md`)
3030
const contentType = response.headers.get('Content-Type')
31-
31+
3232
if (contentType && !contentType.includes('markdown')) {
3333
throw ERROR_NOT_FOUND
3434
}
35-
35+
3636
if (response.status === 200 || response.status === 304) {
3737
const text = await response.text()
3838
setMarkdownContent(text)
@@ -55,11 +55,11 @@ const HelpPageContent = ({ page, onLinkClick }) => {
5555
if (isLoading) {
5656
return <LoadingFrame />
5757
}
58-
58+
5959
if (error) {
6060
return <ErrorView canGoBack={false} error={error} />
6161
}
62-
62+
6363
return (
6464
<Markdown
6565
className="content"
@@ -110,25 +110,36 @@ const HelpPageContent = ({ page, onLinkClick }) => {
110110
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
111111
li: ({ node, ...props }) => <li className="leading-relaxed mb-2" {...props} />,
112112
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
113-
blockquote: ({ node, ...props }) => (
114-
<blockquote
115-
className="border-l-4 border-blue-400 dark:border-blue-500 bg-blue-50 dark:bg-blue-950/30 text-gray-600 dark:text-foreground px-6 py-1 mb-6 rounded-r italic leading-relaxed"
116-
{...props}
117-
/>
118-
),
113+
blockquote: ({ node, ...props }) => {
114+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
115+
const { ref: _ref, ...restProps } =
116+
props as React.ComponentPropsWithoutRef<'blockquote'> & { ref?: React.Ref<HTMLElement> }
117+
return (
118+
<blockquote
119+
className="border-l-4 border-blue-400 dark:border-blue-500 bg-blue-50 dark:bg-blue-950/30 text-gray-600 dark:text-foreground px-6 py-1 mb-6 rounded-r italic leading-relaxed"
120+
{...restProps}
121+
/>
122+
)
123+
},
119124
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
120125
code: ({ node, className, children, ...props }) => {
121-
return typeof children === 'string' && children.length < 30 ? (
122-
<code
123-
className="bg-gray-100 dark:bg-accent text-pink-600 dark:text-pink-400 px-2 py-0.5 rounded font-mono text-sm"
124-
{...props}
125-
>
126-
{children}
127-
</code>
128-
) : (
126+
if (typeof children === 'string' && children.length < 30) {
127+
return (
128+
<code
129+
className="bg-gray-100 dark:bg-accent text-pink-600 dark:text-pink-400 px-2 py-0.5 rounded font-mono text-sm"
130+
{...props}
131+
>
132+
{children}
133+
</code>
134+
)
135+
}
136+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
137+
const { ref: _ref, ...restProps } =
138+
props as React.ComponentPropsWithoutRef<'blockquote'> & { ref?: React.Ref<HTMLElement> }
139+
return (
129140
<blockquote
130141
className="border-l-4 border-blue-400 dark:border-blue-500 bg-blue-50 dark:bg-blue-950/30 text-gray-600 dark:text-foreground px-6 py-3 mb-6 rounded-r italic leading-relaxed"
131-
{...props}
142+
{...restProps}
132143
>
133144
{children}
134145
</blockquote>
@@ -166,10 +177,7 @@ const HelpPageContent = ({ page, onLinkClick }) => {
166177
),
167178
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
168179
th: ({ node, ...props }) => (
169-
<th
170-
className="p-3 font-medium text-left text-gray-700 dark:text-foreground"
171-
{...props}
172-
/>
180+
<th className="p-3 font-medium text-left text-gray-700 dark:text-foreground" {...props} />
173181
),
174182
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
175183
td: ({ node, ...props }) => (
@@ -182,10 +190,4 @@ const HelpPageContent = ({ page, onLinkClick }) => {
182190
)
183191
}
184192

185-
HelpPageContent.propTypes = {
186-
page: PropTypes.string.isRequired,
187-
onLinkClick: PropTypes.func,
188-
locale: PropTypes.string.isRequired,
189-
}
190-
191-
export default HelpPageContent
193+
export default HelpPageContent

app/components/Notifications/SubscribeBtn.jsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { gql } from '@apollo/client'
21
import { Mutation } from '@apollo/client/react/components'
32
import { UPDATE_SUBSCRIPTION_MUTATION } from 'app/API/graphql_queries'
43
import { Bell, BellOff } from 'lucide-react'

app/components/Speakers/AddSpeakerForm.jsx

Lines changed: 69 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,68 @@
1-
import { useLazyQuery, useMutation } from '@apollo/client'
1+
import { gql, useLazyQuery, useMutation } from '@apollo/client'
22
import React, { useMemo, useState } from 'react'
33
import { withTranslation } from 'react-i18next'
44

5-
import {
6-
ADD_SPEAKER_TO_VIDEO_MUTATION,
7-
CREATE_SPEAKER_MUTATION,
8-
SEARCH_SPEAKERS_QUERY,
9-
} from '../../API/graphql_queries'
5+
import { toastError } from '@/lib/toasts'
6+
7+
import { SEARCH_SPEAKERS_QUERY } from '../../API/graphql_queries'
108
import { SPEAKER_NAME_LENGTH } from '../../constants'
119
import { cleanStr } from '../../lib/clean_str'
1210
import { checkLength } from '../../lib/form_validators'
1311
import capitalizeName from '../../lib/name_formatter'
1412
import { ReactiveAsyncCreatable, ReactSelectTheme } from '../../lib/react_select_theme'
1513

14+
const videoDebateSpeakerFieldsFragment = gql`
15+
fragment SpeakerFields on Speaker {
16+
id
17+
fullName
18+
slug
19+
picture
20+
title
21+
}
22+
`
23+
24+
const ADD_SPEAKER_TO_VIDEO_MUTATION = gql`
25+
mutation AddSpeakerToVideo($videoId: ID!, $speakerId: ID!) {
26+
addSpeakerToVideo(videoId: $videoId, speakerId: $speakerId) {
27+
...SpeakerFields
28+
}
29+
}
30+
${videoDebateSpeakerFieldsFragment}
31+
`
32+
33+
const CREATE_SPEAKER_MUTATION = gql`
34+
mutation CreateSpeaker($videoId: ID!, $fullName: String!) {
35+
createSpeaker(videoId: $videoId, fullName: $fullName) {
36+
...SpeakerFields
37+
}
38+
}
39+
${videoDebateSpeakerFieldsFragment}
40+
`
41+
1642
const AddSpeakerForm = ({ disabled, videoId, t }) => {
1743
const [isSearching, setIsSearching] = useState(false)
1844

1945
const [searchSpeakers, { loading: searchLoading }] = useLazyQuery(SEARCH_SPEAKERS_QUERY)
20-
const [addSpeakerToVideo] = useMutation(ADD_SPEAKER_TO_VIDEO_MUTATION)
21-
const [createSpeaker] = useMutation(CREATE_SPEAKER_MUTATION)
46+
const [addSpeakerToVideo] = useMutation(ADD_SPEAKER_TO_VIDEO_MUTATION, {
47+
update: (cache, { data }) => {
48+
cache.modify({
49+
id: cache.identify({ __typename: 'Video', id: videoId }),
50+
fields: {
51+
speakers: (existingSpeakers = []) => [...existingSpeakers, data.addSpeakerToVideo],
52+
},
53+
})
54+
},
55+
})
56+
const [createSpeaker] = useMutation(CREATE_SPEAKER_MUTATION, {
57+
update: (cache, { data }) => {
58+
cache.modify({
59+
id: cache.identify({ __typename: 'Video', id: videoId }),
60+
fields: {
61+
speakers: (existingSpeakers = []) => [...existingSpeakers, data.createSpeaker],
62+
},
63+
})
64+
},
65+
})
2266

2367
const debouncedSearch = useMemo(() => {
2468
let timeoutId
@@ -48,19 +92,23 @@ const AddSpeakerForm = ({ disabled, videoId, t }) => {
4892
}
4993

5094
const onChange = async ({ value }, { action }) => {
51-
if (action === 'select-option' && value && value.id) {
52-
// Add existing speaker to video
53-
await addSpeakerToVideo({
54-
variables: { videoId, speakerId: value.id },
55-
})
56-
} else if (action === 'create-option' && checkLength(value, SPEAKER_NAME_LENGTH)) {
57-
// Create new speaker and add to video
58-
await createSpeaker({
59-
variables: {
60-
videoId,
61-
fullName: capitalizeName(cleanStr(value)),
62-
},
63-
})
95+
try {
96+
if (action === 'select-option' && value && value.id) {
97+
// Add existing speaker to video
98+
await addSpeakerToVideo({
99+
variables: { videoId, speakerId: value.id },
100+
})
101+
} else if (action === 'create-option' && checkLength(value, SPEAKER_NAME_LENGTH)) {
102+
// Create new speaker and add to video
103+
await createSpeaker({
104+
variables: {
105+
videoId,
106+
fullName: capitalizeName(cleanStr(value)),
107+
},
108+
})
109+
}
110+
} catch (error) {
111+
toastError(error)
64112
}
65113
}
66114

app/components/Speakers/SpeakerPreview.jsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { toast } from '@/hooks/use-toast'
1010
import { REMOVE_SPEAKER_FROM_VIDEO_MUTATION } from '../../API/graphql_queries'
1111
import DialogConfirmDelete from '../Dialogs/DialogConfirmDelete'
1212
import { Avatar, AvatarImage } from '../ui/avatar'
13+
import { removeSpeakerFromVideoCache } from '../VideoDebate/graphql-cache'
1314
import EditSpeakerFormModal from './EditSpeakerFormModal'
1415
import { SpeakerDropdownMenu } from './SpeakerDropdownMenu'
1516

@@ -38,10 +39,12 @@ const SpeakerPreview = ({
3839
const handleConfirmRemoveSpeaker = async () => {
3940
await removeSpeakerFromVideo({
4041
variables: { videoId, speakerId: speaker.id },
42+
update: (cache) => {
43+
removeSpeakerFromVideoCache(cache, videoId, speaker.id)
44+
},
4145
})
4246
toast({
43-
title: t('speaker.remove'),
44-
description: t('speaker.confirmRemove', { speaker }),
47+
title: t('speaker.removed'),
4548
})
4649
setDeleteModalOpen(false)
4750
}

app/components/Statements/StatementForm.jsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,14 +159,25 @@ const StatementForm = ({
159159
speakerId: statement.speakerId || null,
160160
isDraft: statement.isDraft || false,
161161
},
162+
update: (cache, { data }) => {
163+
cache.modify({
164+
id: cache.identify({ __typename: 'Video', id: videoId }),
165+
fields: {
166+
statements: (existingStatements = []) => [
167+
...existingStatements,
168+
data.createStatement,
169+
],
170+
},
171+
})
172+
},
162173
})
163174
response = result.data?.createStatement || { success: true }
164175
}
165176

166177
setSubmitting(false)
167-
// The GraphQL subscription will handle adding/updating the statement
178+
168179
// If we have an ID from the response, scroll to it
169-
if (response && response.id) {
180+
if (response.id) {
170181
onSetScrollTo({ id: response.id, __forceAutoScroll: true })
171182
}
172183
// Call onSuccess callback if provided (e.g., to clear the form)

app/components/Users/User.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,7 @@ const User: React.FC<UserProps> = ({ children }) => {
133133
</UserMenu>
134134
</div>
135135
</div>
136-
<DisplayedUserContext.Provider value={{ user: user || null }}>
137-
{children}
138-
</DisplayedUserContext.Provider>
136+
<DisplayedUserContext.Provider value={{ user }}>{children}</DisplayedUserContext.Provider>
139137
</div>
140138
)
141139
}

app/components/UsersActions/UserAction.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ const UserAction = ({
106106

107107
{Boolean(
108108
action.changes &&
109-
(typeof action.changes === 'string' || Object.keys(action.changes || {}).length > 0),
109+
(typeof action.changes === 'string' || Object.keys(action.changes).length > 0),
110110
) && (
111111
<Button
112112
size="xs"

0 commit comments

Comments
 (0)