mirror of
https://github.com/mattermost/focalboard.git
synced 2024-11-24 08:22:29 +02:00
Immutable blocks
This commit is contained in:
parent
932de3a17f
commit
aa950240d9
@ -3,7 +3,21 @@
|
||||
import {IBlock} from '../octoTypes'
|
||||
import {Utils} from '../utils'
|
||||
|
||||
class Block implements IBlock {
|
||||
interface IMutableBlock extends IBlock {
|
||||
id: string
|
||||
parentId: string
|
||||
|
||||
schema: number
|
||||
type: string
|
||||
title?: string
|
||||
fields: Record<string, any>
|
||||
|
||||
createAt: number
|
||||
updateAt: number
|
||||
deleteAt: number
|
||||
}
|
||||
|
||||
class MutableBlock implements IMutableBlock {
|
||||
id: string = Utils.createGuid()
|
||||
schema: number
|
||||
parentId: string
|
||||
@ -14,10 +28,10 @@ class Block implements IBlock {
|
||||
updateAt = 0
|
||||
deleteAt = 0
|
||||
|
||||
static duplicate(block: IBlock) {
|
||||
static duplicate(block: IBlock): IBlock {
|
||||
const now = Date.now()
|
||||
|
||||
const newBlock = new Block(block)
|
||||
const newBlock = new MutableBlock(block)
|
||||
newBlock.id = Utils.createGuid()
|
||||
newBlock.title = `Copy of ${block.title}`
|
||||
newBlock.createAt = now
|
||||
@ -46,4 +60,4 @@ class Block implements IBlock {
|
||||
}
|
||||
}
|
||||
|
||||
export {Block}
|
||||
export {IMutableBlock, MutableBlock}
|
||||
|
@ -1,23 +1,41 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import {Block} from './block'
|
||||
import { IBlock } from '../octoTypes'
|
||||
import {MutableBlock} from './block'
|
||||
|
||||
type PropertyType = 'text' | 'number' | 'select' | 'multiSelect' | 'date' | 'person' | 'file' | 'checkbox' | 'url' | 'email' | 'phone' | 'createdTime' | 'createdBy' | 'updatedTime' | 'updatedBy'
|
||||
|
||||
interface IPropertyOption {
|
||||
readonly value: string,
|
||||
readonly color: string
|
||||
}
|
||||
|
||||
interface IMutablePropertyOption {
|
||||
value: string,
|
||||
color: string
|
||||
}
|
||||
|
||||
// A template for card properties attached to a board
|
||||
interface IPropertyTemplate {
|
||||
readonly id: string
|
||||
readonly name: string
|
||||
readonly type: PropertyType
|
||||
readonly options: IPropertyOption[]
|
||||
}
|
||||
|
||||
interface IMutablePropertyTemplate extends IPropertyTemplate {
|
||||
id: string
|
||||
name: string
|
||||
type: PropertyType
|
||||
options: IPropertyOption[]
|
||||
options: IMutablePropertyOption[]
|
||||
}
|
||||
|
||||
class Board extends Block {
|
||||
interface Board extends IBlock {
|
||||
readonly icon: string
|
||||
readonly cardProperties: readonly IPropertyTemplate[]
|
||||
}
|
||||
|
||||
class MutableBoard extends MutableBlock {
|
||||
get icon(): string {
|
||||
return this.fields.icon as string
|
||||
}
|
||||
@ -25,10 +43,10 @@ class Board extends Block {
|
||||
this.fields.icon = value
|
||||
}
|
||||
|
||||
get cardProperties(): IPropertyTemplate[] {
|
||||
get cardProperties(): IMutablePropertyTemplate[] {
|
||||
return this.fields.cardProperties as IPropertyTemplate[]
|
||||
}
|
||||
set cardProperties(value: IPropertyTemplate[]) {
|
||||
set cardProperties(value: IMutablePropertyTemplate[]) {
|
||||
this.fields.cardProperties = value
|
||||
}
|
||||
|
||||
@ -52,4 +70,4 @@ class Board extends Block {
|
||||
}
|
||||
}
|
||||
|
||||
export {Board, PropertyType, IPropertyOption, IPropertyTemplate}
|
||||
export {Board, MutableBoard, PropertyType, IPropertyOption, IPropertyTemplate}
|
||||
|
@ -1,13 +1,22 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import { IBlock } from '../octoTypes'
|
||||
import {FilterGroup} from '../filterGroup'
|
||||
|
||||
import {Block} from './block'
|
||||
import {MutableBlock} from './block'
|
||||
|
||||
type IViewType = 'board' | 'table' | 'calendar' | 'list' | 'gallery'
|
||||
type ISortOption = { propertyId: '__name' | string, reversed: boolean }
|
||||
|
||||
class BoardView extends Block {
|
||||
interface BoardView extends IBlock {
|
||||
readonly viewType: IViewType
|
||||
readonly groupById: string
|
||||
readonly sortOptions: readonly ISortOption[]
|
||||
readonly visiblePropertyIds: readonly string[]
|
||||
readonly filter: FilterGroup | undefined
|
||||
}
|
||||
|
||||
class MutableBoardView extends MutableBlock {
|
||||
get viewType(): IViewType {
|
||||
return this.fields.viewType
|
||||
}
|
||||
@ -67,4 +76,4 @@ class BoardView extends Block {
|
||||
}
|
||||
}
|
||||
|
||||
export {BoardView, IViewType, ISortOption}
|
||||
export {BoardView, MutableBoardView, IViewType, ISortOption}
|
||||
|
@ -1,8 +1,15 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import {Block} from './block'
|
||||
import {IBlock} from '../octoTypes'
|
||||
|
||||
class Card extends Block {
|
||||
import {MutableBlock} from './block'
|
||||
|
||||
interface Card extends IBlock {
|
||||
readonly icon: string
|
||||
readonly properties: Readonly<Record<string, string>>
|
||||
}
|
||||
|
||||
class MutableCard extends MutableBlock {
|
||||
get icon(): string {
|
||||
return this.fields.icon as string
|
||||
}
|
||||
@ -25,4 +32,4 @@ class Card extends Block {
|
||||
}
|
||||
}
|
||||
|
||||
export {Card}
|
||||
export {MutableCard, Card}
|
||||
|
@ -1,12 +1,16 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import {Block} from './block'
|
||||
import { IBlock } from '../octoTypes'
|
||||
import {MutableBlock} from './block'
|
||||
|
||||
class CommentBlock extends Block {
|
||||
interface CommentBlock extends IBlock {
|
||||
}
|
||||
|
||||
class MutableCommentBlock extends MutableBlock {
|
||||
constructor(block: any = {}) {
|
||||
super(block)
|
||||
this.type = 'comment'
|
||||
}
|
||||
}
|
||||
|
||||
export {CommentBlock}
|
||||
export {CommentBlock, MutableCommentBlock}
|
||||
|
@ -1,10 +1,12 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import {IOrderedBlock} from '../octoTypes'
|
||||
import { IOrderedBlock, MutableOrderedBlock } from './orderedBlock'
|
||||
|
||||
import {Block} from './block'
|
||||
interface ImageBlock extends IOrderedBlock {
|
||||
|
||||
class ImageBlock extends Block implements IOrderedBlock {
|
||||
}
|
||||
|
||||
class MutableImageBlock extends MutableOrderedBlock implements IOrderedBlock {
|
||||
get order(): number {
|
||||
return this.fields.order as number
|
||||
}
|
||||
@ -25,4 +27,4 @@ class ImageBlock extends Block implements IOrderedBlock {
|
||||
}
|
||||
}
|
||||
|
||||
export {ImageBlock}
|
||||
export {ImageBlock, MutableImageBlock}
|
||||
|
21
webapp/src/blocks/orderedBlock.ts
Normal file
21
webapp/src/blocks/orderedBlock.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { IBlock } from "../octoTypes"
|
||||
import { MutableBlock } from "./block"
|
||||
|
||||
interface IOrderedBlock extends IBlock {
|
||||
readonly order: number
|
||||
}
|
||||
|
||||
class MutableOrderedBlock extends MutableBlock implements IOrderedBlock {
|
||||
get order(): number {
|
||||
return this.fields.order as number
|
||||
}
|
||||
set order(value: number) {
|
||||
this.fields.order = value
|
||||
}
|
||||
|
||||
constructor(block: any = {}) {
|
||||
super(block)
|
||||
}
|
||||
}
|
||||
|
||||
export {IOrderedBlock, MutableOrderedBlock}
|
@ -1,10 +1,12 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import {IOrderedBlock} from '../octoTypes'
|
||||
import { IOrderedBlock, MutableOrderedBlock } from './orderedBlock'
|
||||
|
||||
import {Block} from './block'
|
||||
interface TextBlock extends IOrderedBlock {
|
||||
|
||||
class TextBlock extends Block implements IOrderedBlock {
|
||||
}
|
||||
|
||||
class MutableTextBlock extends MutableOrderedBlock {
|
||||
get order(): number {
|
||||
return this.fields.order as number
|
||||
}
|
||||
@ -18,4 +20,4 @@ class TextBlock extends Block implements IOrderedBlock {
|
||||
}
|
||||
}
|
||||
|
||||
export {TextBlock}
|
||||
export {TextBlock, MutableTextBlock}
|
||||
|
@ -1,8 +1,8 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import {Block} from './blocks/block'
|
||||
import {Board, IPropertyOption, IPropertyTemplate} from './blocks/board'
|
||||
import {BoardView} from './blocks/boardView'
|
||||
import {MutableBlock} from './blocks/block'
|
||||
import {Board, IPropertyOption, IPropertyTemplate, MutableBoard} from './blocks/board'
|
||||
import {BoardView, MutableBoardView} from './blocks/boardView'
|
||||
import {Card} from './blocks/card'
|
||||
import {CardFilter} from './cardFilter'
|
||||
import octoClient from './octoClient'
|
||||
@ -28,12 +28,12 @@ interface BoardTree {
|
||||
|
||||
class MutableBoardTree implements BoardTree {
|
||||
board!: Board
|
||||
views: BoardView[] = []
|
||||
views: MutableBoardView[] = []
|
||||
cards: Card[] = []
|
||||
emptyGroupCards: Card[] = []
|
||||
groups: Group[] = []
|
||||
|
||||
activeView?: BoardView
|
||||
activeView?: MutableBoardView
|
||||
groupByProperty?: IPropertyTemplate
|
||||
|
||||
private searchText?: string
|
||||
@ -50,9 +50,9 @@ class MutableBoardTree implements BoardTree {
|
||||
this.rebuild(OctoUtils.hydrateBlocks(blocks))
|
||||
}
|
||||
|
||||
private rebuild(blocks: Block[]) {
|
||||
private rebuild(blocks: IBlock[]) {
|
||||
this.board = blocks.find(block => block.type === "board") as Board
|
||||
this.views = blocks.filter(block => block.type === "view") as BoardView[]
|
||||
this.views = blocks.filter(block => block.type === "view") as MutableBoardView[]
|
||||
this.allCards = blocks.filter(block => block.type === "card") as Card[]
|
||||
this.cards = []
|
||||
|
||||
@ -67,19 +67,21 @@ class MutableBoardTree implements BoardTree {
|
||||
// At least one select property
|
||||
const selectProperties = board.cardProperties.find(o => o.type === "select")
|
||||
if (!selectProperties) {
|
||||
const newBoard = new MutableBoard(board)
|
||||
const property: IPropertyTemplate = {
|
||||
id: Utils.createGuid(),
|
||||
name: "Status",
|
||||
type: "select",
|
||||
options: []
|
||||
}
|
||||
board.cardProperties.push(property)
|
||||
newBoard.cardProperties.push(property)
|
||||
this.board = newBoard
|
||||
didChange = true
|
||||
}
|
||||
|
||||
// At least one view
|
||||
if (this.views.length < 1) {
|
||||
const view = new BoardView()
|
||||
const view = new MutableBoardView()
|
||||
view.parentId = board.id
|
||||
view.groupById = board.cardProperties.find(o => o.type === "select")?.id
|
||||
this.views.push(view)
|
||||
|
@ -7,11 +7,11 @@ import {FilterGroup} from './filterGroup'
|
||||
import {Utils} from './utils'
|
||||
|
||||
class CardFilter {
|
||||
static applyFilterGroup(filterGroup: FilterGroup, templates: IPropertyTemplate[], cards: Card[]): Card[] {
|
||||
static applyFilterGroup(filterGroup: FilterGroup, templates: readonly IPropertyTemplate[], cards: Card[]): Card[] {
|
||||
return cards.filter((card) => this.isFilterGroupMet(filterGroup, templates, card))
|
||||
}
|
||||
|
||||
static isFilterGroupMet(filterGroup: FilterGroup, templates: IPropertyTemplate[], card: Card): boolean {
|
||||
static isFilterGroupMet(filterGroup: FilterGroup, templates: readonly IPropertyTemplate[], card: Card): boolean {
|
||||
const {filters} = filterGroup
|
||||
|
||||
if (filterGroup.filters.length < 1) {
|
||||
@ -43,7 +43,7 @@ class CardFilter {
|
||||
return true
|
||||
}
|
||||
|
||||
static isClauseMet(filter: FilterClause, templates: IPropertyTemplate[], card: Card): boolean {
|
||||
static isClauseMet(filter: FilterClause, templates: readonly IPropertyTemplate[], card: Card): boolean {
|
||||
const value = card.properties[filter.propertyId]
|
||||
switch (filter.condition) {
|
||||
case 'includes': {
|
||||
@ -71,7 +71,7 @@ class CardFilter {
|
||||
return true
|
||||
}
|
||||
|
||||
static propertiesThatMeetFilterGroup(filterGroup: FilterGroup, templates: IPropertyTemplate[]): Record<string, string> {
|
||||
static propertiesThatMeetFilterGroup(filterGroup: FilterGroup, templates: readonly IPropertyTemplate[]): Record<string, string> {
|
||||
// TODO: Handle filter groups
|
||||
const filters = filterGroup.filters.filter((o) => !FilterGroup.isAnInstanceOf(o))
|
||||
if (filters.length < 1) {
|
||||
@ -93,7 +93,7 @@ class CardFilter {
|
||||
return result
|
||||
}
|
||||
|
||||
static propertyThatMeetsFilterClause(filterClause: FilterClause, templates: IPropertyTemplate[]): { id: string, value?: string } {
|
||||
static propertyThatMeetsFilterClause(filterClause: FilterClause, templates: readonly IPropertyTemplate[]): { id: string, value?: string } {
|
||||
const template = templates.find((o) => o.id === filterClause.propertyId)
|
||||
switch (filterClause.condition) {
|
||||
case 'includes': {
|
||||
|
@ -1,9 +1,9 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import {Block} from './blocks/block'
|
||||
import {Card} from './blocks/card'
|
||||
import { IOrderedBlock } from './blocks/orderedBlock'
|
||||
import octoClient from './octoClient'
|
||||
import {IBlock, IOrderedBlock} from './octoTypes'
|
||||
import {IBlock} from './octoTypes'
|
||||
import {OctoUtils} from './octoUtils'
|
||||
|
||||
interface CardTree {
|
||||
@ -25,7 +25,7 @@ class MutableCardTree implements CardTree {
|
||||
this.rebuild(OctoUtils.hydrateBlocks(blocks))
|
||||
}
|
||||
|
||||
private rebuild(blocks: Block[]) {
|
||||
private rebuild(blocks: IBlock[]) {
|
||||
this.card = blocks.find(o => o.id === this.cardId) as Card
|
||||
|
||||
this.comments = blocks
|
||||
|
@ -1,8 +1,8 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React from 'react'
|
||||
import { MutableBlock } from '../blocks/block'
|
||||
|
||||
import {Block} from '../blocks/block'
|
||||
import {IPropertyTemplate} from '../blocks/board'
|
||||
import {Card} from '../blocks/card'
|
||||
import {Menu} from '../menu'
|
||||
@ -89,7 +89,7 @@ class BoardCard extends React.Component<BoardCardProps, BoardCardState> {
|
||||
break
|
||||
}
|
||||
case 'duplicate': {
|
||||
const newCard = Block.duplicate(card)
|
||||
const newCard = MutableBlock.duplicate(card)
|
||||
mutator.insertBlock(newCard, 'duplicate card')
|
||||
break
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import React from 'react'
|
||||
import {Archiver} from '../archiver'
|
||||
import {BlockIcons} from '../blockIcons'
|
||||
import {IPropertyOption} from '../blocks/board'
|
||||
import {Card} from '../blocks/card'
|
||||
import {Card, MutableCard} from '../blocks/card'
|
||||
import {BoardTree} from '../boardTree'
|
||||
import {CardFilter} from '../cardFilter'
|
||||
import ViewMenu from '../components/viewMenu'
|
||||
@ -291,7 +291,7 @@ class BoardComponent extends React.Component<Props, State> {
|
||||
key={color.id}
|
||||
id={color.id}
|
||||
name={color.name}
|
||||
onClick={() => mutator.changePropertyOptionColor(boardTree.board, group.option, color.id)}
|
||||
onClick={() => mutator.changePropertyOptionColor(boardTree.board, boardTree.groupByProperty, group.option, color.id)}
|
||||
/>),
|
||||
)}
|
||||
</Menu>
|
||||
@ -393,7 +393,7 @@ class BoardComponent extends React.Component<Props, State> {
|
||||
const {boardTree} = this.props
|
||||
const {activeView, board} = boardTree
|
||||
|
||||
const card = new Card()
|
||||
const card = new MutableCard()
|
||||
card.parentId = boardTree.board.id
|
||||
card.properties = CardFilter.propertiesThatMeetFilterGroup(activeView.filter, board.cardProperties)
|
||||
card.icon = BlockIcons.shared.randomIcon()
|
||||
@ -458,7 +458,7 @@ class BoardComponent extends React.Component<Props, State> {
|
||||
let optionIndex = 0
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
const card = new Card()
|
||||
const card = new MutableCard()
|
||||
card.parentId = boardTree.board.id
|
||||
card.properties = CardFilter.propertiesThatMeetFilterGroup(activeView.filter, board.cardProperties)
|
||||
if (boardTree.groupByProperty && boardTree.groupByProperty.options.length > 0) {
|
||||
|
@ -1,28 +1,28 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React from 'react'
|
||||
|
||||
import { BlockIcons } from '../blockIcons'
|
||||
import {Block} from '../blocks/block'
|
||||
import {Card} from '../blocks/card'
|
||||
import {TextBlock} from '../blocks/textBlock'
|
||||
import { MutableCommentBlock } from '../blocks/commentBlock'
|
||||
import { IOrderedBlock } from '../blocks/orderedBlock'
|
||||
import { MutableTextBlock } from '../blocks/textBlock'
|
||||
import { BoardTree } from '../boardTree'
|
||||
import { CardTree, MutableCardTree } from '../cardTree'
|
||||
import { Menu as OldMenu, MenuOption } from '../menu'
|
||||
import mutator from '../mutator'
|
||||
import { OctoListener } from '../octoListener'
|
||||
import {IBlock, IOrderedBlock} from '../octoTypes'
|
||||
import { IBlock } from '../octoTypes'
|
||||
import { OctoUtils } from '../octoUtils'
|
||||
import { PropertyMenu } from '../propertyMenu'
|
||||
import { Utils } from '../utils'
|
||||
|
||||
import Button from './button'
|
||||
import { Editable } from './editable'
|
||||
import { MarkdownEditor } from './markdownEditor'
|
||||
|
||||
|
||||
|
||||
type Props = {
|
||||
boardTree: BoardTree
|
||||
card: Card
|
||||
cardId: string
|
||||
}
|
||||
|
||||
type State = {
|
||||
@ -42,13 +42,14 @@ export default class CardDetail extends React.Component<Props, State> {
|
||||
|
||||
componentDidMount() {
|
||||
this.cardListener = new OctoListener()
|
||||
this.cardListener.open(this.props.card.id, async () => {
|
||||
this.cardListener.open(this.props.cardId, async (blockId) => {
|
||||
Utils.log(`cardListener.onChanged: ${blockId}`)
|
||||
await cardTree.sync()
|
||||
this.setState({cardTree})
|
||||
this.setState({...this.state, cardTree})
|
||||
})
|
||||
const cardTree = new MutableCardTree(this.props.card.id)
|
||||
const cardTree = new MutableCardTree(this.props.cardId)
|
||||
cardTree.sync().then(() => {
|
||||
this.setState({cardTree})
|
||||
this.setState({...this.state, cardTree})
|
||||
setTimeout(() => {
|
||||
if (this.titleRef.current) {
|
||||
this.titleRef.current.focus()
|
||||
@ -58,13 +59,13 @@ export default class CardDetail extends React.Component<Props, State> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const {boardTree, card} = this.props
|
||||
const {boardTree} = this.props
|
||||
const {cardTree} = this.state
|
||||
const {board} = boardTree
|
||||
if (!cardTree) {
|
||||
return null
|
||||
}
|
||||
const {comments} = cardTree
|
||||
const {card, comments} = cardTree
|
||||
|
||||
const newCommentPlaceholderText = 'Add a comment...'
|
||||
|
||||
@ -133,8 +134,10 @@ export default class CardDetail 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 MutableTextBlock()
|
||||
block.parentId = card.id
|
||||
block.title = text
|
||||
block.order = cardTree.contents.length * 1000
|
||||
mutator.insertBlock(block, 'add card text')
|
||||
}}
|
||||
/>
|
||||
@ -363,8 +366,9 @@ export default class CardDetail extends React.Component<Props, State> {
|
||||
OldMenu.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 MutableTextBlock()
|
||||
block.parentId = card.id
|
||||
block.order = cardTree.contents.length * 1000
|
||||
await mutator.insertBlock(block, 'add text')
|
||||
break
|
||||
case 'image':
|
||||
@ -386,17 +390,17 @@ export default class CardDetail extends React.Component<Props, State> {
|
||||
}
|
||||
|
||||
async sendComment(text: string) {
|
||||
const {card} = this.props
|
||||
const {cardId} = this.props
|
||||
|
||||
Utils.assertValue(card)
|
||||
Utils.assertValue(cardId)
|
||||
|
||||
const block = new Block({type: 'comment', parentId: card.id, title: text})
|
||||
const block = new MutableCommentBlock({parentId: cardId, title: text})
|
||||
await mutator.insertBlock(block, 'add comment')
|
||||
}
|
||||
|
||||
private showContentBlockMenu(e: React.MouseEvent, block: IOrderedBlock) {
|
||||
const {cardTree} = this.state
|
||||
const {card} = this.props
|
||||
const {cardId} = this.props
|
||||
const index = cardTree.contents.indexOf(block)
|
||||
|
||||
const options: MenuOption[] = []
|
||||
@ -440,7 +444,8 @@ export default class CardDetail extends React.Component<Props, State> {
|
||||
break
|
||||
}
|
||||
case 'insertAbove-text': {
|
||||
const newBlock = new TextBlock({parentId: card.id})
|
||||
const newBlock = new MutableTextBlock()
|
||||
newBlock.parentId = cardId
|
||||
|
||||
// TODO: Handle need to reorder all blocks
|
||||
newBlock.order = OctoUtils.getOrderBefore(block, cardTree.contents)
|
||||
@ -451,7 +456,7 @@ export default class CardDetail extends React.Component<Props, State> {
|
||||
case 'insertAbove-image': {
|
||||
Utils.selectLocalFile(
|
||||
(file) => {
|
||||
mutator.createImageBlock(card.id, file, OctoUtils.getOrderBefore(block, cardTree.contents))
|
||||
mutator.createImageBlock(cardId, file, OctoUtils.getOrderBefore(block, cardTree.contents))
|
||||
},
|
||||
'.jpg,.jpeg,.png')
|
||||
|
||||
@ -467,7 +472,8 @@ export default class CardDetail extends React.Component<Props, State> {
|
||||
}
|
||||
|
||||
private iconClicked(e: React.MouseEvent) {
|
||||
const {card} = this.props
|
||||
const {cardTree} = this.state
|
||||
const {card} = cardTree
|
||||
|
||||
OldMenu.shared.options = [
|
||||
{id: 'random', name: 'Random'},
|
||||
|
@ -37,7 +37,7 @@ class CardDialog extends React.Component<Props> {
|
||||
>
|
||||
<CardDetail
|
||||
boardTree={this.props.boardTree}
|
||||
card={this.props.card}
|
||||
cardId={this.props.card.id}
|
||||
/>
|
||||
</Dialog>
|
||||
)
|
||||
|
@ -1,15 +1,15 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React from 'react'
|
||||
|
||||
import { Archiver } from '../archiver'
|
||||
import {Board} from '../blocks/board'
|
||||
import { Board, MutableBoard } from '../blocks/board'
|
||||
import { BoardTree } from '../boardTree'
|
||||
import mutator from '../mutator'
|
||||
import Menu from '../widgets/menu'
|
||||
import MenuWrapper from '../widgets/menuWrapper'
|
||||
import mutator from '../mutator'
|
||||
import { WorkspaceTree } from '../workspaceTree'
|
||||
|
||||
|
||||
type Props = {
|
||||
showBoard: (id: string) => void
|
||||
workspaceTree: WorkspaceTree,
|
||||
@ -109,7 +109,7 @@ class Sidebar extends React.Component<Props> {
|
||||
const {boardTree, showBoard} = this.props
|
||||
|
||||
const oldBoardId = boardTree?.board?.id
|
||||
const board = new Board()
|
||||
const board = new MutableBoard()
|
||||
await mutator.insertBlock(
|
||||
board,
|
||||
'add board',
|
||||
@ -127,3 +127,4 @@ class Sidebar extends React.Component<Props> {
|
||||
}
|
||||
|
||||
export { Sidebar }
|
||||
|
||||
|
@ -5,7 +5,7 @@ import React from 'react'
|
||||
import {Archiver} from '../archiver'
|
||||
import {BlockIcons} from '../blockIcons'
|
||||
import {IPropertyTemplate} from '../blocks/board'
|
||||
import {Card} from '../blocks/card'
|
||||
import {Card, MutableCard} from '../blocks/card'
|
||||
import {BoardTree} from '../boardTree'
|
||||
import ViewMenu from '../components/viewMenu'
|
||||
import {CsvExporter} from '../csvExporter'
|
||||
@ -451,7 +451,7 @@ class TableComponent extends React.Component<Props, State> {
|
||||
async addCard(show = false) {
|
||||
const {boardTree} = this.props
|
||||
|
||||
const card = new Card()
|
||||
const card = new MutableCard()
|
||||
card.parentId = boardTree.board.id
|
||||
card.icon = BlockIcons.shared.randomIcon()
|
||||
await mutator.insertBlock(
|
||||
|
@ -3,7 +3,7 @@
|
||||
import React from 'react'
|
||||
|
||||
import {Board} from '../blocks/board'
|
||||
import {BoardView} from '../blocks/boardView'
|
||||
import {BoardView, MutableBoardView} from '../blocks/boardView'
|
||||
import {BoardTree} from '../boardTree'
|
||||
import mutator from '../mutator'
|
||||
import {Utils} from '../utils'
|
||||
@ -35,7 +35,7 @@ export default class ViewMenu extends React.Component<Props> {
|
||||
handleAddViewBoard = async (id: string) => {
|
||||
const {board, boardTree, showView} = this.props
|
||||
Utils.log('addview-board')
|
||||
const view = new BoardView()
|
||||
const view = new MutableBoardView()
|
||||
view.title = 'Board View'
|
||||
view.viewType = 'board'
|
||||
view.parentId = board.id
|
||||
@ -57,7 +57,7 @@ export default class ViewMenu extends React.Component<Props> {
|
||||
const {board, boardTree, showView} = this.props
|
||||
|
||||
Utils.log('addview-table')
|
||||
const view = new BoardView()
|
||||
const view = new MutableBoardView()
|
||||
view.title = 'Table View'
|
||||
view.viewType = 'table'
|
||||
view.parentId = board.id
|
||||
|
@ -1,14 +1,15 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
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 {MutableBlock} from './blocks/block'
|
||||
import {Board, IPropertyOption, IPropertyTemplate, MutableBoard, PropertyType} from './blocks/board'
|
||||
import {BoardView, ISortOption, MutableBoardView} from './blocks/boardView'
|
||||
import {Card, MutableCard} from './blocks/card'
|
||||
import {MutableImageBlock} from './blocks/imageBlock'
|
||||
import {IOrderedBlock, MutableOrderedBlock} from './blocks/orderedBlock'
|
||||
import {BoardTree} from './boardTree'
|
||||
import {FilterGroup} from './filterGroup'
|
||||
import octoClient from './octoClient'
|
||||
import {IBlock, IOrderedBlock} from './octoTypes'
|
||||
import {IBlock} from './octoTypes'
|
||||
import undoManager from './undomanager'
|
||||
import {Utils} from './utils'
|
||||
|
||||
@ -67,13 +68,15 @@ class Mutator {
|
||||
|
||||
async changeTitle(block: IBlock, title: string, description = 'change title') {
|
||||
const oldValue = block.title
|
||||
|
||||
const newBlock = new MutableBlock(block)
|
||||
newBlock.title = title
|
||||
|
||||
await undoManager.perform(
|
||||
async () => {
|
||||
block.title = title
|
||||
await octoClient.updateBlock(block)
|
||||
await octoClient.updateBlock(newBlock)
|
||||
},
|
||||
async () => {
|
||||
block.title = oldValue
|
||||
await octoClient.updateBlock(block)
|
||||
},
|
||||
description,
|
||||
@ -81,14 +84,27 @@ class Mutator {
|
||||
}
|
||||
|
||||
async changeIcon(block: Card | Board, icon: string, description = 'change icon') {
|
||||
const oldValue = block.icon
|
||||
var newBlock: IBlock
|
||||
switch (block.type) {
|
||||
case 'card': {
|
||||
const card = new MutableCard(block)
|
||||
card.icon = icon
|
||||
newBlock = card
|
||||
break
|
||||
}
|
||||
case 'board': {
|
||||
const board = new MutableBoard(block)
|
||||
board.icon = icon
|
||||
newBlock = board
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
await undoManager.perform(
|
||||
async () => {
|
||||
block.icon = icon
|
||||
await octoClient.updateBlock(block)
|
||||
await octoClient.updateBlock(newBlock)
|
||||
},
|
||||
async () => {
|
||||
block.icon = oldValue
|
||||
await octoClient.updateBlock(block)
|
||||
},
|
||||
description,
|
||||
@ -96,14 +112,14 @@ class Mutator {
|
||||
}
|
||||
|
||||
async changeOrder(block: IOrderedBlock, order: number, description = 'change order') {
|
||||
const oldValue = block.order
|
||||
const newBlock = new MutableOrderedBlock(block)
|
||||
newBlock.order = order
|
||||
|
||||
await undoManager.perform(
|
||||
async () => {
|
||||
block.order = order
|
||||
await octoClient.updateBlock(block)
|
||||
await octoClient.updateBlock(newBlock)
|
||||
},
|
||||
async () => {
|
||||
block.order = oldValue
|
||||
await octoClient.updateBlock(block)
|
||||
},
|
||||
description,
|
||||
@ -128,17 +144,20 @@ class Mutator {
|
||||
}
|
||||
}
|
||||
|
||||
const oldBlocks: IBlock[] = [new Board(board)]
|
||||
const oldBlocks: IBlock[] = [board]
|
||||
|
||||
const changedBlocks: IBlock[] = [board]
|
||||
board.cardProperties.splice(index, 0, template)
|
||||
const newBoard = new MutableBoard(board)
|
||||
newBoard.cardProperties.splice(index, 0, template)
|
||||
const changedBlocks: IBlock[] = [newBoard]
|
||||
|
||||
let description = 'add property'
|
||||
|
||||
if (activeView.viewType === 'table') {
|
||||
oldBlocks.push(new BoardView(activeView))
|
||||
activeView.visiblePropertyIds.push(template.id)
|
||||
changedBlocks.push(activeView)
|
||||
oldBlocks.push(activeView)
|
||||
|
||||
const newActiveView = new MutableBoardView(activeView)
|
||||
newActiveView.visiblePropertyIds.push(template.id)
|
||||
changedBlocks.push(newActiveView)
|
||||
|
||||
description = 'add column'
|
||||
}
|
||||
@ -154,30 +173,34 @@ class Mutator {
|
||||
)
|
||||
}
|
||||
|
||||
async duplicatePropertyTemplate(boardTree: BoardTree, propertyId: string) {
|
||||
async duplicatePropertyTemplate(boardTree: BoardTree, propertyId: string): Promise<IBlock[]> {
|
||||
const {board, activeView} = boardTree
|
||||
|
||||
const oldBlocks: IBlock[] = [new Board(board)]
|
||||
const oldBlocks: IBlock[] = [board]
|
||||
|
||||
const changedBlocks: IBlock[] = [board]
|
||||
const index = board.cardProperties.findIndex((o) => o.id === propertyId)
|
||||
const newBoard = new MutableBoard(board)
|
||||
const changedBlocks: IBlock[] = [newBoard]
|
||||
const index = newBoard.cardProperties.findIndex((o) => o.id === propertyId)
|
||||
if (index === -1) {
|
||||
Utils.assertFailure(`Cannot find template with id: ${propertyId}`); return
|
||||
Utils.assertFailure(`Cannot find template with id: ${propertyId}`)
|
||||
return
|
||||
}
|
||||
const srcTemplate = board.cardProperties[index]
|
||||
const srcTemplate = newBoard.cardProperties[index]
|
||||
const newTemplate: IPropertyTemplate = {
|
||||
id: Utils.createGuid(),
|
||||
name: `Copy of ${srcTemplate.name}`,
|
||||
type: srcTemplate.type,
|
||||
options: srcTemplate.options.slice(),
|
||||
}
|
||||
board.cardProperties.splice(index + 1, 0, newTemplate)
|
||||
newBoard.cardProperties.splice(index + 1, 0, newTemplate)
|
||||
|
||||
let description = 'duplicate property'
|
||||
if (activeView.viewType === 'table') {
|
||||
oldBlocks.push(new BoardView(activeView))
|
||||
activeView.visiblePropertyIds.push(newTemplate.id)
|
||||
changedBlocks.push(activeView)
|
||||
oldBlocks.push(activeView)
|
||||
|
||||
const newActiveView = new MutableBoardView(activeView)
|
||||
newActiveView.visiblePropertyIds.push(newTemplate.id)
|
||||
changedBlocks.push(newActiveView)
|
||||
|
||||
description = 'duplicate column'
|
||||
}
|
||||
@ -204,13 +227,14 @@ class Mutator {
|
||||
Utils.log(`srcIndex: ${srcIndex}, destIndex: ${destIndex}`)
|
||||
newValue.splice(destIndex, 0, newValue.splice(srcIndex, 1)[0])
|
||||
|
||||
const newBoard = new MutableBoard(board)
|
||||
newBoard.cardProperties = newValue
|
||||
|
||||
await undoManager.perform(
|
||||
async () => {
|
||||
board.cardProperties = newValue
|
||||
await octoClient.updateBlock(board)
|
||||
await octoClient.updateBlock(newBoard)
|
||||
},
|
||||
async () => {
|
||||
board.cardProperties = oldValue
|
||||
await octoClient.updateBlock(board)
|
||||
},
|
||||
'reorder properties',
|
||||
@ -220,23 +244,28 @@ class Mutator {
|
||||
async deleteProperty(boardTree: BoardTree, propertyId: string) {
|
||||
const {board, views, cards} = boardTree
|
||||
|
||||
const oldBlocks: IBlock[] = [new Board(board)]
|
||||
const oldBlocks: IBlock[] = [board]
|
||||
|
||||
const changedBlocks: IBlock[] = [board]
|
||||
board.cardProperties = board.cardProperties.filter((o) => o.id !== propertyId)
|
||||
const newBoard = new MutableBoard(board)
|
||||
const changedBlocks: IBlock[] = [newBoard]
|
||||
newBoard.cardProperties = board.cardProperties.filter((o) => o.id !== propertyId)
|
||||
|
||||
views.forEach((view) => {
|
||||
if (view.visiblePropertyIds.includes(propertyId)) {
|
||||
oldBlocks.push(new BoardView(view))
|
||||
view.visiblePropertyIds = view.visiblePropertyIds.filter((o) => o !== propertyId)
|
||||
changedBlocks.push(view)
|
||||
oldBlocks.push(view)
|
||||
|
||||
const newView = new MutableBoardView(view)
|
||||
newView.visiblePropertyIds = view.visiblePropertyIds.filter((o) => o !== propertyId)
|
||||
changedBlocks.push(newView)
|
||||
}
|
||||
})
|
||||
cards.forEach((card) => {
|
||||
if (card.properties[propertyId]) {
|
||||
oldBlocks.push(new Block(card))
|
||||
delete card.properties[propertyId]
|
||||
changedBlocks.push(card)
|
||||
oldBlocks.push(card)
|
||||
|
||||
const newCard = new MutableCard(card)
|
||||
delete newCard.properties[propertyId]
|
||||
changedBlocks.push(newCard)
|
||||
}
|
||||
})
|
||||
|
||||
@ -252,12 +281,14 @@ class Mutator {
|
||||
}
|
||||
|
||||
async renameProperty(board: Board, propertyId: string, name: string) {
|
||||
const oldBlocks: IBlock[] = [new Board(board)]
|
||||
const changedBlocks: IBlock[] = [board]
|
||||
const oldBlocks: IBlock[] = [board]
|
||||
const newBoard = new MutableBoard(board)
|
||||
const changedBlocks: IBlock[] = [newBoard]
|
||||
|
||||
const template = board.cardProperties.find((o) => o.id === propertyId)
|
||||
const template = newBoard.cardProperties.find((o) => o.id === propertyId)
|
||||
if (!template) {
|
||||
Utils.assertFailure(`Can't find property template with Id: ${propertyId}`); return
|
||||
Utils.assertFailure(`Can't find property template with Id: ${propertyId}`)
|
||||
return
|
||||
}
|
||||
Utils.log(`renameProperty from ${template.name} to ${name}`)
|
||||
template.name = name
|
||||
@ -280,18 +311,16 @@ class Mutator {
|
||||
|
||||
Utils.assert(board.cardProperties.includes(template))
|
||||
|
||||
const oldValue = template.options
|
||||
const newValue = template.options.slice()
|
||||
newValue.push(option)
|
||||
const newBoard = new MutableBoard(board)
|
||||
const newTemplate = newBoard.cardProperties.find(o => o.id === template.id)
|
||||
newTemplate.options.push(option)
|
||||
|
||||
await undoManager.perform(
|
||||
async () => {
|
||||
template.options = newValue
|
||||
await octoClient.updateBlock(board)
|
||||
await octoClient.updateBlock(newBoard)
|
||||
},
|
||||
async () => {
|
||||
// TODO: Also remove property on cards
|
||||
template.options = oldValue
|
||||
await octoClient.updateBlock(board)
|
||||
},
|
||||
description,
|
||||
@ -301,18 +330,17 @@ class Mutator {
|
||||
async deletePropertyOption(boardTree: BoardTree, template: IPropertyTemplate, option: IPropertyOption) {
|
||||
const {board} = boardTree
|
||||
|
||||
const oldValue = template.options.slice()
|
||||
const newValue = template.options.filter((o) => o !== option)
|
||||
const newBoard = new MutableBoard(board)
|
||||
const newTemplate = newBoard.cardProperties.find(o => o.id === template.id)
|
||||
newTemplate.options = newTemplate.options.filter(o => o.value !== option.value)
|
||||
|
||||
// TODO: Also remove property on cards
|
||||
|
||||
await undoManager.perform(
|
||||
async () => {
|
||||
template.options = newValue
|
||||
await octoClient.updateBlock(board)
|
||||
await octoClient.updateBlock(newBoard)
|
||||
},
|
||||
async () => {
|
||||
template.options = oldValue
|
||||
await octoClient.updateBlock(board)
|
||||
},
|
||||
'delete option',
|
||||
@ -320,20 +348,18 @@ class Mutator {
|
||||
}
|
||||
|
||||
async changePropertyOptionOrder(board: Board, template: IPropertyTemplate, option: IPropertyOption, destIndex: number) {
|
||||
const oldValue = template.options
|
||||
const newValue = template.options.slice()
|
||||
|
||||
const srcIndex = newValue.indexOf(option)
|
||||
const srcIndex = template.options.indexOf(option)
|
||||
Utils.log(`srcIndex: ${srcIndex}, destIndex: ${destIndex}`)
|
||||
newValue.splice(destIndex, 0, newValue.splice(srcIndex, 1)[0])
|
||||
|
||||
const newBoard = new MutableBoard(board)
|
||||
const newTemplate = newBoard.cardProperties.find(o => o.id === template.id)
|
||||
newTemplate.options.splice(destIndex, 0, newTemplate.options.splice(srcIndex, 1)[0])
|
||||
|
||||
await undoManager.perform(
|
||||
async () => {
|
||||
template.options = newValue
|
||||
await octoClient.updateBlock(board)
|
||||
await octoClient.updateBlock(newBoard)
|
||||
},
|
||||
async () => {
|
||||
template.options = oldValue
|
||||
await octoClient.updateBlock(board)
|
||||
},
|
||||
'reorder options',
|
||||
@ -344,18 +370,23 @@ class Mutator {
|
||||
const {board, cards} = boardTree
|
||||
|
||||
const oldValue = option.value
|
||||
const oldBlocks: IBlock[] = [new Board(board)]
|
||||
const oldBlocks: IBlock[] = [board]
|
||||
|
||||
const changedBlocks: IBlock[] = [board]
|
||||
option.value = value
|
||||
const newBoard = new MutableBoard(board)
|
||||
const newTemplate = newBoard.cardProperties.find(o => o.id === propertyTemplate.id)
|
||||
const newOption = newTemplate.options.find(o => o.value === oldValue)
|
||||
newOption.value = value
|
||||
const changedBlocks: IBlock[] = [newBoard]
|
||||
|
||||
// Change the value on all cards that have this property too
|
||||
for (const card of cards) {
|
||||
const propertyValue = card.properties[propertyTemplate.id]
|
||||
if (propertyValue && propertyValue === oldValue) {
|
||||
oldBlocks.push(new Block(card))
|
||||
card.properties[propertyTemplate.id] = value
|
||||
changedBlocks.push(card)
|
||||
oldBlocks.push(card)
|
||||
|
||||
const newCard = new MutableCard(card)
|
||||
newCard.properties[propertyTemplate.id] = value
|
||||
changedBlocks.push(newCard)
|
||||
}
|
||||
}
|
||||
|
||||
@ -372,16 +403,19 @@ class Mutator {
|
||||
return changedBlocks
|
||||
}
|
||||
|
||||
async changePropertyOptionColor(board: Board, option: IPropertyOption, color: string) {
|
||||
async changePropertyOptionColor(board: Board, template: IPropertyTemplate, option: IPropertyOption, color: string) {
|
||||
const oldValue = option.color
|
||||
|
||||
const newBoard = new MutableBoard(board)
|
||||
const newTemplate = newBoard.cardProperties.find(o => o.id === template.id)
|
||||
const newOption = newTemplate.options.find(o => o.value === option.value)
|
||||
newOption.color = color
|
||||
|
||||
undoManager.perform(
|
||||
async () => {
|
||||
option.color = color
|
||||
await octoClient.updateBlock(board)
|
||||
await octoClient.updateBlock(newBoard)
|
||||
},
|
||||
async () => {
|
||||
option.color = oldValue
|
||||
await octoClient.updateBlock(board)
|
||||
},
|
||||
'change option color',
|
||||
@ -390,13 +424,15 @@ class Mutator {
|
||||
|
||||
async changePropertyValue(card: Card, propertyId: string, value?: string, description = 'change property') {
|
||||
const oldValue = card.properties[propertyId]
|
||||
|
||||
const newCard = new MutableCard(card)
|
||||
newCard.properties[propertyId] = value
|
||||
|
||||
await undoManager.perform(
|
||||
async () => {
|
||||
card.properties[propertyId] = value
|
||||
await octoClient.updateBlock(card)
|
||||
await octoClient.updateBlock(newCard)
|
||||
},
|
||||
async () => {
|
||||
card.properties[propertyId] = oldValue
|
||||
await octoClient.updateBlock(card)
|
||||
},
|
||||
description,
|
||||
@ -405,13 +441,16 @@ class Mutator {
|
||||
|
||||
async changePropertyType(board: Board, propertyTemplate: IPropertyTemplate, type: PropertyType) {
|
||||
const oldValue = propertyTemplate.type
|
||||
|
||||
const newBoard = new MutableBoard(board)
|
||||
const newTemplate = newBoard.cardProperties.find(o => o.id === propertyTemplate.id)
|
||||
newTemplate.type = type
|
||||
|
||||
await undoManager.perform(
|
||||
async () => {
|
||||
propertyTemplate.type = type
|
||||
await octoClient.updateBlock(board)
|
||||
await octoClient.updateBlock(newBoard)
|
||||
},
|
||||
async () => {
|
||||
propertyTemplate.type = oldValue
|
||||
await octoClient.updateBlock(board)
|
||||
},
|
||||
'change property type',
|
||||
@ -423,13 +462,14 @@ class Mutator {
|
||||
async changeViewSortOptions(view: BoardView, sortOptions: ISortOption[]) {
|
||||
const oldValue = view.sortOptions
|
||||
|
||||
const newView = new MutableBoardView(view)
|
||||
newView.sortOptions = sortOptions
|
||||
|
||||
await undoManager.perform(
|
||||
async () => {
|
||||
view.sortOptions = sortOptions
|
||||
await octoClient.updateBlock(view)
|
||||
await octoClient.updateBlock(newView)
|
||||
},
|
||||
async () => {
|
||||
view.sortOptions = oldValue
|
||||
await octoClient.updateBlock(view)
|
||||
},
|
||||
'sort',
|
||||
@ -439,13 +479,14 @@ class Mutator {
|
||||
async changeViewFilter(view: BoardView, filter?: FilterGroup) {
|
||||
const oldValue = view.filter
|
||||
|
||||
const newView = new MutableBoardView(view)
|
||||
newView.filter = filter
|
||||
|
||||
await undoManager.perform(
|
||||
async () => {
|
||||
view.filter = filter
|
||||
await octoClient.updateBlock(view)
|
||||
await octoClient.updateBlock(newView)
|
||||
},
|
||||
async () => {
|
||||
view.filter = oldValue
|
||||
await octoClient.updateBlock(view)
|
||||
},
|
||||
'filter',
|
||||
@ -455,13 +496,14 @@ class Mutator {
|
||||
async changeViewVisibleProperties(view: BoardView, visiblePropertyIds: string[]) {
|
||||
const oldValue = view.visiblePropertyIds
|
||||
|
||||
const newView = new MutableBoardView(view)
|
||||
newView.visiblePropertyIds = visiblePropertyIds
|
||||
|
||||
await undoManager.perform(
|
||||
async () => {
|
||||
view.visiblePropertyIds = visiblePropertyIds
|
||||
await octoClient.updateBlock(view)
|
||||
await octoClient.updateBlock(newView)
|
||||
},
|
||||
async () => {
|
||||
view.visiblePropertyIds = oldValue
|
||||
await octoClient.updateBlock(view)
|
||||
},
|
||||
'hide / show property',
|
||||
@ -471,13 +513,14 @@ class Mutator {
|
||||
async changeViewGroupById(view: BoardView, groupById: string) {
|
||||
const oldValue = view.groupById
|
||||
|
||||
const newView = new MutableBoardView(view)
|
||||
newView.groupById = groupById
|
||||
|
||||
await undoManager.perform(
|
||||
async () => {
|
||||
view.groupById = groupById
|
||||
await octoClient.updateBlock(view)
|
||||
await octoClient.updateBlock(newView)
|
||||
},
|
||||
async () => {
|
||||
view.groupById = oldValue
|
||||
await octoClient.updateBlock(view)
|
||||
},
|
||||
'group by',
|
||||
@ -500,7 +543,8 @@ class Mutator {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const block = new ImageBlock({parentId})
|
||||
const block = new MutableImageBlock()
|
||||
block.parentId = parentId
|
||||
block.order = order
|
||||
block.url = url
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import { IMutableBlock } from './blocks/block'
|
||||
import {IBlock} from './octoTypes'
|
||||
import {Utils} from './utils'
|
||||
|
||||
@ -17,7 +18,7 @@ class OctoClient {
|
||||
async getSubtree(rootId?: string): Promise<IBlock[]> {
|
||||
const path = `/api/v1/blocks/${rootId}/subtree`
|
||||
const response = await fetch(this.serverUrl + path)
|
||||
const blocks = (await response.json() || []) as IBlock[]
|
||||
const blocks = (await response.json() || []) as IMutableBlock[]
|
||||
this.fixBlocks(blocks)
|
||||
return blocks
|
||||
}
|
||||
@ -25,7 +26,7 @@ class OctoClient {
|
||||
async exportFullArchive(): Promise<IBlock[]> {
|
||||
const path = '/api/v1/blocks/export'
|
||||
const response = await fetch(this.serverUrl + path)
|
||||
const blocks = (await response.json() || []) as IBlock[]
|
||||
const blocks = (await response.json() || []) as IMutableBlock[]
|
||||
this.fixBlocks(blocks)
|
||||
return blocks
|
||||
}
|
||||
@ -59,12 +60,12 @@ class OctoClient {
|
||||
}
|
||||
|
||||
const response = await fetch(this.serverUrl + path)
|
||||
const blocks = (await response.json() || []) as IBlock[]
|
||||
const blocks = (await response.json() || []) as IMutableBlock[]
|
||||
this.fixBlocks(blocks)
|
||||
return blocks
|
||||
}
|
||||
|
||||
fixBlocks(blocks: IBlock[]): void {
|
||||
fixBlocks(blocks: IMutableBlock[]): void {
|
||||
if (!blocks) {
|
||||
return
|
||||
}
|
||||
@ -90,12 +91,12 @@ class OctoClient {
|
||||
}
|
||||
}
|
||||
|
||||
async updateBlock(block: IBlock): Promise<Response> {
|
||||
async updateBlock(block: IMutableBlock): Promise<Response> {
|
||||
block.updateAt = Date.now()
|
||||
return await this.insertBlocks([block])
|
||||
}
|
||||
|
||||
async updateBlocks(blocks: IBlock[]): Promise<Response> {
|
||||
async updateBlocks(blocks: IMutableBlock[]): Promise<Response> {
|
||||
const now = Date.now()
|
||||
blocks.forEach((block) => {
|
||||
block.updateAt = now
|
||||
|
@ -1,21 +1,17 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
interface IBlock {
|
||||
id: string
|
||||
parentId: string
|
||||
readonly id: string
|
||||
readonly parentId: string
|
||||
|
||||
schema: number
|
||||
type: string
|
||||
title?: string
|
||||
fields: Record<string, any>
|
||||
readonly schema: number
|
||||
readonly type: string
|
||||
readonly title?: string
|
||||
readonly fields: Readonly<Record<string, any>>
|
||||
|
||||
createAt: number
|
||||
updateAt: number
|
||||
deleteAt: number
|
||||
}
|
||||
|
||||
interface IOrderedBlock extends IBlock {
|
||||
order: number
|
||||
readonly createAt: number
|
||||
readonly updateAt: number
|
||||
readonly deleteAt: number
|
||||
}
|
||||
|
||||
// These are methods exposed by the top-level page to components
|
||||
@ -26,4 +22,4 @@ interface IPageController {
|
||||
setSearchText(text?: string): void
|
||||
}
|
||||
|
||||
export {IBlock, IOrderedBlock, IPageController}
|
||||
export {IBlock, IPageController}
|
||||
|
@ -2,18 +2,19 @@
|
||||
// See LICENSE.txt for license information.
|
||||
import React from 'react'
|
||||
|
||||
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 {MutableBlock} from './blocks/block'
|
||||
import {Board, IPropertyTemplate, MutableBoard} from './blocks/board'
|
||||
import {BoardView, ISortOption, MutableBoardView} from './blocks/boardView'
|
||||
import {Card, MutableCard} from './blocks/card'
|
||||
import {CommentBlock, MutableCommentBlock} from './blocks/commentBlock'
|
||||
import {ImageBlock, MutableImageBlock} from './blocks/imageBlock'
|
||||
import { IOrderedBlock } from './blocks/orderedBlock'
|
||||
import {MutableTextBlock, TextBlock} from './blocks/textBlock'
|
||||
import {BoardTree} from './boardTree'
|
||||
import {Editable} from './components/editable'
|
||||
import {Menu} from './menu'
|
||||
import mutator from './mutator'
|
||||
import {IBlock, IOrderedBlock} from './octoTypes'
|
||||
import {IBlock} from './octoTypes'
|
||||
import {Utils} from './utils'
|
||||
|
||||
class OctoUtils {
|
||||
@ -76,7 +77,8 @@ class OctoUtils {
|
||||
menu.showAtElement(clickedElement)
|
||||
}
|
||||
|
||||
element = (<div
|
||||
element = (
|
||||
<div
|
||||
key={propertyTemplate.id}
|
||||
className={`${className} ${propertyColorCssClassName}`}
|
||||
tabIndex={0}
|
||||
@ -104,7 +106,7 @@ class OctoUtils {
|
||||
onChanged={(text) => {
|
||||
mutator.changePropertyValue(card, propertyTemplate.id, text)
|
||||
}}
|
||||
></Editable>)
|
||||
/>)
|
||||
} else {
|
||||
element = (<div
|
||||
key={propertyTemplate.id}
|
||||
@ -170,21 +172,22 @@ class OctoUtils {
|
||||
Menu.shared.showAtElement(e.target as HTMLElement)
|
||||
}
|
||||
|
||||
static hydrateBlock(block: IBlock): Block {
|
||||
static hydrateBlock(block: IBlock): MutableBlock {
|
||||
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) }
|
||||
case 'board': { return new MutableBoard(block) }
|
||||
case 'view': { return new MutableBoardView(block) }
|
||||
case 'card': { return new MutableCard(block) }
|
||||
case 'text': { return new MutableTextBlock(block) }
|
||||
case 'image': { return new MutableImageBlock(block) }
|
||||
case 'comment': { return new MutableCommentBlock(block) }
|
||||
default: {
|
||||
Utils.assertFailure(`Can't hydrate unknown block type: ${block.type}`)
|
||||
return new MutableBlock(block)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static hydrateBlocks(blocks: IBlock[]): Block[] {
|
||||
static hydrateBlocks(blocks: IBlock[]): MutableBlock[] {
|
||||
return blocks.map((block) => this.hydrateBlock(block))
|
||||
}
|
||||
}
|
||||
|
@ -162,7 +162,7 @@ export default class BoardPage extends React.Component<Props, State> {
|
||||
Utils.log(`attachToBoard: ${boardId}`)
|
||||
|
||||
this.boardListener.open(boardId, (blockId: string) => {
|
||||
console.log(`octoListener.onChanged: ${blockId}`)
|
||||
Utils.log(`boardListener.onChanged: ${blockId}`)
|
||||
this.sync(boardId)
|
||||
})
|
||||
|
||||
|
@ -1,14 +1,14 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React from 'react'
|
||||
|
||||
import { Archiver } from '../archiver'
|
||||
import {Board} from '../blocks/board'
|
||||
import { MutableBoard } from '../blocks/board'
|
||||
import Button from '../components/button'
|
||||
import octoClient from '../octoClient'
|
||||
import { IBlock } from '../octoTypes'
|
||||
import { Utils } from '../utils'
|
||||
|
||||
|
||||
type Props = {}
|
||||
|
||||
type State = {
|
||||
@ -43,7 +43,7 @@ export default class HomePage extends React.Component<Props, State> {
|
||||
}
|
||||
|
||||
addClicked = async () => {
|
||||
const board = new Board()
|
||||
const board = new MutableBoard()
|
||||
await octoClient.insertBlock(board)
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import {Block} from './blocks/block'
|
||||
import {Board} from './blocks/board'
|
||||
import octoClient from './octoClient'
|
||||
import { IBlock } from './octoTypes'
|
||||
import {OctoUtils} from './octoUtils'
|
||||
|
||||
interface WorkspaceTree {
|
||||
@ -17,7 +17,7 @@ class MutableWorkspaceTree {
|
||||
this.rebuild(OctoUtils.hydrateBlocks(blocks))
|
||||
}
|
||||
|
||||
private rebuild(blocks: Block[]) {
|
||||
private rebuild(blocks: IBlock[]) {
|
||||
this.boards = blocks.filter(block => block.type === "board") as Board[]
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user