Merge pull request #11 from mattermost/singleton-client-and-mutator

Making client and mutator singleton instances
This commit is contained in:
chenilim 2020-10-15 08:39:29 -07:00 committed by GitHub
commit 6b4915b654
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 163 additions and 220 deletions

View file

@ -1,5 +1,5 @@
import { BoardTree } from "./boardTree"
import { Mutator } from "./mutator"
import mutator from "./mutator"
import { IBlock } from "./octoTypes"
import { Utils } from "./utils"
@ -21,7 +21,7 @@ class Archiver {
this.exportArchive(archive)
}
static async exportFullArchive(mutator: Mutator) {
static async exportFullArchive() {
const blocks = await mutator.exportFullArchive()
const archive: Archive = {
version: 1,
@ -50,7 +50,7 @@ class Archiver {
// TODO: Remove or reuse link
}
static importFullArchive(mutator: Mutator, onImported?: () => void): void {
static importFullArchive(onImported?: () => void): void {
const input = document.createElement("input")
input.type = "file"
input.accept = ".octo"

View file

@ -2,7 +2,7 @@ import { Board, IPropertyOption, IPropertyTemplate } from "./board"
import { BoardView } from "./boardView"
import { Card } from "./card"
import { CardFilter } from "./cardFilter"
import { OctoClient } from "./octoClient"
import octoClient from "./octoClient"
import { IBlock } from "./octoTypes"
import { Utils } from "./utils"
@ -24,13 +24,11 @@ class BoardTree {
return [this.board, ...this.views, ...this.allCards]
}
constructor(
private octo: OctoClient,
private boardId: string) {
constructor(private boardId: string) {
}
async sync() {
const blocks = await this.octo.getSubtree(this.boardId)
const blocks = await octoClient.getSubtree(this.boardId)
this.rebuild(blocks)
}

View file

@ -1,5 +1,5 @@
import { Card } from "./card"
import { OctoClient } from "./octoClient"
import octoClient from "./octoClient"
import { IBlock } from "./octoTypes"
class CardTree {
@ -8,13 +8,11 @@ class CardTree {
contents: IBlock[]
isSynched: boolean
constructor(
private octo: OctoClient,
private cardId: string) {
constructor(private cardId: string) {
}
async sync() {
const blocks = await this.octo.getSubtree(this.cardId)
const blocks = await octoClient.getSubtree(this.cardId)
this.rebuild(blocks)
}

View file

@ -3,12 +3,11 @@ import { Block } from "../block"
import { IPropertyTemplate } from "../board"
import { Card } from "../card"
import { Menu } from "../menu"
import { Mutator } from "../mutator"
import mutator from "../mutator"
import { OctoUtils } from "../octoUtils"
import { Utils } from "../utils"
type BoardCardProps = {
mutator: Mutator
card: Card
visiblePropertyTemplates: IPropertyTemplate[]
onClick?: (e: React.MouseEvent<HTMLDivElement>) => void
@ -58,7 +57,7 @@ class BoardCard extends React.Component<BoardCardProps, BoardCardState> {
}
private showOptionsMenu(e: React.MouseEvent) {
const { mutator, card } = this.props
const { card } = this.props
e.stopPropagation()

View file

@ -11,7 +11,7 @@ import Menu from "../widgets/menu"
import { Constants } from "../constants"
import { randomEmojiList } from "../emojiList"
import { Menu as OldMenu } from "../menu"
import { Mutator } from "../mutator"
import mutator from "../mutator"
import { OctoUtils } from "../octoUtils"
import { Utils } from "../utils"
import { BoardCard } from "./boardCard"
@ -20,7 +20,6 @@ import Button from "./button"
import { Editable } from "./editable"
type Props = {
mutator: Mutator,
boardTree?: BoardTree
showView: (id: string) => void
showCard: (card: Card) => void
@ -29,8 +28,8 @@ type Props = {
}
type State = {
isHoverOnCover: boolean
isSearching: boolean
isHoverOnCover: boolean
}
class BoardComponent extends React.Component<Props, State> {
@ -50,7 +49,7 @@ class BoardComponent extends React.Component<Props, State> {
}
render() {
const { mutator, boardTree, showView } = this.props
const { boardTree, showView } = this.props
if (!boardTree || !boardTree.board) {
return (
@ -109,7 +108,6 @@ class BoardComponent extends React.Component<Props, State> {
</div>
<ViewMenu
board={board}
mutator={mutator}
boardTree={boardTree}
showView={showView}
/>
@ -120,7 +118,7 @@ class BoardComponent extends React.Component<Props, State> {
Group by <span style={groupByStyle} id="groupByLabel">{boardTree.groupByProperty?.name}</span>
</div>
<div className={hasFilter ? "octo-button active" : "octo-button"} onClick={(e) => { this.filterClicked(e) }}>Filter</div>
<div className={hasSort ? "octo-button active" : "octo-button"} onClick={(e) => { OctoUtils.showSortMenu(e, mutator, boardTree) }}>Sort</div>
<div className={hasSort ? "octo-button active" : "octo-button"} onClick={(e) => { OctoUtils.showSortMenu(e, boardTree) }}>Sort</div>
{this.state.isSearching
? <Editable
ref={this.searchFieldRef}
@ -197,7 +195,6 @@ class BoardComponent extends React.Component<Props, State> {
<BoardColumn onDrop={(e) => { this.onDropToColumn(undefined) }}>
{boardTree.emptyGroupCards.map(card =>
<BoardCard
mutator={mutator}
card={card}
visiblePropertyTemplates={visiblePropertyTemplates}
key={card.id}
@ -214,7 +211,6 @@ class BoardComponent extends React.Component<Props, State> {
<BoardColumn onDrop={(e) => { this.onDropToColumn(group.option) }} key={group.option.value}>
{group.cards.map(card =>
<BoardCard
mutator={mutator}
card={card}
visiblePropertyTemplates={visiblePropertyTemplates}
key={card.id}
@ -239,7 +235,7 @@ class BoardComponent extends React.Component<Props, State> {
}
async addCard(groupByValue?: string) {
const { mutator, boardTree } = this.props
const { boardTree } = this.props
const { activeView, board } = boardTree
const card = new Card()
@ -252,7 +248,7 @@ class BoardComponent extends React.Component<Props, State> {
}
async propertyNameChanged(option: IPropertyOption, text: string) {
const { mutator, boardTree } = this.props
const { boardTree } = this.props
await mutator.changePropertyOptionValue(boardTree, boardTree.groupByProperty, option, text)
}
@ -290,7 +286,7 @@ class BoardComponent extends React.Component<Props, State> {
}
private async testAddCards(count: number) {
const { mutator, boardTree } = this.props
const { boardTree } = this.props
const { board, activeView } = boardTree
const startCount = boardTree?.cards?.length
@ -313,7 +309,7 @@ class BoardComponent extends React.Component<Props, State> {
}
private async propertiesClicked(e: React.MouseEvent) {
const { mutator, boardTree } = this.props
const { boardTree } = this.props
const { activeView } = boardTree
const selectProperties = boardTree.board.cardProperties
@ -339,7 +335,7 @@ class BoardComponent extends React.Component<Props, State> {
}
private async groupByClicked(e: React.MouseEvent) {
const { mutator, boardTree } = this.props
const { boardTree } = this.props
const selectProperties = boardTree.board.cardProperties.filter(o => o.type === "select")
OldMenu.shared.options = selectProperties.map((o) => { return { id: o.id, name: o.name } })
@ -354,7 +350,7 @@ class BoardComponent extends React.Component<Props, State> {
async addGroupClicked() {
console.log(`onAddGroupClicked`)
const { mutator, boardTree } = this.props
const { boardTree } = this.props
const option: IPropertyOption = {
value: "New group",
@ -366,7 +362,7 @@ class BoardComponent extends React.Component<Props, State> {
}
async onDropToColumn(option: IPropertyOption) {
const { mutator, boardTree } = this.props
const { boardTree } = this.props
const { draggedCard, draggedHeaderOption } = this
const propertyValue = option ? option.value : undefined

View file

@ -4,7 +4,7 @@ import { BlockIcons } from "../blockIcons"
import { BoardTree } from "../boardTree"
import { CardTree } from "../cardTree"
import { Menu, MenuOption } from "../menu"
import { Mutator } from "../mutator"
import mutator from "../mutator"
import { IBlock } from "../octoTypes"
import { OctoUtils } from "../octoUtils"
import { PropertyMenu } from "../propertyMenu"
@ -16,7 +16,6 @@ import { MarkdownEditor } from "./markdownEditor"
type Props = {
boardTree: BoardTree
cardTree: CardTree
mutator: Mutator
onClose: () => void
}
@ -51,7 +50,7 @@ class CardDialog extends React.Component<Props, State> {
}
render() {
const { boardTree, cardTree, mutator } = this.props
const { boardTree, cardTree } = this.props
const { board } = boardTree
const { card, comments } = cardTree
@ -203,7 +202,7 @@ class CardDialog extends React.Component<Props, State> {
}
menu.showAtElement(e.target as HTMLElement)
}}>{propertyTemplate.name}</div>
{OctoUtils.propertyValueEditableElement(mutator, card, propertyTemplate)}
{OctoUtils.propertyValueEditableElement(card, propertyTemplate)}
</div>
)
})}
@ -332,7 +331,7 @@ class CardDialog extends React.Component<Props, State> {
}
async sendComment(text: string) {
const { mutator, cardTree } = this.props
const { cardTree } = this.props
const { card } = cardTree
Utils.assertValue(card)
@ -342,7 +341,7 @@ class CardDialog extends React.Component<Props, State> {
}
private showContentBlockMenu(e: React.MouseEvent, block: IBlock) {
const { mutator, cardTree } = this.props
const { cardTree } = this.props
const { card } = cardTree
const index = cardTree.contents.indexOf(block)
@ -409,7 +408,7 @@ class CardDialog extends React.Component<Props, State> {
}
private iconClicked(e: React.MouseEvent) {
const { mutator, cardTree } = this.props
const { cardTree } = this.props
const { card } = cardTree
Menu.shared.options = [

View file

@ -3,11 +3,10 @@ import { BoardTree } from "../boardTree"
import { FilterClause, FilterCondition } from "../filterClause"
import { FilterGroup } from "../filterGroup"
import { Menu } from "../menu"
import { Mutator } from "../mutator"
import mutator from "../mutator"
import { Utils } from "../utils"
type Props = {
mutator: Mutator
boardTree: BoardTree
pageX: number
pageY: number
@ -67,7 +66,7 @@ class FilterComponent extends React.Component<Props> {
}
private propertyClicked(e: React.MouseEvent, filter: FilterClause) {
const { mutator, boardTree } = this.props
const { boardTree } = this.props
const { board, activeView: view } = boardTree
const filterIndex = view.filter.filters.indexOf(filter)
@ -90,7 +89,7 @@ class FilterComponent extends React.Component<Props> {
}
private conditionClicked(e: React.MouseEvent, filter: FilterClause) {
const { mutator, boardTree } = this.props
const { boardTree } = this.props
const { activeView: view } = boardTree
const filterIndex = view.filter.filters.indexOf(filter)
@ -115,7 +114,7 @@ class FilterComponent extends React.Component<Props> {
}
private valuesClicked(e: React.MouseEvent, filter: FilterClause) {
const { mutator, boardTree } = this.props
const { boardTree } = this.props
const { board, activeView: view } = boardTree
const template = board.cardProperties.find(o => o.id === filter.propertyId)
@ -143,7 +142,7 @@ class FilterComponent extends React.Component<Props> {
}
private deleteClicked(filter: FilterClause) {
const { mutator, boardTree } = this.props
const { boardTree } = this.props
const { activeView: view } = boardTree
const filterGroup = new FilterGroup(view.filter)
@ -153,7 +152,7 @@ class FilterComponent extends React.Component<Props> {
}
private addFilterClicked() {
const { mutator, boardTree } = this.props
const { boardTree } = this.props
const { board, activeView: view } = boardTree
const filters = view.filter?.filters.filter(o => !FilterGroup.isAnInstanceOf(o)) as FilterClause[] || []

View file

@ -3,12 +3,11 @@ import { Archiver } from "../archiver"
import { Board } from "../board"
import { BoardTree } from "../boardTree"
import { Menu, MenuOption } from "../menu"
import { Mutator } from "../mutator"
import mutator from "../mutator"
import { IPageController } from "../octoTypes"
import { WorkspaceTree } from "../workspaceTree"
type Props = {
mutator: Mutator
showBoard: (id: string) => void
workspaceTree: WorkspaceTree,
boardTree?: BoardTree
@ -51,7 +50,7 @@ class Sidebar extends React.Component<Props> {
}
private showOptions(e: React.MouseEvent, board: Board) {
const { mutator, showBoard, workspaceTree } = this.props
const { showBoard, workspaceTree } = this.props
const { boards } = workspaceTree
const options: MenuOption[] = []
@ -79,8 +78,6 @@ class Sidebar extends React.Component<Props> {
}
private settingsClicked(e: React.MouseEvent) {
const { mutator } = this.props
Menu.shared.options = [
{ id: "import", name: "Import Archive" },
{ id: "export", name: "Export Archive" },
@ -88,13 +85,13 @@ class Sidebar extends React.Component<Props> {
Menu.shared.onMenuClicked = (optionId: string, type?: string) => {
switch (optionId) {
case "import": {
Archiver.importFullArchive(mutator, () => {
Archiver.importFullArchive(() => {
this.forceUpdate()
})
break
}
case "export": {
Archiver.exportFullArchive(mutator)
Archiver.exportFullArchive()
break
}
}
@ -112,7 +109,7 @@ class Sidebar extends React.Component<Props> {
}
async addBoardClicked() {
const { mutator, boardTree, showBoard } = this.props
const { boardTree, showBoard } = this.props
const oldBoardId = boardTree?.board?.id
const board = new Board()

View file

@ -10,7 +10,7 @@ import MenuWrapper from "../widgets/menuWrapper"
import Menu from "../widgets/menu"
import { CsvExporter } from "../csvExporter"
import { Menu as OldMenu } from "../menu"
import { Mutator } from "../mutator"
import mutator from "../mutator"
import { OctoUtils } from "../octoUtils"
import { Utils } from "../utils"
import Button from "./button"
@ -18,7 +18,6 @@ import { Editable } from "./editable"
import { TableRow } from "./tableRow"
type Props = {
mutator: Mutator,
boardTree?: BoardTree
showView: (id: string) => void
showCard: (card: Card) => void
@ -50,7 +49,7 @@ class TableComponent extends React.Component<Props, State> {
}
render() {
const { mutator, boardTree, showView } = this.props
const { boardTree, showView } = this.props
if (!boardTree || !boardTree.board) {
return (
@ -107,7 +106,6 @@ class TableComponent extends React.Component<Props, State> {
</div>
<ViewMenu
board={board}
mutator={mutator}
boardTree={boardTree}
showView={showView}
/>
@ -115,7 +113,7 @@ class TableComponent extends React.Component<Props, State> {
<div className="octo-spacer"></div>
<div className="octo-button" onClick={(e) => { this.propertiesClicked(e) }}>Properties</div>
<div className={hasFilter ? "octo-button active" : "octo-button"} onClick={(e) => { this.filterClicked(e) }}>Filter</div>
<div className={hasSort ? "octo-button active" : "octo-button"} onClick={(e) => { OctoUtils.showSortMenu(e, mutator, boardTree) }}>Sort</div>
<div className={hasSort ? "octo-button active" : "octo-button"} onClick={(e) => { OctoUtils.showSortMenu(e, boardTree) }}>Sort</div>
{this.state.isSearching
? <Editable
ref={this.searchFieldRef}
@ -186,7 +184,6 @@ class TableComponent extends React.Component<Props, State> {
const tableRow = <TableRow
key={card.id}
ref={tableRowRef}
mutator={mutator}
boardTree={boardTree}
card={card}
focusOnMount={focusOnMount}
@ -221,7 +218,7 @@ class TableComponent extends React.Component<Props, State> {
}
private async propertiesClicked(e: React.MouseEvent) {
const { mutator, boardTree } = this.props
const { boardTree } = this.props
const { activeView } = boardTree
const selectProperties = boardTree.board.cardProperties
@ -274,7 +271,7 @@ class TableComponent extends React.Component<Props, State> {
}
private async headerClicked(e: React.MouseEvent<HTMLDivElement>, templateId: string) {
const { mutator, boardTree } = this.props
const { boardTree } = this.props
const { board } = boardTree
const { activeView } = boardTree
@ -361,7 +358,7 @@ class TableComponent extends React.Component<Props, State> {
}
async addCard(show: boolean = false) {
const { mutator, boardTree } = this.props
const { boardTree } = this.props
const card = new Card()
card.parentId = boardTree.board.id
@ -383,7 +380,7 @@ class TableComponent extends React.Component<Props, State> {
const { draggedHeaderTemplate } = this
if (!draggedHeaderTemplate) { return }
const { mutator, boardTree } = this.props
const { boardTree } = this.props
const { board } = boardTree
Utils.assertValue(mutator)

View file

@ -1,12 +1,11 @@
import React from "react"
import { BoardTree } from "../boardTree"
import { Card } from "../card"
import { Mutator } from "../mutator"
import mutator from "../mutator"
import { OctoUtils } from "../octoUtils"
import { Editable } from "./editable"
type Props = {
mutator: Mutator
boardTree: BoardTree
card: Card
focusOnMount: boolean
@ -27,7 +26,7 @@ class TableRow extends React.Component<Props, State> {
}
render() {
const { mutator, boardTree, card, showCard, onKeyDown } = this.props
const { boardTree, card, showCard, onKeyDown } = this.props
const { board, activeView } = boardTree
const openButonRef = React.createRef<HTMLDivElement>()
@ -57,7 +56,7 @@ class TableRow extends React.Component<Props, State> {
.filter(template => activeView.visiblePropertyIds.includes(template.id))
.map(template => {
return <div className="octo-table-cell" key={template.id}>
{OctoUtils.propertyValueEditableElement(mutator, card, template)}
{OctoUtils.propertyValueEditableElement(card, template)}
</div>
})}
</div>

View file

@ -2,12 +2,11 @@ import React from "react"
import { Board } from "../board"
import { BoardTree } from "../boardTree"
import { BoardView } from "../boardView"
import { Mutator } from "../mutator"
import mutator from "../mutator"
import { Utils } from "../utils"
import Menu from "../widgets/menu"
type Props = {
mutator: Mutator,
boardTree?: BoardTree
board: Board,
showView: (id: string) => void
@ -15,7 +14,7 @@ type Props = {
export default class ViewMenu extends React.Component<Props> {
handleDeleteView = async (id: string) => {
const { board, boardTree, mutator, showView } = this.props
const { board, boardTree, showView } = this.props
Utils.log(`deleteView`)
const view = boardTree.activeView
const nextView = boardTree.views.find(o => o !== view)
@ -31,7 +30,7 @@ export default class ViewMenu extends React.Component<Props> {
}
handleAddViewBoard = async (id: string) => {
const { board, boardTree, mutator, showView } = this.props
const { board, boardTree, showView } = this.props
Utils.log(`addview-board`)
const view = new BoardView()
view.title = "Board View"
@ -48,7 +47,7 @@ export default class ViewMenu extends React.Component<Props> {
}
handleAddViewTable = async (id: string) => {
const { board, boardTree, mutator, showView } = this.props
const { board, boardTree, showView } = this.props
Utils.log(`addview-table`)
const view = new BoardView()

View file

@ -1,7 +1,6 @@
import React from "react"
import { BoardTree } from "../boardTree"
import { Card } from "../card"
import { Mutator } from "../mutator"
import { Utils } from "../utils"
import { WorkspaceTree } from "../workspaceTree"
import { BoardComponent } from "./boardComponent"
@ -9,7 +8,6 @@ import { Sidebar } from "./sidebar"
import { TableComponent } from "./tableComponent"
type Props = {
mutator: Mutator,
workspaceTree: WorkspaceTree
boardTree?: BoardTree
showBoard: (id: string) => void
@ -21,12 +19,12 @@ type Props = {
class WorkspaceComponent extends React.Component<Props> {
render() {
const { mutator, boardTree, workspaceTree, showBoard } = this.props
const { boardTree, workspaceTree, showBoard } = this.props
Utils.assert(workspaceTree)
const element =
<div className="octo-workspace">
<Sidebar mutator={mutator} showBoard={showBoard} workspaceTree={workspaceTree} boardTree={boardTree}></Sidebar>
<Sidebar showBoard={showBoard} workspaceTree={workspaceTree} boardTree={boardTree}></Sidebar>
{this.mainComponent()}
</div>
@ -34,7 +32,7 @@ class WorkspaceComponent extends React.Component<Props> {
}
private mainComponent() {
const { mutator, boardTree, showCard, showFilter, setSearchText, showView } = this.props
const { boardTree, showCard, showFilter, setSearchText, showView } = this.props
const { activeView } = boardTree || {}
if (!activeView) {
@ -43,11 +41,11 @@ class WorkspaceComponent extends React.Component<Props> {
switch (activeView?.viewType) {
case "board": {
return <BoardComponent mutator={mutator} boardTree={boardTree} showCard={showCard} showFilter={showFilter} setSearchText={setSearchText} showView={showView} />
return <BoardComponent boardTree={boardTree} showCard={showCard} showFilter={showFilter} setSearchText={setSearchText} showView={showView} />
}
case "table": {
return <TableComponent mutator={mutator} boardTree={boardTree} showCard={showCard} showFilter={showFilter} setSearchText={setSearchText} showView={showView} />
return <TableComponent boardTree={boardTree} showCard={showCard} showFilter={showFilter} setSearchText={setSearchText} showView={showView} />
}
default: {

View file

@ -4,9 +4,9 @@ import { BoardTree } from "./boardTree"
import { BoardView, ISortOption } from "./boardView"
import { Card } from "./card"
import { FilterGroup } from "./filterGroup"
import { OctoClient } from "./octoClient"
import octoClient from "./octoClient"
import { IBlock } from "./octoTypes"
import { UndoManager } from "./undomanager"
import undoManager from "./undomanager"
import { Utils } from "./utils"
//
@ -14,37 +14,33 @@ import { Utils } from "./utils"
// It also ensures that the Undo-manager is called for each action
//
class Mutator {
constructor(private octo: OctoClient, private undoManager = UndoManager.shared) {
constructor() {
}
async insertBlock(block: IBlock, description: string = "add", afterRedo?: () => Promise<void>, beforeUndo?: () => Promise<void>) {
const { octo, undoManager } = this
await undoManager.perform(
async () => {
await octo.insertBlock(block)
await octoClient.insertBlock(block)
await afterRedo?.()
},
async () => {
await beforeUndo?.()
await octo.deleteBlock(block.id)
await octoClient.deleteBlock(block.id)
},
description
)
}
async insertBlocks(blocks: IBlock[], description: string = "add", afterRedo?: () => Promise<void>, beforeUndo?: () => Promise<void>) {
const { octo, undoManager } = this
await undoManager.perform(
async () => {
await octo.insertBlocks(blocks)
await octoClient.insertBlocks(blocks)
await afterRedo?.()
},
async () => {
await beforeUndo?.()
for (const block of blocks) {
await octo.deleteBlock(block.id)
await octoClient.deleteBlock(block.id)
}
},
description
@ -52,8 +48,6 @@ class Mutator {
}
async deleteBlock(block: IBlock, description?: string, beforeRedo?: () => Promise<void>, afterUndo?: () => Promise<void>) {
const { octo, undoManager } = this
if (!description) {
description = `delete ${block.type}`
}
@ -61,10 +55,10 @@ class Mutator {
await undoManager.perform(
async () => {
await beforeRedo?.()
await octo.deleteBlock(block.id)
await octoClient.deleteBlock(block.id)
},
async () => {
await octo.insertBlock(block)
await octoClient.insertBlock(block)
await afterUndo?.()
},
description
@ -72,51 +66,45 @@ class Mutator {
}
async changeTitle(block: IBlock, title: string, description: string = "change title") {
const { octo, undoManager } = this
const oldValue = block.title
await undoManager.perform(
async () => {
block.title = title
await octo.updateBlock(block)
await octoClient.updateBlock(block)
},
async () => {
block.title = oldValue
await octo.updateBlock(block)
await octoClient.updateBlock(block)
},
description
)
}
async changeIcon(block: Card | Board, icon: string, description: string = "change icon") {
const { octo, undoManager } = this
const oldValue = block.icon
await undoManager.perform(
async () => {
block.icon = icon
await octo.updateBlock(block)
await octoClient.updateBlock(block)
},
async () => {
block.icon = oldValue
await octo.updateBlock(block)
await octoClient.updateBlock(block)
},
description
)
}
async changeOrder(block: IBlock, order: number, description: string = "change order") {
const { octo, undoManager } = this
const oldValue = block.order
await undoManager.perform(
async () => {
block.order = order
await octo.updateBlock(block)
await octoClient.updateBlock(block)
},
async () => {
block.order = oldValue
await octo.updateBlock(block)
await octoClient.updateBlock(block)
},
description
)
@ -125,7 +113,6 @@ class Mutator {
// Property Templates
async insertPropertyTemplate(boardTree: BoardTree, index: number = -1, template?: IPropertyTemplate) {
const { octo, undoManager } = this
const { board, activeView } = boardTree
if (index < 0) { index = board.cardProperties.length }
@ -156,17 +143,16 @@ class Mutator {
await undoManager.perform(
async () => {
await octo.updateBlocks(changedBlocks)
await octoClient.updateBlocks(changedBlocks)
},
async () => {
await octo.updateBlocks(oldBlocks)
await octoClient.updateBlocks(oldBlocks)
},
description
)
}
async duplicatePropertyTemplate(boardTree: BoardTree, propertyId: string) {
const { octo, undoManager } = this
const { board, activeView } = boardTree
const oldBlocks: IBlock[] = [new Board(board)]
@ -194,10 +180,10 @@ class Mutator {
await undoManager.perform(
async () => {
await octo.updateBlocks(changedBlocks)
await octoClient.updateBlocks(changedBlocks)
},
async () => {
await octo.updateBlocks(oldBlocks)
await octoClient.updateBlocks(oldBlocks)
},
description
)
@ -206,8 +192,6 @@ class Mutator {
}
async changePropertyTemplateOrder(board: Board, template: IPropertyTemplate, destIndex: number) {
const { octo, undoManager } = this
const templates = board.cardProperties
const oldValue = templates
const newValue = templates.slice()
@ -219,18 +203,17 @@ class Mutator {
await undoManager.perform(
async () => {
board.cardProperties = newValue
await octo.updateBlock(board)
await octoClient.updateBlock(board)
},
async () => {
board.cardProperties = oldValue
await octo.updateBlock(board)
await octoClient.updateBlock(board)
},
"reorder properties"
)
}
async deleteProperty(boardTree: BoardTree, propertyId: string) {
const { octo, undoManager } = this
const { board, views, cards } = boardTree
const oldBlocks: IBlock[] = [new Board(board)]
@ -255,18 +238,16 @@ class Mutator {
await undoManager.perform(
async () => {
await octo.updateBlocks(changedBlocks)
await octoClient.updateBlocks(changedBlocks)
},
async () => {
await octo.updateBlocks(oldBlocks)
await octoClient.updateBlocks(oldBlocks)
},
"delete property"
)
}
async renameProperty(board: Board, propertyId: string, name: string) {
const { octo, undoManager } = this
const oldBlocks: IBlock[] = [new Board(board)]
const changedBlocks: IBlock[] = [board]
@ -277,10 +258,10 @@ class Mutator {
await undoManager.perform(
async () => {
await octo.updateBlocks(changedBlocks)
await octoClient.updateBlocks(changedBlocks)
},
async () => {
await octo.updateBlocks(oldBlocks)
await octoClient.updateBlocks(oldBlocks)
},
"rename property"
)
@ -289,7 +270,6 @@ class Mutator {
// Properties
async insertPropertyOption(boardTree: BoardTree, template: IPropertyTemplate, option: IPropertyOption, description: string = "add option") {
const { octo, undoManager } = this
const { board } = boardTree
Utils.assert(board.cardProperties.includes(template))
@ -301,19 +281,18 @@ class Mutator {
await undoManager.perform(
async () => {
template.options = newValue
await octo.updateBlock(board)
await octoClient.updateBlock(board)
},
async () => {
// TODO: Also remove property on cards
template.options = oldValue
await octo.updateBlock(board)
await octoClient.updateBlock(board)
},
description
)
}
async deletePropertyOption(boardTree: BoardTree, template: IPropertyTemplate, option: IPropertyOption) {
const { octo, undoManager } = this
const { board } = boardTree
const oldValue = template.options.slice()
@ -324,19 +303,17 @@ class Mutator {
await undoManager.perform(
async () => {
template.options = newValue
await octo.updateBlock(board)
await octoClient.updateBlock(board)
},
async () => {
template.options = oldValue
await octo.updateBlock(board)
await octoClient.updateBlock(board)
},
"delete option"
)
}
async changePropertyOptionOrder(board: Board, template: IPropertyTemplate, option: IPropertyOption, destIndex: number) {
const { octo, undoManager } = this
const oldValue = template.options
const newValue = template.options.slice()
@ -347,18 +324,17 @@ class Mutator {
await undoManager.perform(
async () => {
template.options = newValue
await octo.updateBlock(board)
await octoClient.updateBlock(board)
},
async () => {
template.options = oldValue
await octo.updateBlock(board)
await octoClient.updateBlock(board)
},
"reorder options"
)
}
async changePropertyOptionValue(boardTree: BoardTree, propertyTemplate: IPropertyTemplate, option: IPropertyOption, value: string) {
const { octo, undoManager } = this
const { board, cards } = boardTree
const oldValue = option.value
@ -379,10 +355,10 @@ class Mutator {
await undoManager.perform(
async () => {
await octo.updateBlocks(changedBlocks)
await octoClient.updateBlocks(changedBlocks)
},
async () => {
await octo.updateBlocks(oldBlocks)
await octoClient.updateBlocks(oldBlocks)
},
"rename option"
)
@ -391,52 +367,46 @@ class Mutator {
}
async changePropertyOptionColor(board: Board, option: IPropertyOption, color: string) {
const { octo, undoManager } = this
const oldValue = option.color
undoManager.perform(
async () => {
option.color = color
await octo.updateBlock(board)
await octoClient.updateBlock(board)
},
async () => {
option.color = oldValue
await octo.updateBlock(board)
await octoClient.updateBlock(board)
},
"change option color"
)
}
async changePropertyValue(card: Card, propertyId: string, value?: string, description: string = "change property") {
const { octo, undoManager } = this
const oldValue = card.properties[propertyId]
await undoManager.perform(
async () => {
card.properties[propertyId] = value
await octo.updateBlock(card)
await octoClient.updateBlock(card)
},
async () => {
card.properties[propertyId] = oldValue
await octo.updateBlock(card)
await octoClient.updateBlock(card)
},
description
)
}
async changePropertyType(board: Board, propertyTemplate: IPropertyTemplate, type: PropertyType) {
const { octo, undoManager } = this
const oldValue = propertyTemplate.type
await undoManager.perform(
async () => {
propertyTemplate.type = type
await octo.updateBlock(board)
await octoClient.updateBlock(board)
},
async () => {
propertyTemplate.type = oldValue
await octo.updateBlock(board)
await octoClient.updateBlock(board)
},
"change property type"
)
@ -445,72 +415,64 @@ class Mutator {
// Views
async changeViewSortOptions(view: BoardView, sortOptions: ISortOption[]) {
const { octo, undoManager } = this
const oldValue = view.sortOptions
await undoManager.perform(
async () => {
view.sortOptions = sortOptions
await octo.updateBlock(view)
await octoClient.updateBlock(view)
},
async () => {
view.sortOptions = oldValue
await octo.updateBlock(view)
await octoClient.updateBlock(view)
},
"sort"
)
}
async changeViewFilter(view: BoardView, filter?: FilterGroup) {
const { octo, undoManager } = this
const oldValue = view.filter
await undoManager.perform(
async () => {
view.filter = filter
await octo.updateBlock(view)
await octoClient.updateBlock(view)
},
async () => {
view.filter = oldValue
await octo.updateBlock(view)
await octoClient.updateBlock(view)
},
"filter"
)
}
async changeViewVisibleProperties(view: BoardView, visiblePropertyIds: string[]) {
const { octo, undoManager } = this
const oldValue = view.visiblePropertyIds
await undoManager.perform(
async () => {
view.visiblePropertyIds = visiblePropertyIds
await octo.updateBlock(view)
await octoClient.updateBlock(view)
},
async () => {
view.visiblePropertyIds = oldValue
await octo.updateBlock(view)
await octoClient.updateBlock(view)
},
"hide / show property"
)
}
async changeViewGroupById(view: BoardView, groupById: string) {
const { octo, undoManager } = this
const oldValue = view.groupById
await undoManager.perform(
async () => {
view.groupById = groupById
await octo.updateBlock(view)
await octoClient.updateBlock(view)
},
async () => {
view.groupById = oldValue
await octo.updateBlock(view)
await octoClient.updateBlock(view)
},
"group by"
)
@ -518,18 +480,16 @@ class Mutator {
// Not a mutator, but convenient to put here since Mutator wraps OctoClient
async exportFullArchive() {
return this.octo.exportFullArchive()
return octoClient.exportFullArchive()
}
// Not a mutator, but convenient to put here since Mutator wraps OctoClient
async importFullArchive(blocks: IBlock[]) {
return this.octo.importFullArchive(blocks)
return octoClient.importFullArchive(blocks)
}
async createImageBlock(parentId: string, file: File, order = 1000): Promise<IBlock | undefined> {
const { octo, undoManager } = this
const url = await octo.uploadFile(file)
const url = await octoClient.uploadFile(file)
if (!url) {
return undefined
}
@ -539,16 +499,35 @@ class Mutator {
await undoManager.perform(
async () => {
await octo.insertBlock(block)
await octoClient.insertBlock(block)
},
async () => {
await octo.deleteBlock(block.id)
await octoClient.deleteBlock(block.id)
},
"group by"
)
return block
}
async undo() {
await undoManager.undo()
}
undoDescription(): string | undefined {
return undoManager.undoDescription
}
async redo() {
await undoManager.redo()
}
redoDescription(): string | undefined {
return undoManager.redoDescription
}
}
export { Mutator }
const mutator = new Mutator()
export default mutator
export { mutator }

View file

@ -152,4 +152,6 @@ class OctoClient {
}
}
export { OctoClient }
const client = new OctoClient()
export default client

View file

@ -5,7 +5,7 @@ import { ISortOption } from "./boardView"
import { Card } from "./card"
import { Editable } from "./components/editable"
import { Menu } from "./menu"
import { Mutator } from "./mutator"
import mutator from "./mutator"
import { IBlock } from "./octoTypes"
import { Utils } from "./utils"
@ -27,14 +27,14 @@ class OctoUtils {
}
static propertyValueReadonlyElement(card: Card, propertyTemplate: IPropertyTemplate, emptyDisplayValue: string = "Empty"): JSX.Element {
return this.propertyValueElement(undefined, card, propertyTemplate, emptyDisplayValue)
return this.propertyValueElement(true, card, propertyTemplate, emptyDisplayValue)
}
static propertyValueEditableElement(mutator: Mutator, card: Card, propertyTemplate: IPropertyTemplate, emptyDisplayValue?: string): JSX.Element {
return this.propertyValueElement(mutator, card, propertyTemplate, emptyDisplayValue)
static propertyValueEditableElement(card: Card, propertyTemplate: IPropertyTemplate, emptyDisplayValue?: string): JSX.Element {
return this.propertyValueElement(false, card, propertyTemplate, emptyDisplayValue)
}
private static propertyValueElement(mutator: Mutator | undefined, card: Card, propertyTemplate: IPropertyTemplate, emptyDisplayValue: string = "Empty"): JSX.Element {
private static propertyValueElement(readOnly: boolean, card: Card, propertyTemplate: IPropertyTemplate, emptyDisplayValue: string = "Empty"): JSX.Element {
const propertyValue = card.properties[propertyTemplate.id]
const displayValue = OctoUtils.propertyDisplayValue(card, propertyValue, propertyTemplate)
const finalDisplayValue = displayValue || emptyDisplayValue
@ -69,18 +69,18 @@ class OctoUtils {
key={propertyTemplate.id}
className={`${className} ${propertyColorCssClassName}`}
tabIndex={0}
onClick={mutator ? (e) => { showMenu(e.target as HTMLElement) } : undefined}
onKeyDown={mutator ? (e) => {
onClick={!readOnly ? (e) => { showMenu(e.target as HTMLElement) } : undefined}
onKeyDown={!readOnly ? (e) => {
if (e.keyCode === 13) {
showMenu(e.target as HTMLElement)
}
} : undefined}
onFocus={mutator ? () => { Menu.shared.hide() } : undefined}
onFocus={!readOnly ? () => { Menu.shared.hide() } : undefined}
>
{finalDisplayValue}
</div>
} else if (propertyTemplate.type === "text" || propertyTemplate.type === "number") {
if (mutator) {
if (!readOnly) {
element = <Editable
key={propertyTemplate.id}
className="octo-propertyvalue"
@ -121,7 +121,7 @@ class OctoUtils {
return (block.order + nextBlock.order) / 2
}
static showSortMenu(e: React.MouseEvent, mutator: Mutator, boardTree: BoardTree) {
static showSortMenu(e: React.MouseEvent, boardTree: BoardTree) {
const { activeView } = boardTree
const { sortOptions } = activeView
const sortOption = sortOptions.length > 0 ? sortOptions[0] : undefined

View file

@ -8,10 +8,9 @@ import { CardDialog } from "../components/cardDialog"
import { FilterComponent } from "../components/filterComponent"
import { WorkspaceComponent } from "../components/workspaceComponent"
import { FlashMessage } from "../flashMessage"
import { Mutator } from "../mutator"
import { OctoClient } from "../octoClient"
import mutator from "../mutator"
import octoClient from "../octoClient"
import { OctoListener } from "../octoListener"
import { UndoManager } from "../undomanager"
import { Utils } from "../utils"
import { WorkspaceTree } from "../workspaceTree"
@ -33,7 +32,6 @@ export default class BoardPage extends React.Component<Props, State> {
updateTitleTimeout: number
updatePropertyLabelTimeout: number
private octo = new OctoClient()
private boardListener = new OctoListener()
private cardListener = new OctoListener()
@ -46,7 +44,7 @@ export default class BoardPage extends React.Component<Props, State> {
this.state = {
boardId,
viewId,
workspaceTree: new WorkspaceTree(this.octo),
workspaceTree: new WorkspaceTree(),
}
Utils.log(`BoardPage. boardId: ${boardId}`)
@ -73,8 +71,8 @@ export default class BoardPage extends React.Component<Props, State> {
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()
const description = mutator.undoDescription()
await mutator.undo()
if (description) {
FlashMessage.show(`Undo ${description}`)
} else {
@ -82,8 +80,8 @@ export default class BoardPage extends React.Component<Props, State> {
}
} 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()
const description = mutator.redoDescription()
await mutator.redo()
if (description) {
FlashMessage.show(`Redo ${description}`)
} else {
@ -108,12 +106,11 @@ export default class BoardPage extends React.Component<Props, State> {
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 && shownCardTree) {
ReactDOM.render(
<CardDialog mutator={mutator} boardTree={this.state.boardTree} cardTree={shownCardTree} onClose={() => { this.showCard(undefined) }}></CardDialog>,
<CardDialog boardTree={this.state.boardTree} cardTree={shownCardTree} onClose={() => { this.showCard(undefined) }}></CardDialog>,
Utils.getElementById("overlay")
)
} else {
@ -137,7 +134,6 @@ export default class BoardPage extends React.Component<Props, State> {
ReactDOM.render(
<FilterComponent
mutator={mutator}
boardTree={this.state.boardTree}
pageX={pageX}
pageY={pageY}
@ -157,7 +153,6 @@ export default class BoardPage extends React.Component<Props, State> {
return (
<div className='BoardPage'>
<WorkspaceComponent
mutator={mutator}
workspaceTree={workspaceTree}
boardTree={this.state.boardTree}
showView={(id) => { this.showView(id) }}
@ -187,7 +182,7 @@ export default class BoardPage extends React.Component<Props, State> {
await workspaceTree.sync()
if (boardId) {
const boardTree = new BoardTree(this.octo, boardId)
const boardTree = new BoardTree(boardId)
await boardTree.sync()
// Default to first view
@ -214,7 +209,7 @@ export default class BoardPage extends React.Component<Props, State> {
this.cardListener.close()
if (card) {
const cardTree = new CardTree(this.octo, card.id)
const cardTree = new CardTree(card.id)
await cardTree.sync()
this.setState({...this.state, shownCardTree: cardTree})

View file

@ -2,10 +2,9 @@ import React from 'react'
import { Archiver } from "../archiver"
import { Board } from "../board"
import Button from '../components/button'
import { Mutator } from "../mutator"
import { OctoClient } from "../octoClient"
import mutator from '../mutator'
import octoClient from "../octoClient"
import { IBlock } from "../octoTypes"
import { UndoManager } from "../undomanager"
import { Utils } from "../utils"
type Props = {}
@ -27,29 +26,23 @@ export default class HomePage extends React.Component<Props, State> {
}
loadBoards = async () => {
const octo = new OctoClient()
const boards = await octo.getBlocks(null, "board")
const boards = await octoClient.getBlocks(null, "board")
this.setState({ boards })
}
importClicked = async () => {
const octo = new OctoClient()
const mutator = new Mutator(octo, UndoManager.shared)
Archiver.importFullArchive(mutator, () => {
Archiver.importFullArchive(() => {
this.loadBoards()
})
}
exportClicked = async () => {
const octo = new OctoClient()
const mutator = new Mutator(octo, UndoManager.shared)
Archiver.exportFullArchive(mutator)
Archiver.exportFullArchive()
}
addClicked = async () => {
const octo = new OctoClient()
const board = new Board()
await octo.insertBlock(board)
await octoClient.insertBlock(board)
}
render(): React.ReactNode {

View file

@ -9,8 +9,6 @@ interface UndoCommand {
// General-purpose undo manager
//
class UndoManager {
static shared = new UndoManager()
onStateDidChange?: () => void
private commands: UndoCommand[] = []
@ -164,4 +162,5 @@ class UndoManager {
}
}
export { UndoManager }
const undoManager = new UndoManager()
export default undoManager

View file

@ -1,16 +1,12 @@
import { Board } from "./board"
import { OctoClient } from "./octoClient"
import octoClient from "./octoClient"
import { IBlock } from "./octoTypes"
class WorkspaceTree {
boards: Board[] = []
constructor(
private octo: OctoClient) {
}
async sync() {
const blocks = await this.octo.getBlocks(undefined, "board")
const blocks = await octoClient.getBlocks(undefined, "board")
this.rebuild(blocks)
}