From a84ff6901a1282ed7e40c6a049272d4e310f7a70 Mon Sep 17 00:00:00 2001 From: Chen-I Lim Date: Tue, 13 Oct 2020 14:42:20 -0700 Subject: [PATCH] Initial implementation of sidebar. --- html-templates/index.ejs | 29 ---- server/main/main.go | 20 ++- server/main/octoDatabase.go | 26 ++++ src/client/boardPage.tsx | 146 +++++++++---------- src/client/boardsPage.ts | 112 -------------- src/client/components/sidebar.tsx | 126 ++++++++++++++++ src/client/components/tableComponent.tsx | 2 +- src/client/components/tableRow.tsx | 2 +- src/client/components/workspaceComponent.tsx | 57 ++++++++ src/client/mutator.ts | 4 +- src/client/octoTypes.ts | 5 +- src/client/workspaceTree.ts | 23 +++ src/static/main.css | 91 +++++++++++- webpack.common.js | 31 +--- 14 files changed, 417 insertions(+), 257 deletions(-) delete mode 100644 html-templates/index.ejs delete mode 100644 src/client/boardsPage.ts create mode 100644 src/client/components/sidebar.tsx create mode 100644 src/client/components/workspaceComponent.tsx create mode 100644 src/client/workspaceTree.ts diff --git a/html-templates/index.ejs b/html-templates/index.ejs deleted file mode 100644 index 85f4fd368..000000000 --- a/html-templates/index.ejs +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - <%= htmlWebpackPlugin.options.title %> - - - - - - - - - - - -
-

- All Boards -

-
- - - \ No newline at end of file diff --git a/server/main/main.go b/server/main/main.go index db2aebb07..f49eb7156 100644 --- a/server/main/main.go +++ b/server/main/main.go @@ -39,6 +39,13 @@ func handleStaticFile(r *mux.Router, requestPath string, filePath string, conten }) } +func handleDefault(r *mux.Router, requestPath string) { + r.HandleFunc(requestPath, func(w http.ResponseWriter, r *http.Request) { + log.Printf("handleDefault") + http.Redirect(w, r, "/board", http.StatusFound) + }) +} + // ---------------------------------------------------------------------------------------------------- // REST APIs @@ -48,12 +55,15 @@ func handleGetBlocks(w http.ResponseWriter, r *http.Request) { blockType := query.Get("type") var blocks []string - if len(blockType) > 0 { + if len(blockType) > 0 && len(parentID) > 0 { blocks = getBlocksWithParentAndType(parentID, blockType) + } else if len(blockType) > 0 { + blocks = getBlocksWithType(blockType) } else { blocks = getBlocksWithParent(parentID) } - log.Printf("GetBlocks parentID: %s, %d result(s)", parentID, len(blocks)) + + log.Printf("GetBlocks parentID: %s, type: %s, %d result(s)", parentID, blockType, len(blocks)) response := `[` + strings.Join(blocks[:], ",") + `]` jsonResponse(w, 200, response) } @@ -335,11 +345,9 @@ func main() { r := mux.NewRouter() // Static files - handleStaticFile(r, "/", "index.html", "text/html; charset=utf-8") - handleStaticFile(r, "/boards", "boards.html", "text/html; charset=utf-8") - handleStaticFile(r, "/board", "board.html", "text/html; charset=utf-8") + handleDefault(r, "/") - handleStaticFile(r, "/boardsPage.js", "boardsPage.js", "text/javascript; charset=utf-8") + handleStaticFile(r, "/board", "board.html", "text/html; charset=utf-8") handleStaticFile(r, "/boardPage.js", "boardPage.js", "text/javascript; charset=utf-8") handleStaticFile(r, "/favicon.ico", "static/favicon.svg", "image/svg+xml; charset=utf-8") diff --git a/server/main/octoDatabase.go b/server/main/octoDatabase.go index 4a4568fa1..3b98953f2 100644 --- a/server/main/octoDatabase.go +++ b/server/main/octoDatabase.go @@ -155,6 +155,32 @@ func getBlocksWithParent(parentID string) []string { return blocksFromRows(rows) } +func getBlocksWithType(blockType string) []string { + query := `WITH latest AS + ( + SELECT * FROM + ( + SELECT + *, + ROW_NUMBER() OVER (PARTITION BY id ORDER BY insert_at DESC) AS rn + FROM blocks + ) a + WHERE rn = 1 + ) + + SELECT COALESCE("json", '{}') + FROM latest + WHERE delete_at = 0 and type = $1` + + rows, err := db.Query(query, blockType) + if err != nil { + log.Printf(`getBlocksWithParentAndType ERROR: %v`, err) + panic(err) + } + + return blocksFromRows(rows) +} + func getSubTree(blockID string) []string { query := `WITH latest AS ( diff --git a/src/client/boardPage.tsx b/src/client/boardPage.tsx index 6e6c2a6f2..ad5eb650f 100644 --- a/src/client/boardPage.tsx +++ b/src/client/boardPage.tsx @@ -3,11 +3,10 @@ import ReactDOM from "react-dom" import { BoardTree } from "./boardTree" import { BoardView } from "./boardView" import { CardTree } from "./cardTree" -import { BoardComponent } from "./components/boardComponent" import { CardDialog } from "./components/cardDialog" import { FilterComponent } from "./components/filterComponent" import { PageHeader } from "./components/pageHeader" -import { TableComponent } from "./components/tableComponent" +import { WorkspaceComponent } from "./components/workspaceComponent" import { FlashMessage } from "./flashMessage" import { Mutator } from "./mutator" import { OctoClient } from "./octoClient" @@ -15,6 +14,7 @@ import { OctoListener } from "./octoListener" import { IBlock, IPageController } from "./octoTypes" import { UndoManager } from "./undomanager" import { Utils } from "./utils" +import { WorkspaceTree } from "./workspaceTree" class BoardPage implements IPageController { boardTitle: HTMLElement @@ -23,10 +23,11 @@ class BoardPage implements IPageController { groupByButton: HTMLElement groupByLabel: HTMLElement - boardId: string - viewId: string + boardId?: string + viewId?: string - boardTree: BoardTree + workspaceTree: WorkspaceTree + boardTree?: BoardTree view: BoardView updateTitleTimeout: number @@ -41,28 +42,18 @@ class BoardPage implements IPageController { constructor() { const queryString = new URLSearchParams(window.location.search) - if (!queryString.has("id")) { - // No id, redirect to home - window.location.href = "/" - return - } + const boardId = queryString.get("id") + const viewId = queryString.get("v") this.layoutPage() - this.boardId = queryString.get("id") - this.viewId = queryString.get("v") + this.workspaceTree = new WorkspaceTree(this.octo) console.log(`BoardPage. boardId: ${this.boardId}`) - if (this.boardId) { - this.boardTree = new BoardTree(this.octo, this.boardId) - this.sync() - - this.boardListener.open(this.boardId, (blockId: string) => { - console.log(`octoListener.onChanged: ${blockId}`) - this.sync() - }) + if (boardId) { + this.attachToBoard(boardId, viewId) } else { - // Show error + this.sync() } document.body.addEventListener("keydown", async (e) => { @@ -111,10 +102,15 @@ class BoardPage implements IPageController { render() { const { octo, boardTree } = this - const { board, activeView } = boardTree + const { board, activeView } = boardTree || {} const mutator = new Mutator(octo) - const rootElement = Utils.getElementById("main") + const mainElement = Utils.getElementById("main") + + ReactDOM.render( + , + Utils.getElementById("header") + ) ReactDOM.render( , @@ -123,54 +119,23 @@ class BoardPage implements IPageController { if (board) { Utils.setFavicon(board.icon) - } else { - ReactDOM.render( -
Loading...
, - rootElement - ) - return + document.title = `OCTO - ${board.title} | ${activeView.title}` } - if (activeView) { - document.title = `OCTO - ${board.title} | ${activeView.title}` + ReactDOM.render( + , + mainElement + ) - switch (activeView.viewType) { - case "board": { - ReactDOM.render( - , - rootElement - ) - break - } - - case "table": { - ReactDOM.render( - , - rootElement - ) - break - } - - default: { - Utils.assertFailure(`render() Unhandled viewType: ${activeView.viewType}`) - } - } - - if (boardTree && boardTree.board && this.shownCardTree) { - ReactDOM.render( - { this.showCard(undefined) }}>, - Utils.getElementById("overlay") - ) - } else { - ReactDOM.render( -
, - Utils.getElementById("overlay") - ) - } + if (boardTree && boardTree.board && this.shownCardTree) { + ReactDOM.render( + { this.showCard(undefined) }}>, + Utils.getElementById("overlay") + ) } else { ReactDOM.render( -
Loading...
, - rootElement +
, + Utils.getElementById("overlay") ) } @@ -189,7 +154,7 @@ class BoardPage implements IPageController { boardTree={boardTree} pageX={pageX} pageY={pageY} - onClose={() => {this.showFilter(undefined)}} + onClose={() => { this.showFilter(undefined) }} > , Utils.getElementById("modal") @@ -199,22 +164,38 @@ class BoardPage implements IPageController { } } + private attachToBoard(boardId: string, viewId?: string) { + this.boardId = boardId + this.viewId = viewId + + this.boardTree = new BoardTree(this.octo, boardId) + + this.boardListener.open(boardId, (blockId: string) => { + console.log(`octoListener.onChanged: ${blockId}`) + this.sync() + }) + + this.sync() + } + async sync() { - const { boardTree } = this + const { workspaceTree, boardTree } = this - await boardTree.sync() + await workspaceTree.sync() + if (boardTree) { + await boardTree.sync() - // Default to first view - if (!this.viewId) { - this.viewId = boardTree.views[0].id + // Default to first view + if (!this.viewId) { + this.viewId = boardTree.views[0].id + } + + boardTree.setActiveView(this.viewId) + // TODO: Handle error (viewId not found) + this.viewId = boardTree.activeView.id + console.log(`sync complete... title: ${boardTree.board.title}`) } - boardTree.setActiveView(this.viewId) - // TODO: Handle error (viewId not found) - this.viewId = boardTree.activeView.id - - console.log(`sync complete... title: ${boardTree.board.title}`) - this.render() } @@ -240,6 +221,15 @@ class BoardPage implements IPageController { this.render() } + showBoard(boardId: string) { + if (this.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) + + this.attachToBoard(boardId) + } + showView(viewId: string) { this.viewId = viewId this.boardTree.setActiveView(this.viewId) diff --git a/src/client/boardsPage.ts b/src/client/boardsPage.ts deleted file mode 100644 index b37603446..000000000 --- a/src/client/boardsPage.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { Archiver } from "./archiver" -import { Board } from "./board" -import { Mutator } from "./mutator" -import { OctoClient } from "./octoClient" -import { UndoManager } from "./undomanager" -import { Utils } from "./utils" - -class BoardsPage { - boardsPanel: HTMLElement - - boardId: string - boards: Board[] - - octo = new OctoClient() - - constructor() { - // This is a placeholder page - - const root = Utils.getElementById("octo-tasks-app") - root.innerText = "" - - // Header - root.appendChild(Utils.htmlToElement(`