Use card id in the url (#1050)
* Use card id in the url * Fix linter error Co-authored-by: Hossein <hahmadia@users.noreply.github.com>
This commit is contained in:
parent
55e506bd0e
commit
171164ec6d
4 changed files with 53 additions and 38 deletions
|
@ -78,7 +78,7 @@ const App = React.memo((): JSX.Element => {
|
||||||
<Route path='/shared/:boardId?/:viewId?'>
|
<Route path='/shared/:boardId?/:viewId?'>
|
||||||
<BoardPage readonly={true}/>
|
<BoardPage readonly={true}/>
|
||||||
</Route>
|
</Route>
|
||||||
<Route path='/board/:boardId?/:viewId?'>
|
<Route path='/board/:boardId?/:viewId?/:cardId?'>
|
||||||
{loggedIn === false && <Redirect to='/login'/>}
|
{loggedIn === false && <Redirect to='/login'/>}
|
||||||
{loggedIn === true && <BoardPage/>}
|
{loggedIn === true && <BoardPage/>}
|
||||||
</Route>
|
</Route>
|
||||||
|
@ -86,7 +86,7 @@ const App = React.memo((): JSX.Element => {
|
||||||
<BoardPage readonly={true}/>
|
<BoardPage readonly={true}/>
|
||||||
</Route>
|
</Route>
|
||||||
<Route
|
<Route
|
||||||
path='/workspace/:workspaceId/:boardId?/:viewId?'
|
path='/workspace/:workspaceId/:boardId?/:viewId?/:cardId?'
|
||||||
render={({match}) => {
|
render={({match}) => {
|
||||||
if (loggedIn === false) {
|
if (loggedIn === false) {
|
||||||
let redirectUrl = '/' + Utils.buildURL(`/workspace/${match.params.workspaceId}/`)
|
let redirectUrl = '/' + Utils.buildURL(`/workspace/${match.params.workspaceId}/`)
|
||||||
|
@ -109,7 +109,7 @@ const App = React.memo((): JSX.Element => {
|
||||||
>
|
>
|
||||||
<DashboardPage/>
|
<DashboardPage/>
|
||||||
</Route>
|
</Route>
|
||||||
<Route path='/:boardId?/:viewId?'>
|
<Route path='/:boardId?/:viewId?/:cardId?'>
|
||||||
{loggedIn === false && <Redirect to='/login'/>}
|
{loggedIn === false && <Redirect to='/login'/>}
|
||||||
{loggedIn === true && <BoardPage/>}
|
{loggedIn === true && <BoardPage/>}
|
||||||
</Route>
|
</Route>
|
||||||
|
|
|
@ -37,10 +37,11 @@ type Props = {
|
||||||
readonly: boolean
|
readonly: boolean
|
||||||
addCard: (card: Card) => void
|
addCard: (card: Card) => void
|
||||||
addTemplate: (template: Card) => void
|
addTemplate: (template: Card) => void
|
||||||
|
shownCardId?: string
|
||||||
|
showCard: (cardId?: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
shownCardId?: string
|
|
||||||
selectedCardIds: string[]
|
selectedCardIds: string[]
|
||||||
cardIdToFocusOnRender: string
|
cardIdToFocusOnRender: string
|
||||||
}
|
}
|
||||||
|
@ -77,10 +78,6 @@ class CenterPanel extends React.Component<Props, State> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount(): void {
|
|
||||||
this.showCardInUrl()
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(props: Props) {
|
constructor(props: Props) {
|
||||||
super(props)
|
super(props)
|
||||||
this.state = {
|
this.state = {
|
||||||
|
@ -93,14 +90,6 @@ class CenterPanel extends React.Component<Props, State> {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
render(): JSX.Element {
|
||||||
const {groupByProperty, activeView, board, views, cards} = this.props
|
const {groupByProperty, activeView, board, views, cards} = this.props
|
||||||
const {visible: visibleGroups, hidden: hiddenGroups} = this.getVisibleAndHiddenGroups(cards, activeView.fields.visibleOptionIds, activeView.fields.hiddenOptionIds, groupByProperty)
|
const {visible: visibleGroups, hidden: hiddenGroups} = this.getVisibleAndHiddenGroups(cards, activeView.fields.visibleOptionIds, activeView.fields.hiddenOptionIds, groupByProperty)
|
||||||
|
@ -117,15 +106,15 @@ class CenterPanel extends React.Component<Props, State> {
|
||||||
keyName='ctrl+d,del,esc,backspace'
|
keyName='ctrl+d,del,esc,backspace'
|
||||||
onKeyDown={this.keydownHandler}
|
onKeyDown={this.keydownHandler}
|
||||||
/>
|
/>
|
||||||
{this.state.shownCardId &&
|
{this.props.shownCardId &&
|
||||||
<RootPortal>
|
<RootPortal>
|
||||||
<CardDialog
|
<CardDialog
|
||||||
board={board}
|
board={board}
|
||||||
activeView={activeView}
|
activeView={activeView}
|
||||||
views={views}
|
views={views}
|
||||||
cards={cards}
|
cards={cards}
|
||||||
key={this.state.shownCardId}
|
key={this.props.shownCardId}
|
||||||
cardId={this.state.shownCardId}
|
cardId={this.props.shownCardId}
|
||||||
onClose={() => this.showCard(undefined)}
|
onClose={() => this.showCard(undefined)}
|
||||||
showCard={(cardId) => this.showCard(cardId)}
|
showCard={(cardId) => this.showCard(cardId)}
|
||||||
readonly={this.props.readonly}
|
readonly={this.props.readonly}
|
||||||
|
@ -316,8 +305,8 @@ class CenterPanel extends React.Component<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private showCard = (cardId?: string) => {
|
private showCard = (cardId?: string) => {
|
||||||
Utils.replaceUrlQueryParam('c', cardId)
|
this.setState({selectedCardIds: []})
|
||||||
this.setState({selectedCardIds: [], shownCardId: cardId})
|
this.props.showCard(cardId)
|
||||||
}
|
}
|
||||||
|
|
||||||
private async deleteSelectedCards() {
|
private async deleteSelectedCards() {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
import React from 'react'
|
import React, {useCallback} from 'react'
|
||||||
import {useRouteMatch} from 'react-router-dom'
|
import {generatePath, useRouteMatch, useHistory} from 'react-router-dom'
|
||||||
import {FormattedMessage} from 'react-intl'
|
import {FormattedMessage} from 'react-intl'
|
||||||
|
|
||||||
import {getCurrentBoard} from '../store/boards'
|
import {getCurrentBoard} from '../store/boards'
|
||||||
|
@ -19,12 +19,19 @@ type Props = {
|
||||||
}
|
}
|
||||||
|
|
||||||
function CenterContent(props: Props) {
|
function CenterContent(props: Props) {
|
||||||
const match = useRouteMatch<{boardId: string, viewId: string}>()
|
const match = useRouteMatch<{boardId: string, viewId: string, cardId?: string}>()
|
||||||
const board = useAppSelector(getCurrentBoard)
|
const board = useAppSelector(getCurrentBoard)
|
||||||
const cards = useAppSelector(getCurrentViewCardsSortedFilteredAndGrouped)
|
const cards = useAppSelector(getCurrentViewCardsSortedFilteredAndGrouped)
|
||||||
const activeView = useAppSelector(getView(match.params.viewId))
|
const activeView = useAppSelector(getView(match.params.viewId))
|
||||||
const views = useAppSelector(getCurrentBoardViews)
|
const views = useAppSelector(getCurrentBoardViews)
|
||||||
const groupByProperty = useAppSelector(getCurrentViewGroupBy)
|
const groupByProperty = useAppSelector(getCurrentViewGroupBy)
|
||||||
|
const history = useHistory()
|
||||||
|
|
||||||
|
const showCard = useCallback((cardId?: string) => {
|
||||||
|
const params = {...match.params, cardId}
|
||||||
|
const newPath = generatePath(match.path, params)
|
||||||
|
history.push(newPath)
|
||||||
|
}, [match, history])
|
||||||
|
|
||||||
if (board && activeView) {
|
if (board && activeView) {
|
||||||
let property = groupByProperty
|
let property = groupByProperty
|
||||||
|
@ -36,6 +43,8 @@ function CenterContent(props: Props) {
|
||||||
readonly={props.readonly}
|
readonly={props.readonly}
|
||||||
board={board}
|
board={board}
|
||||||
cards={cards}
|
cards={cards}
|
||||||
|
shownCardId={match.params.cardId}
|
||||||
|
showCard={showCard}
|
||||||
activeView={activeView}
|
activeView={activeView}
|
||||||
groupByProperty={property}
|
groupByProperty={property}
|
||||||
views={views}
|
views={views}
|
||||||
|
|
|
@ -42,7 +42,7 @@ const BoardPage = (props: Props) => {
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
const history = useHistory()
|
const history = useHistory()
|
||||||
const match = useRouteMatch<{boardId: string, viewId: string, workspaceId?: string}>()
|
const match = useRouteMatch<{boardId: string, viewId: string, cardId?: string, workspaceId?: string}>()
|
||||||
const [websocketClosed, setWebsocketClosed] = useState(false)
|
const [websocketClosed, setWebsocketClosed] = useState(false)
|
||||||
|
|
||||||
// TODO: Make this less brittle. This only works because this is the root render function
|
// TODO: Make this less brittle. This only works because this is the root render function
|
||||||
|
@ -53,20 +53,37 @@ const BoardPage = (props: Props) => {
|
||||||
// Backward compatibility: This can be removed in the future, this is for
|
// Backward compatibility: This can be removed in the future, this is for
|
||||||
// transform the old query params into routes
|
// transform the old query params into routes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const queryString = new URLSearchParams(window.location.search)
|
|
||||||
const queryBoardId = queryString.get('id')
|
|
||||||
const queryViewId = queryString.get('v')
|
|
||||||
if (queryBoardId) {
|
|
||||||
const params = {...match.params, boardId: queryBoardId}
|
|
||||||
if (queryViewId) {
|
|
||||||
params.viewId = queryViewId
|
|
||||||
}
|
|
||||||
const newPath = generatePath(match.path, params)
|
|
||||||
history.replace(newPath)
|
|
||||||
}
|
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// Backward compatibility: This can be removed in the future, this is for
|
||||||
|
// transform the old query params into routes
|
||||||
|
const queryString = new URLSearchParams(history.location.search)
|
||||||
|
const queryBoardId = queryString.get('id')
|
||||||
|
const params = {...match.params}
|
||||||
|
let needsRedirect = false
|
||||||
|
if (queryBoardId) {
|
||||||
|
params.boardId = queryBoardId
|
||||||
|
needsRedirect = true
|
||||||
|
}
|
||||||
|
const queryViewId = queryString.get('v')
|
||||||
|
if (queryViewId) {
|
||||||
|
params.viewId = queryViewId
|
||||||
|
needsRedirect = true
|
||||||
|
}
|
||||||
|
const queryCardId = queryString.get('c')
|
||||||
|
if (queryCardId) {
|
||||||
|
params.cardId = queryCardId
|
||||||
|
needsRedirect = true
|
||||||
|
}
|
||||||
|
if (needsRedirect) {
|
||||||
|
const newPath = generatePath(match.path, params)
|
||||||
|
history.replace(newPath)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backward compatibility end
|
||||||
|
|
||||||
const boardId = match.params.boardId
|
const boardId = match.params.boardId
|
||||||
const viewId = match.params.viewId
|
const viewId = match.params.viewId
|
||||||
|
|
||||||
|
@ -96,7 +113,7 @@ const BoardPage = (props: Props) => {
|
||||||
UserSettings.lastViewId = viewId || ''
|
UserSettings.lastViewId = viewId || ''
|
||||||
dispatch(setCurrentBoard(boardId || ''))
|
dispatch(setCurrentBoard(boardId || ''))
|
||||||
dispatch(setCurrentView(viewId || ''))
|
dispatch(setCurrentView(viewId || ''))
|
||||||
}, [match.params.boardId, match.params.viewId, history, boardViews])
|
}, [match.params.boardId, match.params.viewId, boardViews])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
Utils.setFavicon(board?.fields.icon)
|
Utils.setFavicon(board?.fields.icon)
|
||||||
|
@ -119,7 +136,7 @@ const BoardPage = (props: Props) => {
|
||||||
let token = localStorage.getItem('focalboardSessionId') || ''
|
let token = localStorage.getItem('focalboardSessionId') || ''
|
||||||
if (props.readonly) {
|
if (props.readonly) {
|
||||||
loadAction = initialReadOnlyLoad
|
loadAction = initialReadOnlyLoad
|
||||||
const queryString = new URLSearchParams(window.location.search)
|
const queryString = new URLSearchParams(history.location.search)
|
||||||
token = token || queryString.get('r') || ''
|
token = token || queryString.get('r') || ''
|
||||||
}
|
}
|
||||||
dispatch(loadAction(match.params.boardId))
|
dispatch(loadAction(match.params.boardId))
|
||||||
|
|
Loading…
Reference in a new issue