1
0
mirror of https://github.com/mattermost/focalboard.git synced 2024-12-24 13:43:12 +02:00

Show views in sidebar

This commit is contained in:
Chen-I Lim 2020-10-21 13:20:00 -07:00
parent 0b07f454bb
commit 277505e6f9
7 changed files with 90 additions and 56 deletions

View File

@ -8,10 +8,12 @@ import mutator from '../mutator'
import Menu from '../widgets/menu'
import MenuWrapper from '../widgets/menuWrapper'
import { WorkspaceTree } from '../viewModel/workspaceTree'
import { BoardView } from '../blocks/boardView'
type Props = {
showBoard: (id: string) => void
showView: (id: string, boardId?: string) => void
workspaceTree: WorkspaceTree,
boardTree?: BoardTree
}
@ -23,47 +25,51 @@ class Sidebar extends React.Component<Props> {
return <div/>
}
const {boards} = workspaceTree
const {boards, views} = workspaceTree
return (
<div className='octo-sidebar'>
{
boards.map((board) => {
const displayTitle = board.title || '(Untitled Board)'
const boardViews = views.filter(view => view.parentId === board.id)
return (
<div
key={board.id}
className='octo-sidebar-item octo-hover-container'
>
<div
className='octo-sidebar-title'
onClick={() => {
this.boardClicked(board)
}}
>{board.icon ? `${board.icon} ${displayTitle}` : displayTitle}</div>
<div className='octo-spacer'/>
<MenuWrapper>
<div className='octo-button square octo-hover-item'><div className='imageOptions'/></div>
<Menu>
<Menu.Text
id='delete'
name='Delete board'
onClick={async () => {
const nextBoardId = boards.length > 1 ? boards.find((o) => o.id !== board.id).id : undefined
mutator.deleteBlock(
board,
'delete block',
async () => {
nextBoardId && this.props.showBoard(nextBoardId!)
},
async () => {
this.props.showBoard(board.id)
},
)
}}
/>
</Menu>
</MenuWrapper>
<div key={board.id}>
<div className='octo-sidebar-item octo-hover-container'>
<div className='octo-sidebar-title' onClick={() => { this.boardClicked(board) }}>
{board.icon ? `${board.icon} ${displayTitle}` : displayTitle}
</div>
<div className='octo-spacer'/>
<MenuWrapper>
<div className='octo-button square octo-hover-item'><div className='imageOptions'/></div>
<Menu>
<Menu.Text
id='delete'
name='Delete board'
onClick={async () => {
const nextBoardId = boards.length > 1 ? boards.find((o) => o.id !== board.id).id : undefined
mutator.deleteBlock(
board,
'delete block',
async () => {
nextBoardId && this.props.showBoard(nextBoardId!)
},
async () => {
this.props.showBoard(board.id)
},
)
}}
/>
</Menu>
</MenuWrapper>
</div>
{boardViews.map(view => {
return <div key={view.id} className='octo-sidebar-item subitem octo-hover-container'>
<div className='octo-sidebar-title' onClick={() => { this.viewClicked(board, view) }}>
{view.title || '(Untitled View)'}
</div>
</div>
})}
</div>
)
})
@ -105,6 +111,10 @@ class Sidebar extends React.Component<Props> {
this.props.showBoard(board.id)
}
private viewClicked(board: Board, view: BoardView) {
this.props.showView(view.id, board.id)
}
async addBoardClicked() {
const {boardTree, showBoard} = this.props
@ -127,4 +137,3 @@ class Sidebar extends React.Component<Props> {
}
export { Sidebar }

View File

@ -14,20 +14,21 @@ type Props = {
workspaceTree: WorkspaceTree
boardTree?: BoardTree
showBoard: (id: string) => void
showView: (id: string) => void
showView: (id: string, boardId?: string) => void
showFilter: (el: HTMLElement) => void
setSearchText: (text: string) => void
}
class WorkspaceComponent extends React.Component<Props> {
render() {
const {boardTree, workspaceTree, showBoard} = this.props
const {boardTree, workspaceTree, showBoard, showView} = this.props
Utils.assert(workspaceTree)
const element =
(<div className='octo-workspace'>
<Sidebar
showBoard={showBoard}
showView={showView}
workspaceTree={workspaceTree}
boardTree={boardTree}
/>

View File

@ -47,18 +47,22 @@ class OctoClient {
})
}
async getBlocks(parentId?: string, type?: string): Promise<IBlock[]> {
async getBlocksWithParent(parentId: string, type?: string): Promise<IBlock[]> {
let path: string
if (parentId && type) {
if (type) {
path = `/api/v1/blocks?parent_id=${encodeURIComponent(parentId)}&type=${encodeURIComponent(type)}`
} else if (parentId) {
path = `/api/v1/blocks?parent_id=${encodeURIComponent(parentId)}`
} else if (type) {
path = `/api/v1/blocks?type=${encodeURIComponent(type)}`
} else {
path = '/api/v1/blocks'
path = `/api/v1/blocks?parent_id=${encodeURIComponent(parentId)}`
}
return this.getBlocksWithPath(path)
}
async getBlocksWithType(type: string): Promise<IBlock[]> {
const path = `/api/v1/blocks?type=${encodeURIComponent(type)}`
return this.getBlocksWithPath(path)
}
private async getBlocksWithPath(path: string): Promise<IBlock[]> {
const response = await fetch(this.serverUrl + path)
const blocks = (await response.json() || []) as IMutableBlock[]
this.fixBlocks(blocks)

View File

@ -141,8 +141,8 @@ export default class BoardPage extends React.Component<Props, State> {
<WorkspaceComponent
workspaceTree={workspaceTree}
boardTree={this.state.boardTree}
showView={(id) => {
this.showView(id)
showView={(id, boardId) => {
this.showView(id, boardId)
}}
showBoard={(id) => {
this.showBoard(id)
@ -218,10 +218,15 @@ export default class BoardPage extends React.Component<Props, State> {
this.attachToBoard(boardId)
}
showView(viewId: string) {
this.state.boardTree.setActiveView(viewId)
this.setState({...this.state, viewId})
const newUrl = window.location.protocol + '//' + window.location.host + window.location.pathname + `?id=${encodeURIComponent(this.state.boardId)}&v=${encodeURIComponent(viewId)}`
showView(viewId: string, boardId: string = this.state.boardId) {
if (this.state.boardId !== boardId) {
this.attachToBoard(boardId, viewId)
} else {
this.state.boardTree.setActiveView(viewId)
this.setState({...this.state, viewId})
}
const newUrl = window.location.protocol + '//' + window.location.host + window.location.pathname + `?id=${encodeURIComponent(boardId)}&v=${encodeURIComponent(viewId)}`
window.history.pushState({path: newUrl}, '', newUrl)
}

View File

@ -28,7 +28,7 @@ export default class HomePage extends React.Component<Props, State> {
}
loadBoards = async () => {
const boards = await octoClient.getBlocks(null, 'board')
const boards = await octoClient.getBlocksWithType('board')
this.setState({boards})
}

View File

@ -4,21 +4,29 @@ import {Board} from '../blocks/board'
import octoClient from '../octoClient'
import {IBlock} from '../blocks/block'
import {OctoUtils} from '../octoUtils'
import {BoardView} from '../blocks/boardView'
interface WorkspaceTree {
readonly boards: readonly Board[]
readonly views: readonly BoardView[]
}
class MutableWorkspaceTree {
boards: Board[] = []
views: BoardView[] = []
async sync() {
const blocks = await octoClient.getBlocks(undefined, "board")
this.rebuild(OctoUtils.hydrateBlocks(blocks))
const boards = await octoClient.getBlocksWithType("board")
const views = await octoClient.getBlocksWithType("view")
this.rebuild(
OctoUtils.hydrateBlocks(boards),
OctoUtils.hydrateBlocks(views)
)
}
private rebuild(blocks: IBlock[]) {
this.boards = blocks.filter(block => block.type === "board") as Board[]
private rebuild(boards: IBlock[], views: IBlock[]) {
this.boards = boards.filter(block => block.type === "board") as Board[]
this.views = views.filter(block => block.type === "view") as BoardView[]
}
}

View File

@ -92,12 +92,13 @@ hr {
}
.octo-sidebar {
flex: 0 0 230px;
display: flex;
flex-direction: column;
min-height: 100%;
background-color: rgb(247, 246, 243);
min-width: 230px;
padding: 20px 0;
}
@ -109,6 +110,12 @@ hr {
padding: 3px 20px;
}
.octo-sidebar-item.subitem {
color: rgba(25, 23, 18, 0.6);
font-weight: 400;
margin-left: 20px;
}
.octo-sidebar-title {
cursor: pointer;
}