Skip to content

Commit

Permalink
Update cache when posting nested replies
Browse files Browse the repository at this point in the history
Test Plan:
 - Navigate to a discussion, example:
     /courses/1/discussion_topics/16.
 - Click on the Reply button on an entry and, reply of an entry.
 - Write a text and hit reply. The new reply should appear.

flag=react_discussions_post

Refs VICE-1228

Change-Id: I4c80305d83187013791f38e6d0e55518ca6c1d0b
Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/264714
Reviewed-by: Matthew Lemon <[email protected]>
QA-Review: Matthew Lemon <[email protected]>
Product-Review: Matthew Lemon <[email protected]>
Tested-by: Service Cloud Jenkins <[email protected]>
  • Loading branch information
omarpr committed May 12, 2021
1 parent eac14a4 commit 51e5ca0
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ const DiscussionTopicManager = props => {
const currentDiscussion = JSON.parse(JSON.stringify(cache.readQuery(options)))

if (currentDiscussion && newDiscussionEntry) {
currentDiscussion.legacyNode.rootDiscussionEntriesConnection.nodes.push(newDiscussionEntry)
currentDiscussion.legacyNode.entryCounts.repliesCount += 1
currentDiscussion.legacyNode.discussionEntriesConnection.nodes.push(newDiscussionEntry)

// TODO: Handle sorting.
cache.writeQuery({...options, data: currentDiscussion})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,13 @@ import {ThreadActions} from '../../components/ThreadActions/ThreadActions'
import {ThreadingToolbar} from '../../components/ThreadingToolbar/ThreadingToolbar'
import {useMutation, useQuery} from 'react-apollo'
import {View} from '@instructure/ui-view'
import {isGraded, getSpeedGraderUrl} from '../../utils'
import {
isGraded,
getSpeedGraderUrl,
addReplyToDiscussionEntry,
addReplyToSubentries,
addReplyToDiscussion
} from '../../utils'
import theme from '@instructure/canvas-theme'

export const mockThreads = {
Expand Down Expand Up @@ -83,7 +89,16 @@ export const DiscussionThreadContainer = props => {
const [editorExpanded, setEditorExpanded] = useState(false)
const threadRef = useRef()

const updateCache = (cache, result) => {
const newDiscussionEntry = result.data.createDiscussionEntry.discussionEntry

addReplyToDiscussion(cache, props.discussionTopicGraphQLId, newDiscussionEntry)
addReplyToDiscussionEntry(cache, props.discussionEntry.id, newDiscussionEntry)
addReplyToSubentries(cache, props.discussionEntry._id, newDiscussionEntry)
}

const [createDiscussionEntry] = useMutation(CREATE_DISCUSSION_ENTRY, {
update: updateCache,
onCompleted: () => {
setOnSuccess(I18n.t('The discussion entry was successfully created.'))
},
Expand Down Expand Up @@ -352,6 +367,7 @@ export const DiscussionThreadContainer = props => {
</div>
{(expandReplies || props.depth > 0) && props.discussionEntry.subentriesCount > 0 && (
<DiscussionSubentries
discussionTopicGraphQLId={props.discussionTopicGraphQLId}
discussionEntryId={props.discussionEntry._id}
depth={props.depth + 1}
/>
Expand Down Expand Up @@ -380,6 +396,7 @@ export const DiscussionThreadContainer = props => {
}

DiscussionThreadContainer.propTypes = {
discussionTopicGraphQLId: PropTypes.string,
discussionEntry: DiscussionEntry.shape,
depth: PropTypes.number,
assignment: Assignment.shape
Expand Down Expand Up @@ -419,11 +436,13 @@ const DiscussionSubentries = props => {
depth={props.depth}
assignment={discussionTopic?.assignment}
discussionEntry={entry}
discussionTopicGraphQLId={props.discussionTopicGraphQLId}
/>
))
}

DiscussionSubentries.propTypes = {
discussionTopicGraphQLId: PropTypes.string,
discussionEntryId: PropTypes.string,
depth: PropTypes.number
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,34 +18,43 @@

import {DISCUSSION_QUERY} from '../../../graphql/Queries'
import {Discussion} from '../../../graphql/Discussion'
import {DiscussionEntry} from '../../../graphql/DiscussionEntry'
import {DiscussionThreadContainer} from '../DiscussionThreadContainer/DiscussionThreadContainer'
import LoadingIndicator from '@canvas/loading-indicator'
import {PageInfo} from '../../../graphql/PageInfo'
import {PER_PAGE} from '../../utils/constants'
import PropTypes from 'prop-types'
import React, {useState} from 'react'
import React from 'react'
import {ThreadPagination} from '../../components/ThreadPagination/ThreadPagination'
import {useQuery} from 'react-apollo'
import {useLazyQuery} from 'react-apollo'
import {View} from '@instructure/ui-view'

export const DiscussionThreadsContainer = props => {
const [currentPage, setCurrentPage] = useState(0)
let threads = props.threads
let selectedPage = Math.ceil(atob(props.pageInfo.startCursor) / PER_PAGE)

const variables = {
discussionID: props.discussionTopicId,
perPage: PER_PAGE,
page: btoa(currentPage * PER_PAGE)
}

const {loading, data} = useQuery(DISCUSSION_QUERY, {
variables
})
const [discussionTopicQuery, {called, loading, data}] = useLazyQuery(DISCUSSION_QUERY)

if (loading) {
if (called && loading) {
return <LoadingIndicator />
}

const threads = data.legacyNode.rootDiscussionEntriesConnection.nodes
const totalPages = data.legacyNode.rootEntriesTotalPages
if (called && data) {
selectedPage = Math.ceil(
atob(data.legacyNode.rootDiscussionEntriesConnection.pageInfo.startCursor) / PER_PAGE
)
threads = data.legacyNode.rootDiscussionEntriesConnection.nodes
}

const setPage = pageNumber => {
discussionTopicQuery({
variables: {
discussionID: props.discussionTopicId,
perPage: PER_PAGE,
page: btoa(pageNumber * PER_PAGE)
}
})
}

return (
<View as="div" margin="medium none none none">
Expand All @@ -55,14 +64,15 @@ export const DiscussionThreadsContainer = props => {
key={`discussion-thread-${thread.id}`}
assignment={props.discussionTopic?.assignment}
discussionEntry={thread}
discussionTopicGraphQLId={props.discussionTopic.id}
/>
)
})}
{props.totalPages > 1 && (
<ThreadPagination
setPage={setCurrentPage}
selectedPage={currentPage + 1}
totalPages={totalPages}
setPage={setPage}
selectedPage={selectedPage}
totalPages={props.totalPages}
/>
)}
</View>
Expand All @@ -72,6 +82,8 @@ export const DiscussionThreadsContainer = props => {
DiscussionThreadsContainer.propTypes = {
discussionTopic: Discussion.shape,
discussionTopicId: PropTypes.string.isRequired,
threads: PropTypes.arrayOf(DiscussionEntry.shape),
pageInfo: PageInfo.shape.isRequired,
totalPages: PropTypes.number
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ describe('DiscussionThreadContainer', () => {
const defaultProps = () => {
return {
discussionTopicId: '1',
discussionTopic: {
id: 'RGlzY3Vzc2lvbi0zMw==',
_id: '1'
},
threads: [
{
_id: '49',
Expand Down
74 changes: 74 additions & 0 deletions ui/features/discussion_topics_post/react/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import {Discussion} from '../../graphql/Discussion'
import {DiscussionEntry} from '../../graphql/DiscussionEntry'
import {DISCUSSION_SUBENTRIES_QUERY} from '../../graphql/Queries'
import {PER_PAGE} from './constants'

export const isGraded = (assignment = null) => {
return assignment !== null && (assignment?.dueAt || assignment?.pointsPossible)
}
Expand All @@ -33,3 +38,72 @@ export const getSpeedGraderUrl = (courseId, assignmentId, authorId = null) => {
export const getEditUrl = (courseId, discussionTopicId) => {
return `/courses/${courseId}/discussion_topics/${discussionTopicId}/edit`
}

export const addReplyToDiscussion = (cache, discussionTopicGraphQLId) => {
const options = {
id: discussionTopicGraphQLId,
fragment: Discussion.fragment,
fragmentName: 'Discussion'
}
const data = JSON.parse(JSON.stringify(cache.readFragment(options)))

if (data) {
data.entryCounts.repliesCount += 1

cache.writeFragment({
...options,
data
})
}
}

export const addReplyToDiscussionEntry = (cache, discussionEntryGraphQLId, newDiscussionEntry) => {
const options = {
id: discussionEntryGraphQLId,
fragment: DiscussionEntry.fragment,
fragmentName: 'DiscussionEntry'
}
const data = JSON.parse(JSON.stringify(cache.readFragment(options)))

if (data) {
// On nested-replies we don't have rootEntryParticipantCounts or a last reply.
if (data.rootEntryParticipantCounts) {
data.rootEntryParticipantCounts.unreadCount += 1
data.rootEntryParticipantCounts.repliesCount += 1
data.lastReply = {
createdAt: newDiscussionEntry.createdAt,
__typename: 'DiscussionEntry'
}
}

data.subentriesCount += 1

cache.writeFragment({
...options,
data
})
}
}
export const addReplyToSubentries = (cache, discussionEntryId, newDiscussionEntry) => {
try {
const options = {
query: DISCUSSION_SUBENTRIES_QUERY,
variables: {
discussionEntryID: discussionEntryId,
perPage: PER_PAGE
}
}
const currentSubentries = JSON.parse(JSON.stringify(cache.readQuery(options)))

if (currentSubentries) {
const subentriesLegacyNode = currentSubentries.legacyNode
subentriesLegacyNode.subentriesCount += 1

// TODO: Handle sorting.
subentriesLegacyNode.discussionSubentriesConnection.nodes.push(newDiscussionEntry)

cache.writeQuery({...options, data: currentSubentries})
}
// eslint-disable-next-line no-empty
} catch (e) {}
}

0 comments on commit 51e5ca0

Please sign in to comment.