Refactor Blocks - each with its own class
This commit is contained in:
parent
469887f6fe
commit
fccade93ec
11 changed files with 112 additions and 49 deletions
|
@ -7,7 +7,6 @@ class Block implements IBlock {
|
|||
parentId: string
|
||||
type: string
|
||||
title: string
|
||||
order: number
|
||||
fields: Record<string, any> = {}
|
||||
createAt: number = Date.now()
|
||||
updateAt: number = 0
|
||||
|
@ -38,7 +37,6 @@ class Block implements IBlock {
|
|||
this.fields = block.fields ? { ...block.fields } : {}
|
||||
|
||||
this.title = block.title
|
||||
this.order = block.order
|
||||
|
||||
this.createAt = block.createAt || now
|
||||
this.updateAt = block.updateAt || now
|
||||
|
|
10
src/client/blocks/commentBlock.ts
Normal file
10
src/client/blocks/commentBlock.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
import { Block } from "./block"
|
||||
|
||||
class CommentBlock extends Block {
|
||||
constructor(block: any = {}) {
|
||||
super(block)
|
||||
this.type = "comment"
|
||||
}
|
||||
}
|
||||
|
||||
export { CommentBlock }
|
17
src/client/blocks/imageBlock.ts
Normal file
17
src/client/blocks/imageBlock.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { IOrderedBlock } from "../octoTypes"
|
||||
import { Block } from "./block"
|
||||
|
||||
class ImageBlock extends Block implements IOrderedBlock {
|
||||
get order(): number { return this.fields.order as number }
|
||||
set order(value: number) { this.fields.order = value }
|
||||
|
||||
get url(): string { return this.fields.url as string }
|
||||
set url(value: string) { this.fields.url = value }
|
||||
|
||||
constructor(block: any = {}) {
|
||||
super(block)
|
||||
this.type = "image"
|
||||
}
|
||||
}
|
||||
|
||||
export { ImageBlock }
|
14
src/client/blocks/textBlock.ts
Normal file
14
src/client/blocks/textBlock.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import { IOrderedBlock } from "../octoTypes"
|
||||
import { Block } from "./block"
|
||||
|
||||
class TextBlock extends Block implements IOrderedBlock {
|
||||
get order(): number { return this.fields.order as number }
|
||||
set order(value: number) { this.fields.order = value }
|
||||
|
||||
constructor(block: any = {}) {
|
||||
super(block)
|
||||
this.type = "text"
|
||||
}
|
||||
}
|
||||
|
||||
export { TextBlock }
|
|
@ -1,9 +1,11 @@
|
|||
import { Block } from "./blocks/block"
|
||||
import { Board, IPropertyOption, IPropertyTemplate } from "./blocks/board"
|
||||
import { BoardView } from "./blocks/boardView"
|
||||
import { Card } from "./blocks/card"
|
||||
import { CardFilter } from "./cardFilter"
|
||||
import octoClient from "./octoClient"
|
||||
import { IBlock } from "./octoTypes"
|
||||
import { OctoUtils } from "./octoUtils"
|
||||
import { Utils } from "./utils"
|
||||
|
||||
type Group = { option: IPropertyOption, cards: Card[] }
|
||||
|
@ -29,21 +31,13 @@ class BoardTree {
|
|||
|
||||
async sync() {
|
||||
const blocks = await octoClient.getSubtree(this.boardId)
|
||||
this.rebuild(blocks)
|
||||
this.rebuild(OctoUtils.hydrateBlocks(blocks))
|
||||
}
|
||||
|
||||
private rebuild(blocks: IBlock[]) {
|
||||
const boardBlock = blocks.find(block => block.type === "board")
|
||||
|
||||
if (boardBlock) {
|
||||
this.board = new Board(boardBlock)
|
||||
}
|
||||
|
||||
const viewBlocks = blocks.filter(block => block.type === "view")
|
||||
this.views = viewBlocks.map(o => new BoardView(o))
|
||||
|
||||
const cardBlocks = blocks.filter(block => block.type === "card")
|
||||
this.allCards = cardBlocks.map(o => new Card(o))
|
||||
private rebuild(blocks: Block[]) {
|
||||
this.board = blocks.find(block => block.type === "board") as Board
|
||||
this.views = blocks.filter(block => block.type === "view") as BoardView[]
|
||||
this.allCards = blocks.filter(block => block.type === "card") as Card[]
|
||||
this.cards = []
|
||||
|
||||
this.ensureMinimumSchema()
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import { Block } from "./blocks/block"
|
||||
import { Card } from "./blocks/card"
|
||||
import octoClient from "./octoClient"
|
||||
import { IBlock } from "./octoTypes"
|
||||
import { IBlock, IOrderedBlock } from "./octoTypes"
|
||||
import { OctoUtils } from "./octoUtils"
|
||||
|
||||
class CardTree {
|
||||
card: Card
|
||||
comments: IBlock[]
|
||||
contents: IBlock[]
|
||||
contents: IOrderedBlock[]
|
||||
isSynched: boolean
|
||||
|
||||
constructor(private cardId: string) {
|
||||
|
@ -13,19 +15,18 @@ class CardTree {
|
|||
|
||||
async sync() {
|
||||
const blocks = await octoClient.getSubtree(this.cardId)
|
||||
this.rebuild(blocks)
|
||||
this.rebuild(OctoUtils.hydrateBlocks(blocks))
|
||||
}
|
||||
|
||||
private rebuild(blocks: IBlock[]) {
|
||||
this.card = new Card(blocks.find(o => o.id === this.cardId))
|
||||
private rebuild(blocks: Block[]) {
|
||||
this.card = blocks.find(o => o.id === this.cardId) as Card
|
||||
|
||||
this.comments = blocks
|
||||
.filter(block => block.type === "comment")
|
||||
.sort((a, b) => a.createAt - b.createAt)
|
||||
|
||||
this.contents = blocks
|
||||
.filter(block => block.type === "text" || block.type === "image")
|
||||
.sort((a, b) => a.order - b.order)
|
||||
const contentBlocks = blocks.filter(block => block.type === "text" || block.type === "image") as IOrderedBlock[]
|
||||
this.contents = contentBlocks.sort((a, b) => a.order - b.order)
|
||||
|
||||
this.isSynched = true
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import { BoardTree } from "../boardTree"
|
|||
import { CardTree } from "../cardTree"
|
||||
import { Menu, MenuOption } from "../menu"
|
||||
import mutator from "../mutator"
|
||||
import { IBlock } from "../octoTypes"
|
||||
import { IBlock, IOrderedBlock } from "../octoTypes"
|
||||
import { OctoUtils } from "../octoUtils"
|
||||
import { PropertyMenu } from "../propertyMenu"
|
||||
import { OctoListener } from "../octoListener"
|
||||
|
@ -14,6 +14,8 @@ import { Utils } from "../utils"
|
|||
import Button from "./button"
|
||||
import { Editable } from "./editable"
|
||||
import { MarkdownEditor } from "./markdownEditor"
|
||||
import { TextBlock } from "../blocks/textBlock"
|
||||
import { CommentBlock } from "../blocks/commentBlock"
|
||||
|
||||
type Props = {
|
||||
boardTree: BoardTree
|
||||
|
@ -125,8 +127,8 @@ class CardDialog extends React.Component<Props, State> {
|
|||
text=""
|
||||
placeholderText="Add a description..."
|
||||
onChanged={(text) => {
|
||||
const order = cardTree.contents.length * 1000
|
||||
const block = new Block({ type: "text", parentId: card.id, title: text, order })
|
||||
const block = new TextBlock({ parentId: card.id, title: text })
|
||||
block.order = cardTree.contents.length * 1000
|
||||
mutator.insertBlock(block, "add card text")
|
||||
}} />
|
||||
</div>
|
||||
|
@ -329,8 +331,8 @@ class CardDialog extends React.Component<Props, State> {
|
|||
Menu.shared.onMenuClicked = async (optionId: string, type?: string) => {
|
||||
switch (optionId) {
|
||||
case "text":
|
||||
const order = cardTree.contents.length * 1000
|
||||
const block = new Block({ type: "text", parentId: card.id, order })
|
||||
const block = new TextBlock({ parentId: card.id })
|
||||
block.order = cardTree.contents.length * 1000
|
||||
await mutator.insertBlock(block, "add text")
|
||||
break
|
||||
case "image":
|
||||
|
@ -359,11 +361,11 @@ class CardDialog extends React.Component<Props, State> {
|
|||
|
||||
Utils.assertValue(card)
|
||||
|
||||
const block = new Block({ type: "comment", parentId: card.id, title: text })
|
||||
const block = new CommentBlock({ parentId: card.id, title: text })
|
||||
await mutator.insertBlock(block, "add comment")
|
||||
}
|
||||
|
||||
private showContentBlockMenu(e: React.MouseEvent, block: IBlock) {
|
||||
private showContentBlockMenu(e: React.MouseEvent, block: IOrderedBlock) {
|
||||
const { card } = this.props
|
||||
const { cardTree } = this.state
|
||||
const index = cardTree.contents.indexOf(block)
|
||||
|
@ -405,10 +407,10 @@ class CardDialog extends React.Component<Props, State> {
|
|||
break
|
||||
}
|
||||
case "insertAbove-text": {
|
||||
const newBlock = new Block({ type: "text", parentId: card.id })
|
||||
// TODO: Handle need to reorder all blocks
|
||||
const newBlock = new TextBlock({ parentId: card.id })
|
||||
newBlock.order = OctoUtils.getOrderBefore(block, cardTree.contents)
|
||||
Utils.log(`insert block ${block.id}, order: ${block.order}`)
|
||||
// TODO: Handle need to reorder all blocks
|
||||
Utils.log(`insert block ${newBlock.id}, order: ${newBlock.order}`)
|
||||
mutator.insertBlock(newBlock, "insert card text")
|
||||
break
|
||||
}
|
||||
|
|
|
@ -2,10 +2,11 @@ import { Block } from "./blocks/block"
|
|||
import { Board, IPropertyOption, IPropertyTemplate, PropertyType } from "./blocks/board"
|
||||
import { BoardView, ISortOption } from "./blocks/boardView"
|
||||
import { Card } from "./blocks/card"
|
||||
import { ImageBlock } from "./blocks/imageBlock"
|
||||
import { BoardTree } from "./boardTree"
|
||||
import { FilterGroup } from "./filterGroup"
|
||||
import octoClient from "./octoClient"
|
||||
import { IBlock } from "./octoTypes"
|
||||
import { IBlock, IOrderedBlock } from "./octoTypes"
|
||||
import undoManager from "./undomanager"
|
||||
import { Utils } from "./utils"
|
||||
|
||||
|
@ -92,7 +93,7 @@ class Mutator {
|
|||
)
|
||||
}
|
||||
|
||||
async changeOrder(block: IBlock, order: number, description: string = "change order") {
|
||||
async changeOrder(block: IOrderedBlock, order: number, description: string = "change order") {
|
||||
const oldValue = block.order
|
||||
await undoManager.perform(
|
||||
async () => {
|
||||
|
@ -491,8 +492,9 @@ class Mutator {
|
|||
return undefined
|
||||
}
|
||||
|
||||
const block = new Block({ type: "image", parentId, order })
|
||||
block.fields.url = url
|
||||
const block = new ImageBlock({ parentId })
|
||||
block.order = order
|
||||
block.url = url
|
||||
|
||||
await undoManager.perform(
|
||||
async () => {
|
||||
|
|
|
@ -6,7 +6,6 @@ interface IBlock {
|
|||
schema: number
|
||||
type: string
|
||||
title?: string
|
||||
order: number
|
||||
fields: Record<string, any>
|
||||
|
||||
createAt: number
|
||||
|
@ -14,6 +13,10 @@ interface IBlock {
|
|||
deleteAt: number
|
||||
}
|
||||
|
||||
interface IOrderedBlock extends IBlock {
|
||||
order: number
|
||||
}
|
||||
|
||||
// These are methods exposed by the top-level page to components
|
||||
interface IPageController {
|
||||
showBoard(boardId: string): void
|
||||
|
@ -22,4 +25,4 @@ interface IPageController {
|
|||
setSearchText(text?: string): void
|
||||
}
|
||||
|
||||
export { IBlock, IPageController }
|
||||
export { IBlock, IOrderedBlock, IPageController }
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
import React from "react"
|
||||
import { IPropertyTemplate } from "./blocks/board"
|
||||
import { ISortOption } from "./blocks/boardView"
|
||||
import { Block } from "./blocks/block"
|
||||
import { Board, IPropertyTemplate } from "./blocks/board"
|
||||
import { BoardView, ISortOption } from "./blocks/boardView"
|
||||
import { Card } from "./blocks/card"
|
||||
import { CommentBlock } from "./blocks/commentBlock"
|
||||
import { ImageBlock } from "./blocks/imageBlock"
|
||||
import { TextBlock } from "./blocks/textBlock"
|
||||
import { BoardTree } from "./boardTree"
|
||||
import { Editable } from "./components/editable"
|
||||
import { Menu } from "./menu"
|
||||
import mutator from "./mutator"
|
||||
import { IBlock } from "./octoTypes"
|
||||
import { IBlock, IOrderedBlock } from "./octoTypes"
|
||||
import { Utils } from "./utils"
|
||||
|
||||
class OctoUtils {
|
||||
|
@ -103,7 +107,7 @@ class OctoUtils {
|
|||
return element
|
||||
}
|
||||
|
||||
static getOrderBefore(block: IBlock, blocks: IBlock[]): number {
|
||||
static getOrderBefore(block: IOrderedBlock, blocks: IOrderedBlock[]): number {
|
||||
const index = blocks.indexOf(block)
|
||||
if (index === 0) {
|
||||
return block.order / 2
|
||||
|
@ -112,7 +116,7 @@ class OctoUtils {
|
|||
return (block.order + previousBlock.order) / 2
|
||||
}
|
||||
|
||||
static getOrderAfter(block: IBlock, blocks: IBlock[]): number {
|
||||
static getOrderAfter(block: IOrderedBlock, blocks: IOrderedBlock[]): number {
|
||||
const index = blocks.indexOf(block)
|
||||
if (index === blocks.length - 1) {
|
||||
return block.order + 1000
|
||||
|
@ -151,6 +155,24 @@ class OctoUtils {
|
|||
}
|
||||
Menu.shared.showAtElement(e.target as HTMLElement)
|
||||
}
|
||||
|
||||
static hydrateBlock(block: IBlock): Block {
|
||||
switch (block.type) {
|
||||
case "board": { return new Board(block) }
|
||||
case "view": { return new BoardView(block) }
|
||||
case "card": { return new Card(block) }
|
||||
case "text": { return new TextBlock(block) }
|
||||
case "image": { return new ImageBlock(block) }
|
||||
case "comment": { return new CommentBlock(block) }
|
||||
default: {
|
||||
Utils.assertFailure(`Can't hydrate unknown block type: ${block.type}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static hydrateBlocks(blocks: IBlock[]): Block[] {
|
||||
return blocks.map( block => this.hydrateBlock(block) )
|
||||
}
|
||||
}
|
||||
|
||||
export { OctoUtils }
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
import { Block } from "./blocks/block"
|
||||
import { Board } from "./blocks/board"
|
||||
import octoClient from "./octoClient"
|
||||
import { IBlock } from "./octoTypes"
|
||||
import { OctoUtils } from "./octoUtils"
|
||||
|
||||
class WorkspaceTree {
|
||||
boards: Board[] = []
|
||||
|
||||
async sync() {
|
||||
const blocks = await octoClient.getBlocks(undefined, "board")
|
||||
this.rebuild(blocks)
|
||||
this.rebuild(OctoUtils.hydrateBlocks(blocks))
|
||||
}
|
||||
|
||||
private rebuild(blocks: IBlock[]) {
|
||||
const boardBlocks = blocks.filter(block => block.type === "board")
|
||||
this.boards = boardBlocks.map(o => new Board(o))
|
||||
private rebuild(blocks: Block[]) {
|
||||
this.boards = blocks.filter(block => block.type === "board") as Board[]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue