1
0
mirror of https://github.com/mattermost/focalboard.git synced 2024-11-27 08:31:20 +02:00

Put shown cardId in url, to allow direct linking to cards

This commit is contained in:
Chen-I Lim 2020-12-08 10:57:36 -08:00
parent 8f441747dd
commit cd9e34499b
3 changed files with 66 additions and 21 deletions

View File

@ -72,6 +72,7 @@ class BoardComponent extends React.Component<Props, State> {
}
componentDidMount(): void {
this.showCardInUrl()
document.addEventListener('keydown', this.keydownHandler)
}
@ -99,6 +100,14 @@ class BoardComponent extends React.Component<Props, State> {
}
}
private showCardInUrl() {
const queryString = new URLSearchParams(window.location.search)
const cardId = queryString.get('c') || undefined
if (cardId !== this.state.shownCardId) {
this.setState({shownCardId: cardId})
}
}
render(): JSX.Element {
const {boardTree, showView} = this.props
const {groupByProperty} = boardTree
@ -129,8 +138,8 @@ class BoardComponent extends React.Component<Props, State> {
key={this.state.shownCardId}
boardTree={boardTree}
cardId={this.state.shownCardId}
onClose={() => this.setState({shownCardId: undefined})}
showCard={(cardId) => this.setState({shownCardId: cardId})}
onClose={() => this.showCard(undefined)}
showCard={(cardId) => this.showCard(cardId)}
/>
</RootPortal>}
@ -482,10 +491,10 @@ class BoardComponent extends React.Component<Props, State> {
this.props.intl.formatMessage({id: 'Mutator.new-card-from-template', defaultMessage: 'new card from template'}),
false,
async (newCardId) => {
this.setState({shownCardId: newCardId})
this.showCard(newCardId)
},
async () => {
this.setState({shownCardId: undefined})
this.showCard(undefined)
},
)
}
@ -514,10 +523,10 @@ class BoardComponent extends React.Component<Props, State> {
card,
'add card',
async () => {
this.setState({shownCardId: card.id})
this.showCard(card.id)
},
async () => {
this.setState({shownCardId: undefined})
this.showCard(undefined)
},
)
}
@ -533,15 +542,15 @@ class BoardComponent extends React.Component<Props, State> {
cardTemplate,
'add card template',
async () => {
this.setState({shownCardId: cardTemplate.id})
this.showCard(cardTemplate.id)
}, async () => {
this.setState({shownCardId: undefined})
this.showCard(undefined)
},
)
}
private editCardTemplate = (cardTemplateId: string) => {
this.setState({shownCardId: cardTemplateId})
this.showCard(cardTemplateId)
}
private async propertyNameChanged(option: IPropertyOption, text: string): Promise<void> {
@ -577,12 +586,17 @@ class BoardComponent extends React.Component<Props, State> {
this.setState({selectedCardIds})
}
} else {
this.setState({selectedCardIds: [], shownCardId: card.id})
this.showCard(card.id)
}
e.stopPropagation()
}
private showCard = (cardId?: string) => {
Utils.replaceUrlQueryParam('c', cardId)
this.setState({selectedCardIds: [], shownCardId: cardId})
}
private addGroupClicked = async () => {
Utils.log('onAddGroupClicked')

View File

@ -45,6 +45,18 @@ class TableComponent extends React.Component<Props, State> {
return true
}
componentDidMount(): void {
this.showCardInUrl()
}
private showCardInUrl() {
const queryString = new URLSearchParams(window.location.search)
const cardId = queryString.get('c') || undefined
if (cardId !== this.state.shownCardId) {
this.setState({shownCardId: cardId})
}
}
render(): JSX.Element {
const {boardTree, showView} = this.props
const {board, cards, activeView} = boardTree
@ -66,8 +78,8 @@ class TableComponent extends React.Component<Props, State> {
key={this.state.shownCardId}
boardTree={boardTree}
cardId={this.state.shownCardId}
onClose={() => this.setState({shownCardId: undefined})}
showCard={(cardId) => this.setState({shownCardId: cardId})}
onClose={() => this.showCard(undefined)}
showCard={(cardId) => this.showCard(cardId)}
/>
</RootPortal>}
<div className='octo-frame'>
@ -263,9 +275,7 @@ class TableComponent extends React.Component<Props, State> {
this.addCard(false)
}
}}
showCard={(cardId) => {
this.setState({shownCardId: cardId})
}}
showCard={this.showCard}
/>)
this.cardIdToRowMap.set(card.id, tableRowRef)
@ -295,6 +305,11 @@ class TableComponent extends React.Component<Props, State> {
)
}
private showCard = (cardId?: string) => {
Utils.replaceUrlQueryParam('c', cardId)
this.setState({shownCardId: cardId})
}
private columnWidth(templateId: string): number {
return Math.max(Constants.minColumnWidth, this.props.boardTree.activeView.columnWidths[templateId] || 0)
}
@ -309,10 +324,10 @@ class TableComponent extends React.Component<Props, State> {
this.props.intl.formatMessage({id: 'Mutator.new-card-from-template', defaultMessage: 'new card from template'}),
false,
async (newCardId) => {
this.setState({shownCardId: newCardId})
this.showCard(newCardId)
},
async () => {
this.setState({shownCardId: undefined})
this.showCard(undefined)
},
)
}
@ -332,7 +347,7 @@ class TableComponent extends React.Component<Props, State> {
'add card',
async () => {
if (show) {
this.setState({shownCardId: card.id})
this.showCard(card.id)
} else {
// Focus on this card's title inline on next render
this.cardIdToFocusOnRender = card.id
@ -352,15 +367,15 @@ class TableComponent extends React.Component<Props, State> {
cardTemplate,
'add card template',
async () => {
this.setState({shownCardId: cardTemplate.id})
this.showCard(cardTemplate.id)
}, async () => {
this.setState({shownCardId: undefined})
this.showCard(undefined)
},
)
}
private editCardTemplate = (cardTemplateId: string) => {
this.setState({shownCardId: cardTemplateId})
this.showCard(cardTemplateId)
}
private async onDropToColumn(template: IPropertyTemplate) {

View File

@ -131,6 +131,22 @@ class Utils {
document.getElementsByTagName('head')[0].appendChild(link)
}
// URL
static replaceUrlQueryParam(paramName: string, value?: string): void {
const queryString = new URLSearchParams(window.location.search)
const currentValue = queryString.get(paramName) || ''
if (currentValue !== value) {
const newUrl = new URL(window.location.toString())
if (value) {
newUrl.searchParams.set(paramName, value)
} else {
newUrl.searchParams.delete(paramName)
}
window.history.pushState({}, document.title, newUrl.toString())
}
}
// File names
static sanitizeFilename(filename: string): string {