diff --git a/src/client/app.tsx b/src/client/app.tsx index dc5c79d77..d7bdbc5b0 100644 --- a/src/client/app.tsx +++ b/src/client/app.tsx @@ -1,40 +1,37 @@ -import React from "react"; +import React from "react" import { - BrowserRouter as Router, - Switch, - Route, - Link -} from "react-router-dom"; + BrowserRouter as Router, + Switch, + Route, + Link +} from "react-router-dom" -import LoginPage from './pages/loginPage'; -import BoardPage from './pages/boardPage'; +import LoginPage from './pages/loginPage' +import BoardPage from './pages/boardPage' export default function App() { - return ( - -
- + return ( + +
+
+ OCTO +
-
- - - - - - - - -
+
+ + + + + + + + +
-
-
- -
-
-
-
- ); +
+
+
+
+ ) } diff --git a/src/client/components/sidebar.tsx b/src/client/components/sidebar.tsx index d4b64d647..adace5a11 100644 --- a/src/client/components/sidebar.tsx +++ b/src/client/components/sidebar.tsx @@ -9,7 +9,7 @@ import { WorkspaceTree } from "../workspaceTree" type Props = { mutator: Mutator - showBoard: (id: string) => void + showBoard: (id: string) => void workspaceTree: WorkspaceTree, boardTree?: BoardTree } @@ -18,6 +18,10 @@ class Sidebar extends React.Component { render() { const { workspaceTree } = this.props + if (!workspaceTree) { + return
+ } + const { boards } = workspaceTree return ( diff --git a/src/client/components/workspaceComponent.tsx b/src/client/components/workspaceComponent.tsx index 9d19e426b..eed77b353 100644 --- a/src/client/components/workspaceComponent.tsx +++ b/src/client/components/workspaceComponent.tsx @@ -12,17 +12,18 @@ type Props = { mutator: Mutator, workspaceTree: WorkspaceTree boardTree?: BoardTree - showBoard: (id: string) => void - showView: (id: string) => void - showCard: (card: IBlock) => void - showFilter: (el: HTMLElement) => void - setSearchText: (text: string) => void + showBoard: (id: string) => void + showView: (id: string) => void + showCard: (card: IBlock) => void + showFilter: (el: HTMLElement) => void + setSearchText: (text: string) => void } class WorkspaceComponent extends React.Component { render() { - const { mutator, boardTree, workspaceTree, showBoard} = this.props + const { mutator, boardTree, workspaceTree, showBoard } = this.props + Utils.assert(workspaceTree) const element =
@@ -42,11 +43,11 @@ class WorkspaceComponent extends React.Component { switch (activeView?.viewType) { case "board": { - return + return } case "table": { - return + return } default: { diff --git a/src/client/pages/boardPage.tsx b/src/client/pages/boardPage.tsx index 20ece3b9f..ad4ee5f03 100644 --- a/src/client/pages/boardPage.tsx +++ b/src/client/pages/boardPage.tsx @@ -5,13 +5,12 @@ import { BoardView } from "../boardView" import { CardTree } from "../cardTree" import { CardDialog } from "../components/cardDialog" import { FilterComponent } from "../components/filterComponent" -import { PageHeader } from "../components/pageHeader" import { WorkspaceComponent } from "../components/workspaceComponent" import { FlashMessage } from "../flashMessage" import { Mutator } from "../mutator" import { OctoClient } from "../octoClient" import { OctoListener } from "../octoListener" -import { IBlock, IPageController } from "../octoTypes" +import { IBlock } from "../octoTypes" import { UndoManager } from "../undomanager" import { Utils } from "../utils" import { WorkspaceTree } from "../workspaceTree" @@ -20,109 +19,111 @@ type Props = { } type State = { - boardId: string - viewId: string - workspaceTree: WorkspaceTree - boardTree?: BoardTree + boardId: string + viewId: string + workspaceTree: WorkspaceTree + boardTree?: BoardTree + shownCardTree?: CardTree } export default class BoardPage extends React.Component { - workspaceTree: WorkspaceTree - boardTree?: BoardTree view: BoardView updateTitleTimeout: number updatePropertyLabelTimeout: number - shownCardTree: CardTree - private filterAnchorElement?: HTMLElement private octo = new OctoClient() private boardListener = new OctoListener() private cardListener = new OctoListener() constructor(props: Props) { - super(props) + super(props) const queryString = new URLSearchParams(window.location.search) const boardId = queryString.get("id") const viewId = queryString.get("v") - this.state = { - boardId, - viewId, - workspaceTree: new WorkspaceTree(this.octo), - } - - console.log(`BoardPage. boardId: ${boardId}`) - } - - componentDidUpdate(prevProps: Props, prevState: State) { - const board = this.state.boardTree.board; - const prevBoard = prevState.boardTree.board; - - const activeView = this.state.boardTree.activeView; - const prevActiveView = prevState.boardTree.activeView; - - if (board.icon !== prevBoard.icon) { - Utils.setFavicon(board.icon) - } - if (board.title !== prevBoard.title || activeView.title !== prevActiveView.title) { - document.title = `OCTO - ${board.title} | ${activeView.title}` + this.state = { + boardId, + viewId, + workspaceTree: new WorkspaceTree(this.octo), } - } - undoRedoHandler = async (e: KeyboardEvent) => { - if (e.target !== document) { return } + Utils.log(`BoardPage. boardId: ${boardId}`) + } - if (e.keyCode === 90 && !e.shiftKey && (e.ctrlKey || e.metaKey) && !e.altKey) { // Cmd+Z - Utils.log(`Undo`) - const description = UndoManager.shared.undoDescription - await UndoManager.shared.undo() - if (description) { - FlashMessage.show(`Undo ${description}`) - } else { - FlashMessage.show(`Undo`) - } - } else if (e.keyCode === 90 && e.shiftKey && (e.ctrlKey || e.metaKey) && !e.altKey) { // Shift+Cmd+Z - Utils.log(`Redo`) - const description = UndoManager.shared.redoDescription - await UndoManager.shared.redo() - if (description) { - FlashMessage.show(`Redo ${description}`) - } else { - FlashMessage.show(`Redo`) - } - } - } + componentDidUpdate(prevProps: Props, prevState: State) { + Utils.log(`componentDidUpdate`) + const board = this.state.boardTree?.board + const prevBoard = prevState.boardTree?.board - componentDidMount() { + const activeView = this.state.boardTree?.activeView + const prevActiveView = prevState.boardTree?.activeView + + if (board?.icon !== prevBoard?.icon) { + Utils.setFavicon(board?.icon) + } + if (board?.title !== prevBoard?.title || activeView?.title !== prevActiveView?.title) { + document.title = `OCTO - ${board?.title} | ${activeView?.title}` + } + } + + undoRedoHandler = async (e: KeyboardEvent) => { + if (e.target !== document.body) { return } + + if (e.keyCode === 90 && !e.shiftKey && (e.ctrlKey || e.metaKey) && !e.altKey) { // Cmd+Z + Utils.log(`Undo`) + const description = UndoManager.shared.undoDescription + await UndoManager.shared.undo() + if (description) { + FlashMessage.show(`Undo ${description}`) + } else { + FlashMessage.show(`Undo`) + } + } else if (e.keyCode === 90 && e.shiftKey && (e.ctrlKey || e.metaKey) && !e.altKey) { // Shift+Cmd+Z + Utils.log(`Redo`) + const description = UndoManager.shared.redoDescription + await UndoManager.shared.redo() + if (description) { + FlashMessage.show(`Redo ${description}`) + } else { + FlashMessage.show(`Redo`) + } + } + } + + componentDidMount() { document.addEventListener("keydown", this.undoRedoHandler) if (this.state.boardId) { this.attachToBoard(this.state.boardId, this.state.viewId) - } else { - this.sync() - } + } else { + this.sync() + } } - componentWillUnmount() { + componentWillUnmount() { document.removeEventListener("keydown", this.undoRedoHandler) - } + } render() { + const { workspaceTree, shownCardTree } = this.state const { board, activeView } = this.state.boardTree || {} const mutator = new Mutator(this.octo) - // TODO Move all this into the root portal component when that is merged - if (this.state.boardTree && this.state.boardTree.board && this.shownCardTree) { + // TODO Move all this into the root portal component when that is merged + if (this.state.boardTree && this.state.boardTree.board && shownCardTree) { ReactDOM.render( - { this.showCard(undefined) }}>, + { this.showCard(undefined) }}>, Utils.getElementById("overlay") ) } else { - ReactDOM.render( -
, - Utils.getElementById("overlay") - ) + const overlay = document.getElementById("overlay") + if (overlay) { + ReactDOM.render( +
, + overlay + ) + } } if (this.filterAnchorElement) { @@ -146,50 +147,64 @@ export default class BoardPage extends React.Component { Utils.getElementById("modal") ) } else { - ReactDOM.render(
, Utils.getElementById("modal")) + const modal = document.getElementById("modal") + if (modal) { + ReactDOM.render(
, modal) + } } - return ( -
- , -
- ); + Utils.log(`BoardPage.render ${this.state.boardTree?.board?.title}`) + return ( +
+ { this.showView(id) }} + showCard={(card) => { this.showCard(card) }} + showBoard={(id) => { this.showBoard(id) }} + showFilter={(el) => { this.showFilter(el) }} + setSearchText={(text) => { this.setSearchText(text) }} /> +
+ ) } - private attachToBoard(boardId: string, viewId?: string) { - const boardTree = new BoardTree(this.octo, boardId) - this.setState({ - boardId, - viewId, - boardTree, - }) + private async attachToBoard(boardId: string, viewId?: string) { + Utils.log(`attachToBoard: ${boardId}`) this.boardListener.open(boardId, (blockId: string) => { console.log(`octoListener.onChanged: ${blockId}`) - this.sync() + this.sync(boardId) }) - this.sync() + this.sync(boardId, viewId) } - async sync() { - const { viewId, workspaceTree, boardTree } = this.state + async sync(boardId: string = this.state.boardId, viewId: string | undefined = this.state.viewId) { + const { workspaceTree } = this.state + Utils.log(`sync start: ${boardId}`) await workspaceTree.sync() - if (boardTree) { + + if (boardId) { + const boardTree = new BoardTree(this.octo, boardId) await boardTree.sync() // Default to first view if (!viewId) { - this.setState({viewId: boardTree.views[0].id}) + viewId = boardTree.views[0].id } - boardTree.setActiveView(this.state.viewId) + boardTree.setActiveView(viewId) // TODO: Handle error (viewId not found) - this.setState({ - viewId: boardTree.activeView.id - }) - console.log(`sync complete... title: ${boardTree.board.title}`) + this.setState({ + ...this.state, + boardTree, + viewId: boardTree.activeView.id + }) + Utils.log(`sync complete: ${boardTree.board.id} (${boardTree.board.title})`) + } else { + this.forceUpdate() } } @@ -201,20 +216,22 @@ export default class BoardPage extends React.Component { if (card) { const cardTree = new CardTree(this.octo, card.id) await cardTree.sync() - this.shownCardTree = cardTree + this.setState({...this.state, shownCardTree: cardTree}) this.cardListener = new OctoListener() this.cardListener.open(card.id, async () => { await cardTree.sync() - this.render() + this.forceUpdate() }) } else { - this.shownCardTree = undefined + this.setState({...this.state, shownCardTree: undefined}) } } showBoard(boardId: string) { - if (this.boardTree?.board?.id === boardId) { return } + const { boardTree } = this.state + + if (boardTree?.board?.id === boardId) { return } const newUrl = window.location.protocol + "//" + window.location.host + window.location.pathname + `?id=${encodeURIComponent(boardId)}` window.history.pushState({ path: newUrl }, "", newUrl) @@ -224,7 +241,7 @@ export default class BoardPage extends React.Component { showView(viewId: string) { this.state.boardTree.setActiveView(viewId) - this.setState({viewId, boardTree: this.state.boardTree}) + this.setState({ viewId, boardTree: this.state.boardTree }) const newUrl = window.location.protocol + "//" + window.location.host + window.location.pathname + `?id=${encodeURIComponent(this.state.boardId)}&v=${encodeURIComponent(viewId)}` window.history.pushState({ path: newUrl }, "", newUrl) } @@ -234,6 +251,6 @@ export default class BoardPage extends React.Component { } setSearchText(text?: string) { - this.boardTree.setSearchText(text) + this.state.boardTree?.setSearchText(text) } } diff --git a/src/static/main.css b/src/static/main.css index 512da8390..893d8b8ec 100644 --- a/src/static/main.css +++ b/src/static/main.css @@ -73,7 +73,9 @@ hr { overflow: auto; } -#octo-tasks-app > #main { +#octo-tasks-app #frame, +#octo-tasks-app #main, +#octo-tasks-app .BoardPage { flex: 1 1 auto; display: flex; flex-direction: column;