Migrating cardDialog to functional component and creating the cardListener hook
This commit is contained in:
parent
361c37c1e0
commit
239fc689d4
2 changed files with 126 additions and 122 deletions
|
@ -1,17 +1,17 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React from 'react'
|
||||
import React, {useState} from 'react'
|
||||
import {FormattedMessage, injectIntl, IntlShape} from 'react-intl'
|
||||
|
||||
import mutator from '../mutator'
|
||||
import octoClient from '../octoClient'
|
||||
import {OctoListener} from '../octoListener'
|
||||
import {Utils} from '../utils'
|
||||
import {BoardTree} from '../viewModel/boardTree'
|
||||
import {CardTree, MutableCardTree} from '../viewModel/cardTree'
|
||||
import DeleteIcon from '../widgets/icons/delete'
|
||||
import Menu from '../widgets/menu'
|
||||
|
||||
import useCardListener from '../hooks/cardListener'
|
||||
|
||||
import CardDetail from './cardDetail/cardDetail'
|
||||
import Dialog from './dialog'
|
||||
|
||||
|
@ -24,139 +24,99 @@ type Props = {
|
|||
readonly: boolean
|
||||
}
|
||||
|
||||
type State = {
|
||||
cardTree?: CardTree,
|
||||
syncComplete: boolean
|
||||
}
|
||||
const CardDialog = (props: Props) => {
|
||||
const [syncComplete, setSyncComplete] = useState(false)
|
||||
const [cardTree, setCardTree] = useState<CardTree>()
|
||||
useCardListener(
|
||||
props.cardId,
|
||||
async (blocks) => {
|
||||
Utils.log(`cardListener.onChanged: ${blocks.length}`)
|
||||
const newCardTree = cardTree ? MutableCardTree.incrementalUpdate(cardTree, blocks) : await MutableCardTree.sync(props.cardId)
|
||||
setCardTree(newCardTree)
|
||||
setSyncComplete(true)
|
||||
},
|
||||
async () => {
|
||||
Utils.log('cardListener.onReconnect')
|
||||
const newCardTree = await MutableCardTree.sync(props.cardId)
|
||||
setCardTree(newCardTree)
|
||||
setSyncComplete(true)
|
||||
},
|
||||
)
|
||||
|
||||
class CardDialog extends React.Component<Props, State> {
|
||||
state: State = {syncComplete: false}
|
||||
|
||||
private cardListener?: OctoListener
|
||||
|
||||
shouldComponentUpdate(): boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
this.createCardTreeAndSync()
|
||||
}
|
||||
|
||||
private async createCardTreeAndSync() {
|
||||
const cardTree = await MutableCardTree.sync(this.props.cardId)
|
||||
this.createListener()
|
||||
this.setState({cardTree, syncComplete: true})
|
||||
Utils.log(`cardDialog.createCardTreeAndSync: ${cardTree?.card.id}`)
|
||||
}
|
||||
|
||||
private createListener() {
|
||||
this.deleteListener()
|
||||
|
||||
this.cardListener = new OctoListener()
|
||||
this.cardListener.open(
|
||||
octoClient.workspaceId,
|
||||
[this.props.cardId],
|
||||
async (blocks) => {
|
||||
Utils.log(`cardListener.onChanged: ${blocks.length}`)
|
||||
const newCardTree = this.state.cardTree ? MutableCardTree.incrementalUpdate(this.state.cardTree, blocks) : await MutableCardTree.sync(this.props.cardId)
|
||||
this.setState({cardTree: newCardTree, syncComplete: true})
|
||||
},
|
||||
async () => {
|
||||
Utils.log('cardListener.onReconnect')
|
||||
const newCardTree = await MutableCardTree.sync(this.props.cardId)
|
||||
this.setState({cardTree: newCardTree, syncComplete: true})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private deleteListener() {
|
||||
this.cardListener?.close()
|
||||
this.cardListener = undefined
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
this.deleteListener()
|
||||
}
|
||||
|
||||
render(): JSX.Element {
|
||||
const {cardTree} = this.state
|
||||
|
||||
const menu = (
|
||||
<Menu position='left'>
|
||||
<Menu.Text
|
||||
id='delete'
|
||||
icon={<DeleteIcon/>}
|
||||
name='Delete'
|
||||
onClick={async () => {
|
||||
const card = this.state.cardTree?.card
|
||||
if (!card) {
|
||||
Utils.assertFailure()
|
||||
return
|
||||
}
|
||||
await mutator.deleteBlock(card, 'delete card')
|
||||
this.props.onClose()
|
||||
}}
|
||||
/>
|
||||
{(cardTree && !cardTree.card.isTemplate) &&
|
||||
<Menu.Text
|
||||
id='makeTemplate'
|
||||
name='New template from card'
|
||||
onClick={this.makeTemplateClicked}
|
||||
/>
|
||||
}
|
||||
</Menu>
|
||||
)
|
||||
return (
|
||||
<Dialog
|
||||
onClose={this.props.onClose}
|
||||
toolsMenu={!this.props.readonly && menu}
|
||||
>
|
||||
{(cardTree?.card.isTemplate) &&
|
||||
<div className='banner'>
|
||||
<FormattedMessage
|
||||
id='CardDialog.editing-template'
|
||||
defaultMessage="You're editing a template"
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
{this.state.cardTree &&
|
||||
<CardDetail
|
||||
boardTree={this.props.boardTree}
|
||||
cardTree={this.state.cardTree}
|
||||
readonly={this.props.readonly}
|
||||
/>
|
||||
}
|
||||
{(!this.state.cardTree && this.state.syncComplete) &&
|
||||
<div className='banner error'>
|
||||
<FormattedMessage
|
||||
id='CardDialog.nocard'
|
||||
defaultMessage="This card doesn't exist or is inaccessible"
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
private makeTemplateClicked = async () => {
|
||||
const {cardTree} = this.state
|
||||
const makeTemplateClicked = async () => {
|
||||
if (!cardTree) {
|
||||
Utils.assertFailure('this.state.cardTree')
|
||||
Utils.assertFailure('cardTree')
|
||||
return
|
||||
}
|
||||
|
||||
await mutator.duplicateCard(
|
||||
cardTree.card.id,
|
||||
this.props.intl.formatMessage({id: 'Mutator.new-template-from-card', defaultMessage: 'new template from card'}),
|
||||
props.intl.formatMessage({id: 'Mutator.new-template-from-card', defaultMessage: 'new template from card'}),
|
||||
true,
|
||||
async (newCardId) => {
|
||||
this.props.showCard(newCardId)
|
||||
props.showCard(newCardId)
|
||||
},
|
||||
async () => {
|
||||
this.props.showCard(undefined)
|
||||
props.showCard(undefined)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
const menu = (
|
||||
<Menu position='left'>
|
||||
<Menu.Text
|
||||
id='delete'
|
||||
icon={<DeleteIcon/>}
|
||||
name='Delete'
|
||||
onClick={async () => {
|
||||
const card = cardTree?.card
|
||||
if (!card) {
|
||||
Utils.assertFailure()
|
||||
return
|
||||
}
|
||||
await mutator.deleteBlock(card, 'delete card')
|
||||
props.onClose()
|
||||
}}
|
||||
/>
|
||||
{(cardTree && !cardTree.card.isTemplate) &&
|
||||
<Menu.Text
|
||||
id='makeTemplate'
|
||||
name='New template from card'
|
||||
onClick={makeTemplateClicked}
|
||||
/>
|
||||
}
|
||||
</Menu>
|
||||
)
|
||||
return (
|
||||
<Dialog
|
||||
onClose={props.onClose}
|
||||
toolsMenu={!props.readonly && menu}
|
||||
>
|
||||
{(cardTree?.card.isTemplate) &&
|
||||
<div className='banner'>
|
||||
<FormattedMessage
|
||||
id='CardDialog.editing-template'
|
||||
defaultMessage="You're editing a template"
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
{cardTree &&
|
||||
<CardDetail
|
||||
boardTree={props.boardTree}
|
||||
cardTree={cardTree}
|
||||
readonly={props.readonly}
|
||||
/>
|
||||
}
|
||||
{(!cardTree && syncComplete) &&
|
||||
<div className='banner error'>
|
||||
<FormattedMessage
|
||||
id='CardDialog.nocard'
|
||||
defaultMessage="This card doesn't exist or is inaccessible"
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
export default injectIntl(CardDialog)
|
||||
|
|
44
webapp/src/hooks/cardListener.tsx
Normal file
44
webapp/src/hooks/cardListener.tsx
Normal file
|
@ -0,0 +1,44 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React, {useEffect} from 'react'
|
||||
|
||||
import octoClient from '../octoClient'
|
||||
import {OctoListener} from '../octoListener'
|
||||
import {MutableCardTree} from '../viewModel/cardTree'
|
||||
import {Utils} from '../utils'
|
||||
import {IBlock} from '../blocks/block'
|
||||
|
||||
export default function useCardListener(cardId:string, onChange: (blocks: IBlock[]) => void, onReconnect: () => void): void {
|
||||
let cardListener: OctoListener | null = null
|
||||
|
||||
const deleteListener = () => {
|
||||
cardListener?.close()
|
||||
cardListener = null
|
||||
}
|
||||
|
||||
const createListener = () => {
|
||||
deleteListener()
|
||||
|
||||
cardListener = new OctoListener()
|
||||
cardListener.open(
|
||||
octoClient.workspaceId,
|
||||
[cardId],
|
||||
onChange,
|
||||
onReconnect,
|
||||
)
|
||||
}
|
||||
|
||||
const createCardTreeAndSync = async () => {
|
||||
onReconnect()
|
||||
createListener()
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
Utils.log(`useCardListener.connect: ${cardId}`)
|
||||
createCardTreeAndSync()
|
||||
return () => {
|
||||
Utils.log(`useCardListener.disconnect: ${cardId}`)
|
||||
deleteListener()
|
||||
}
|
||||
}, [cardId])
|
||||
}
|
Loading…
Reference in a new issue