diff --git a/config.json b/config.json
index 5ea8eb1f9..30ff1f6c3 100644
--- a/config.json
+++ b/config.json
@@ -1,4 +1,5 @@
{
+ "serverRoot": "http://localhost:8000",
"port": 8000,
"dbtype": "sqlite3",
"dbconfig": "./octo.db",
diff --git a/server/main/config.go b/server/main/config.go
index b7ab3f2f4..7cd2e1e20 100644
--- a/server/main/config.go
+++ b/server/main/config.go
@@ -21,7 +21,7 @@ func readConfigFile() (*Configuration, error) {
viper.SetConfigName("config") // name of config file (without extension)
viper.SetConfigType("json") // REQUIRED if the config file does not have the extension in the name
viper.AddConfigPath(".") // optionally look for config in the working directory
- viper.SetDefault("ServerRoot", "http://localhost")
+ viper.SetDefault("ServerRoot", "http://localhost:8000")
viper.SetDefault("Port", 8000)
viper.SetDefault("DBType", "sqlite3")
viper.SetDefault("DBConfigString", "./octo.db")
diff --git a/server/main/octoDatabase.go b/server/main/octoDatabase.go
index dfae0d2f9..487322a1d 100644
--- a/server/main/octoDatabase.go
+++ b/server/main/octoDatabase.go
@@ -48,16 +48,16 @@ func NewSQLStore(dbType, connectionString string) (*SQLStore, error) {
// Block is the basic data unit
type Block struct {
- ID string `json:"id"`
- ParentID string `json:"parentId"`
- Schema int64 `json:"schema"`
- Type string `json:"type"`
- Title string `json:"title"`
- Properties map[string]interface{} `json:"properties"`
- Fields map[string]interface{} `json:"fields"`
- CreateAt int64 `json:"createAt"`
- UpdateAt int64 `json:"updateAt"`
- DeleteAt int64 `json:"deleteAt"`
+ ID string `json:"id"`
+ ParentID string `json:"parentId"`
+ Schema int64 `json:"schema"`
+ Type string `json:"type"`
+ Title string `json:"title"`
+ Order int64 `json:"order"`
+ Fields map[string]interface{} `json:"fields"`
+ CreateAt int64 `json:"createAt"`
+ UpdateAt int64 `json:"updateAt"`
+ DeleteAt int64 `json:"deleteAt"`
}
func (s *SQLStore) createTablesIfNotExists() error {
diff --git a/src/client/app.tsx b/src/client/app.tsx
index d7bdbc5b0..e0c781904 100644
--- a/src/client/app.tsx
+++ b/src/client/app.tsx
@@ -31,6 +31,9 @@ export default function App() {
+
+
+
)
diff --git a/src/client/block.ts b/src/client/block.ts
index 08aba81e3..4bf7c609d 100644
--- a/src/client/block.ts
+++ b/src/client/block.ts
@@ -7,10 +7,7 @@ class Block implements IBlock {
parentId: string
type: string
title: string
- icon?: string
- url?: string
order: number
- properties: Record = {}
fields: Record = {}
createAt: number = Date.now()
updateAt: number = 0
@@ -37,32 +34,15 @@ class Block implements IBlock {
this.parentId = block.parentId
this.type = block.type
+ // Shallow copy here. Derived classes must make deep copies of their known properties in their constructors.
this.fields = block.fields ? { ...block.fields } : {}
this.title = block.title
- this.icon = block.icon
- this.url = block.url
this.order = block.order
this.createAt = block.createAt || now
this.updateAt = block.updateAt || now
this.deleteAt = block.deleteAt || 0
-
- if (block.schema !== 1) {
- if (Array.isArray(block.properties)) {
- // HACKHACK: Port from old schema
- this.properties = {}
- for (const property of block.properties) {
- if (property.id) {
- this.properties[property.id] = property.value
- }
- }
- } else {
- this.properties = { ...block.properties || {} }
- }
- } else {
- this.properties = { ...block.properties } // Shallow copy here. Derived classes must make deep copies of their known properties in their constructors.
- }
}
}
diff --git a/src/client/board.ts b/src/client/board.ts
index f01ba7f61..ea28c30bb 100644
--- a/src/client/board.ts
+++ b/src/client/board.ts
@@ -16,6 +16,9 @@ interface IPropertyTemplate {
}
class Board extends Block {
+ get icon(): string { return this.fields.icon as string }
+ set icon(value: string) { this.fields.icon = value }
+
get cardProperties(): IPropertyTemplate[] { return this.fields.cardProperties as IPropertyTemplate[] }
set cardProperties(value: IPropertyTemplate[]) { this.fields.cardProperties = value }
@@ -25,7 +28,7 @@ class Board extends Block {
if (block.fields?.cardProperties) {
// Deep clone of card properties and their options
- this.cardProperties = block.fields?.cardProperties.map((o: IPropertyTemplate) => {
+ this.cardProperties = block.fields.cardProperties.map((o: IPropertyTemplate) => {
return {
id: o.id,
name: o.name,
@@ -36,17 +39,6 @@ class Board extends Block {
} else {
this.cardProperties = []
}
-
- if (block.schema !== 1) {
- this.cardProperties = block.cardProperties?.map((o: IPropertyTemplate) => {
- return {
- id: o.id,
- name: o.name,
- type: o.type,
- options: o.options ? o.options.map(option => ({ ...option })) : []
- }
- }) || []
- }
}
}
diff --git a/src/client/boardTree.ts b/src/client/boardTree.ts
index 0d4d84e93..285bb2440 100644
--- a/src/client/boardTree.ts
+++ b/src/client/boardTree.ts
@@ -1,24 +1,25 @@
import { Board, IPropertyOption, IPropertyTemplate } from "./board"
import { BoardView } from "./boardView"
+import { Card } from "./card"
import { CardFilter } from "./cardFilter"
import { OctoClient } from "./octoClient"
import { IBlock } from "./octoTypes"
import { Utils } from "./utils"
-type Group = { option: IPropertyOption, cards: IBlock[] }
+type Group = { option: IPropertyOption, cards: Card[] }
class BoardTree {
board!: Board
views: BoardView[] = []
- cards: IBlock[] = []
- emptyGroupCards: IBlock[] = []
+ cards: Card[] = []
+ emptyGroupCards: Card[] = []
groups: Group[] = []
activeView?: BoardView
groupByProperty?: IPropertyTemplate
private searchText?: string
- private allCards: IBlock[] = []
+ private allCards: Card[] = []
get allBlocks(): IBlock[] {
return [this.board, ...this.views, ...this.allCards]
}
@@ -44,7 +45,7 @@ class BoardTree {
this.views = viewBlocks.map(o => new BoardView(o))
const cardBlocks = blocks.filter(block => block.type === "card")
- this.allCards = cardBlocks
+ this.allCards = cardBlocks.map(o => new Card(o))
this.cards = []
this.ensureMinimumSchema()
@@ -104,18 +105,25 @@ class BoardTree {
}
applyFilterSortAndGroup() {
+ Utils.assert(this.allCards !== undefined)
+
this.cards = this.filterCards(this.allCards)
+ Utils.assert(this.cards !== undefined)
this.cards = this.searchFilterCards(this.cards)
+ Utils.assert(this.cards !== undefined)
this.cards = this.sortCards(this.cards)
+ Utils.assert(this.cards !== undefined)
if (this.activeView.groupById) {
this.setGroupByProperty(this.activeView.groupById)
} else {
Utils.assert(this.activeView.viewType !== "board")
}
+
+ Utils.assert(this.cards !== undefined)
}
- private searchFilterCards(cards: IBlock[]) {
+ private searchFilterCards(cards: Card[]): Card[] {
const searchText = this.searchText?.toLocaleLowerCase()
if (!searchText) { return cards.slice() }
@@ -166,7 +174,7 @@ class BoardTree {
}
}
- private filterCards(cards: IBlock[]): IBlock[] {
+ private filterCards(cards: Card[]): Card[] {
const { board } = this
const filterGroup = this.activeView?.filter
if (!filterGroup) { return cards.slice() }
@@ -174,11 +182,11 @@ class BoardTree {
return CardFilter.applyFilterGroup(filterGroup, board.cardProperties, cards)
}
- private sortCards(cards: IBlock[]): IBlock[] {
+ private sortCards(cards: Card[]): Card[] {
if (!this.activeView) { Utils.assertFailure(); return cards }
const { board } = this
const { sortOptions } = this.activeView
- let sortedCards: IBlock[]
+ let sortedCards: Card[] = []
if (sortOptions.length < 1) {
Utils.log(`Default sort`)
@@ -213,7 +221,11 @@ class BoardTree {
} else {
const sortPropertyId = sortOption.propertyId
const template = board.cardProperties.find(o => o.id === sortPropertyId)
- Utils.log(`Sort by ${template.name}`)
+ if (!template) {
+ Utils.logError(`Missing template for property id: ${sortPropertyId}`)
+ return cards.slice()
+ }
+ Utils.log(`Sort by ${template?.name}`)
sortedCards = cards.sort((a, b) => {
// Always put cards with no titles at the bottom
if (a.title && !b.title) { return -1 }
diff --git a/src/client/boardView.ts b/src/client/boardView.ts
index c1aed1b23..9b7c8692a 100644
--- a/src/client/boardView.ts
+++ b/src/client/boardView.ts
@@ -25,9 +25,9 @@ class BoardView extends Block {
this.type = "view"
- this.sortOptions = block.properties?.sortOptions?.map((o: ISortOption) => ({ ...o })) || [] // Deep clone
- this.visiblePropertyIds = block.properties?.visiblePropertyIds?.slice() || []
- this.filter = new FilterGroup(block.properties?.filter)
+ this.sortOptions = block.fields?.sortOptions?.map((o: ISortOption) => ({ ...o })) || [] // Deep clone
+ this.visiblePropertyIds = block.fields?.visiblePropertyIds?.slice() || []
+ this.filter = new FilterGroup(block.fields?.filter)
// TODO: Remove this fixup code
if (block.schema !== 1) {
diff --git a/src/client/card.ts b/src/client/card.ts
new file mode 100644
index 000000000..5150b2c05
--- /dev/null
+++ b/src/client/card.ts
@@ -0,0 +1,18 @@
+import { Block } from "./block"
+
+class Card extends Block {
+ get icon(): string { return this.fields.icon as string }
+ set icon(value: string) { this.fields.icon = value }
+
+ get properties(): Record { return this.fields.properties as Record }
+ set properties(value: Record) { this.fields.properties = value }
+
+ constructor(block: any = {}) {
+ super(block)
+ this.type = "card"
+
+ this.properties = { ...(block.fields?.properties || {}) }
+ }
+}
+
+export { Card }
diff --git a/src/client/cardFilter.ts b/src/client/cardFilter.ts
index 82641f89c..28dd14768 100644
--- a/src/client/cardFilter.ts
+++ b/src/client/cardFilter.ts
@@ -1,15 +1,15 @@
import { IPropertyTemplate } from "./board"
+import { Card } from "./card"
import { FilterClause } from "./filterClause"
import { FilterGroup } from "./filterGroup"
-import { IBlock } from "./octoTypes"
import { Utils } from "./utils"
class CardFilter {
- static applyFilterGroup(filterGroup: FilterGroup, templates: IPropertyTemplate[], cards: IBlock[]): IBlock[] {
+ static applyFilterGroup(filterGroup: FilterGroup, templates: IPropertyTemplate[], cards: Card[]): Card[] {
return cards.filter(card => this.isFilterGroupMet(filterGroup, templates, card))
}
- static isFilterGroupMet(filterGroup: FilterGroup, templates: IPropertyTemplate[], card: IBlock): boolean {
+ static isFilterGroupMet(filterGroup: FilterGroup, templates: IPropertyTemplate[], card: Card): boolean {
const { filters } = filterGroup
if (filterGroup.filters.length < 1) {
@@ -38,7 +38,7 @@ class CardFilter {
}
}
- static isClauseMet(filter: FilterClause, templates: IPropertyTemplate[], card: IBlock): boolean {
+ static isClauseMet(filter: FilterClause, templates: IPropertyTemplate[], card: Card): boolean {
const value = card.properties[filter.propertyId]
switch (filter.condition) {
case "includes": {
@@ -55,22 +55,31 @@ class CardFilter {
case "isNotEmpty": {
return !!value
}
+ default: {
+ Utils.assertFailure(`Invalid filter condition ${filter.condition}`)
+ }
}
- Utils.assertFailure(`Invalid filter condition ${filter.condition}`)
return true
}
- static propertiesThatMeetFilterGroup(filterGroup: FilterGroup, templates: IPropertyTemplate[]): Record {
+ static propertiesThatMeetFilterGroup(filterGroup: FilterGroup, templates: IPropertyTemplate[]): Record {
// TODO: Handle filter groups
const filters = filterGroup.filters.filter(o => !FilterGroup.isAnInstanceOf(o))
- if (filters.length < 1) { return [] }
+ if (filters.length < 1) { return {} }
if (filterGroup.operation === "or") {
// Just need to meet the first clause
const property = this.propertyThatMeetsFilterClause(filters[0] as FilterClause, templates)
- return [property]
+ const result: Record = {}
+ result[property.id] = property.value
+ return result
} else {
- return filters.map(filterClause => this.propertyThatMeetsFilterClause(filterClause as FilterClause, templates))
+ const result: Record = {}
+ filters.forEach(filterClause => {
+ const p = this.propertyThatMeetsFilterClause(filterClause as FilterClause, templates)
+ result[p.id] = p.value
+ })
+ return result
}
}
diff --git a/src/client/cardTree.ts b/src/client/cardTree.ts
index 28b2d44d0..819d522b3 100644
--- a/src/client/cardTree.ts
+++ b/src/client/cardTree.ts
@@ -1,8 +1,9 @@
+import { Card } from "./card"
import { OctoClient } from "./octoClient"
import { IBlock } from "./octoTypes"
class CardTree {
- card: IBlock
+ card: Card
comments: IBlock[]
contents: IBlock[]
isSynched: boolean
@@ -18,7 +19,7 @@ class CardTree {
}
private rebuild(blocks: IBlock[]) {
- this.card = blocks.find(o => o.id === this.cardId)
+ this.card = new Card(blocks.find(o => o.id === this.cardId))
this.comments = blocks
.filter(block => block.type === "comment")
diff --git a/src/client/components/boardCard.tsx b/src/client/components/boardCard.tsx
index 65e7af493..39ca5fb96 100644
--- a/src/client/components/boardCard.tsx
+++ b/src/client/components/boardCard.tsx
@@ -1,15 +1,15 @@
import React from "react"
import { Block } from "../block"
import { IPropertyTemplate } from "../board"
+import { Card } from "../card"
import { Menu } from "../menu"
import { Mutator } from "../mutator"
-import { IBlock } from "../octoTypes"
import { OctoUtils } from "../octoUtils"
import { Utils } from "../utils"
type BoardCardProps = {
mutator: Mutator
- card: IBlock
+ card: Card
visiblePropertyTemplates: IPropertyTemplate[]
onClick?: (e: React.MouseEvent) => void
onDragStart?: (e: React.DragEvent) => void
diff --git a/src/client/components/boardComponent.tsx b/src/client/components/boardComponent.tsx
index 1240527a0..af0966b5a 100644
--- a/src/client/components/boardComponent.tsx
+++ b/src/client/components/boardComponent.tsx
@@ -1,17 +1,17 @@
import React from "react"
import { Archiver } from "../archiver"
-import { Block } from "../block"
import { BlockIcons } from "../blockIcons"
import { IPropertyOption } from "../board"
import { BoardTree } from "../boardTree"
+import { Card } from "../card"
import { CardFilter } from "../cardFilter"
import ViewMenu from "../components/viewMenu"
import MenuWrapper from "../widgets/menuWrapper"
import Menu from "../widgets/menu"
import { Constants } from "../constants"
+import { randomEmojiList } from "../emojiList"
import { Menu as OldMenu } from "../menu"
import { Mutator } from "../mutator"
-import { IBlock } from "../octoTypes"
import { OctoUtils } from "../octoUtils"
import { Utils } from "../utils"
import { BoardCard } from "./boardCard"
@@ -23,7 +23,7 @@ type Props = {
mutator: Mutator,
boardTree?: BoardTree
showView: (id: string) => void
- showCard: (card: IBlock) => void
+ showCard: (card: Card) => void
showFilter: (el: HTMLElement) => void
setSearchText: (text: string) => void
}
@@ -34,7 +34,7 @@ type State = {
}
class BoardComponent extends React.Component {
- private draggedCard: IBlock
+ private draggedCard: Card
private draggedHeaderOption: IPropertyOption
private searchFieldRef = React.createRef()
@@ -232,7 +232,7 @@ class BoardComponent extends React.Component {
)
}
- async showCard(card?: IBlock) {
+ async showCard(card?: Card) {
console.log(`showCard: ${card?.title}`)
await this.props.showCard(card)
@@ -242,8 +242,9 @@ class BoardComponent extends React.Component {
const { mutator, boardTree } = this.props
const { activeView, board } = boardTree
- const properties = CardFilter.propertiesThatMeetFilterGroup(activeView.filter, board.cardProperties)
- const card = new Block({ type: "card", parentId: boardTree.board.id, properties })
+ const card = new Card()
+ card.parentId = boardTree.board.id
+ card.properties = CardFilter.propertiesThatMeetFilterGroup(activeView.filter, board.cardProperties)
if (boardTree.groupByProperty) {
card.properties[boardTree.groupByProperty.id] = groupByValue
}
@@ -277,9 +278,11 @@ class BoardComponent extends React.Component {
}
case "testAdd100Cards": {
this.testAddCards(100)
+ break
}
case "testAdd1000Cards": {
this.testAddCards(1000)
+ break
}
}
}
@@ -290,17 +293,20 @@ class BoardComponent extends React.Component {
const { mutator, boardTree } = this.props
const { board, activeView } = boardTree
+ const startCount = boardTree?.cards?.length
let optionIndex = 0
for (let i = 0; i < count; i++) {
- const properties = CardFilter.propertiesThatMeetFilterGroup(activeView.filter, board.cardProperties)
- const card = new Block({ type: "card", parentId: boardTree.board.id, properties })
+ const card = new Card()
+ card.parentId = boardTree.board.id
+ card.properties = CardFilter.propertiesThatMeetFilterGroup(activeView.filter, board.cardProperties)
if (boardTree.groupByProperty && boardTree.groupByProperty.options.length > 0) {
// Cycle through options
const option = boardTree.groupByProperty.options[optionIndex]
optionIndex = (optionIndex + 1) % boardTree.groupByProperty.options.length
card.properties[boardTree.groupByProperty.id] = option.value
- card.title = `Test Card ${i + 1}`
+ card.title = `Test Card ${startCount + i + 1}`
+ card.icon = BlockIcons.shared.randomIcon()
}
await mutator.insertBlock(card, "test add card")
}
@@ -355,6 +361,7 @@ class BoardComponent extends React.Component {
color: "#cccccc"
}
+ Utils.assert(boardTree.groupByProperty)
await mutator.insertPropertyOption(boardTree, boardTree.groupByProperty, option, "add group")
}
diff --git a/src/client/components/cardDialog.tsx b/src/client/components/cardDialog.tsx
index 2a47ac310..e664c55c9 100644
--- a/src/client/components/cardDialog.tsx
+++ b/src/client/components/cardDialog.tsx
@@ -79,7 +79,7 @@ class CardDialog extends React.Component {
}} />
} else if (block.type === "image") {
- const url = block.url
+ const url = block.fields.url
return
{ this.showContentBlockMenu(e, block) }}>
diff --git a/src/client/components/switch.tsx b/src/client/components/switch.tsx
index 281c936c0..3354e239d 100644
--- a/src/client/components/switch.tsx
+++ b/src/client/components/switch.tsx
@@ -52,7 +52,7 @@ class Switch extends React.Component
{
private async onClicked() {
const newIsOn = !this.state.isOn
- await this.setState({ isOn: newIsOn })
+ this.setState({ isOn: newIsOn })
const { onChanged } = this.props
diff --git a/src/client/components/tableComponent.tsx b/src/client/components/tableComponent.tsx
index 62de747c4..93084987f 100644
--- a/src/client/components/tableComponent.tsx
+++ b/src/client/components/tableComponent.tsx
@@ -4,13 +4,13 @@ import { Block } from "../block"
import { BlockIcons } from "../blockIcons"
import { IPropertyTemplate } from "../board"
import { BoardTree } from "../boardTree"
-import { CsvExporter } from "../csvExporter"
+import { Card } from "../card"
import ViewMenu from "../components/viewMenu"
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 { IBlock } from "../octoTypes"
import { OctoUtils } from "../octoUtils"
import { Utils } from "../utils"
import Button from "./button"
@@ -21,7 +21,7 @@ type Props = {
mutator: Mutator,
boardTree?: BoardTree
showView: (id: string) => void
- showCard: (card: IBlock) => void
+ showCard: (card: Card) => void
showFilter: (el: HTMLElement) => void
setSearchText: (text: string) => void
}
@@ -348,7 +348,7 @@ class TableComponent extends React.Component {
OldMenu.shared.showAtElement(e.target as HTMLElement)
}
- async showCard(card: IBlock) {
+ async showCard(card: Card) {
console.log(`showCard: ${card.title}`)
await this.props.showCard(card)
@@ -363,7 +363,8 @@ class TableComponent extends React.Component {
async addCard(show: boolean = false) {
const { mutator, boardTree } = this.props
- const card = new Block({ type: "card", parentId: boardTree.board.id })
+ const card = new Card()
+ card.parentId = boardTree.board.id
await mutator.insertBlock(
card,
"add card",
diff --git a/src/client/components/tableRow.tsx b/src/client/components/tableRow.tsx
index fc6d6d7d2..3625fef47 100644
--- a/src/client/components/tableRow.tsx
+++ b/src/client/components/tableRow.tsx
@@ -1,16 +1,16 @@
import React from "react"
import { BoardTree } from "../boardTree"
+import { Card } from "../card"
import { Mutator } from "../mutator"
-import { IBlock } from "../octoTypes"
import { OctoUtils } from "../octoUtils"
import { Editable } from "./editable"
type Props = {
mutator: Mutator
boardTree: BoardTree
- card: IBlock
+ card: Card
focusOnMount: boolean
- showCard: (card: IBlock) => void
+ showCard: (card: Card) => void
onKeyDown: (e: React.KeyboardEvent) => void
}
diff --git a/src/client/components/workspaceComponent.tsx b/src/client/components/workspaceComponent.tsx
index eed77b353..e7c37a30f 100644
--- a/src/client/components/workspaceComponent.tsx
+++ b/src/client/components/workspaceComponent.tsx
@@ -1,7 +1,7 @@
import React from "react"
import { BoardTree } from "../boardTree"
+import { Card } from "../card"
import { Mutator } from "../mutator"
-import { IBlock } from "../octoTypes"
import { Utils } from "../utils"
import { WorkspaceTree } from "../workspaceTree"
import { BoardComponent } from "./boardComponent"
@@ -14,7 +14,7 @@ type Props = {
boardTree?: BoardTree
showBoard: (id: string) => void
showView: (id: string) => void
- showCard: (card: IBlock) => void
+ showCard: (card: Card) => void
showFilter: (el: HTMLElement) => void
setSearchText: (text: string) => void
}
diff --git a/src/client/mutator.ts b/src/client/mutator.ts
index ef77bc346..918134935 100644
--- a/src/client/mutator.ts
+++ b/src/client/mutator.ts
@@ -2,6 +2,7 @@ import { Block } from "./block"
import { Board, IPropertyOption, IPropertyTemplate, PropertyType } from "./board"
import { BoardTree } from "./boardTree"
import { BoardView, ISortOption } from "./boardView"
+import { Card } from "./card"
import { FilterGroup } from "./filterGroup"
import { OctoClient } from "./octoClient"
import { IBlock } from "./octoTypes"
@@ -87,7 +88,7 @@ class Mutator {
)
}
- async changeIcon(block: IBlock, icon: string, description: string = "change icon") {
+ async changeIcon(block: Card | Board, icon: string, description: string = "change icon") {
const { octo, undoManager } = this
const oldValue = block.icon
@@ -291,6 +292,8 @@ class Mutator {
const { octo, undoManager } = this
const { board } = boardTree
+ Utils.assert(board.cardProperties.includes(template))
+
const oldValue = template.options
const newValue = template.options.slice()
newValue.push(option)
@@ -405,18 +408,18 @@ class Mutator {
)
}
- async changePropertyValue(block: IBlock, propertyId: string, value?: string, description: string = "change property") {
+ async changePropertyValue(card: Card, propertyId: string, value?: string, description: string = "change property") {
const { octo, undoManager } = this
- const oldValue = block.properties[propertyId]
+ const oldValue = card.properties[propertyId]
await undoManager.perform(
async () => {
- block.properties[propertyId] = value
- await octo.updateBlock(block)
+ card.properties[propertyId] = value
+ await octo.updateBlock(card)
},
async () => {
- block.properties[propertyId] = oldValue
- await octo.updateBlock(block)
+ card.properties[propertyId] = oldValue
+ await octo.updateBlock(card)
},
description
)
@@ -531,7 +534,8 @@ class Mutator {
return undefined
}
- const block = new Block({ type: "image", parentId, url, order })
+ const block = new Block({ type: "image", parentId, order })
+ block.fields.url = url
await undoManager.perform(
async () => {
diff --git a/src/client/octoClient.ts b/src/client/octoClient.ts
index 85b138498..1c804bce9 100644
--- a/src/client/octoClient.ts
+++ b/src/client/octoClient.ts
@@ -57,25 +57,20 @@ class OctoClient {
}
const response = await fetch(this.serverUrl + path)
- const blocks = await response.json() as IBlock[]
+ const blocks = (await response.json() || []) as IBlock[]
this.fixBlocks(blocks)
return blocks
}
- fixBlocks(blocks: IBlock[]) {
+ fixBlocks(blocks: IBlock[]): void {
+ // TODO
for (const block of blocks) {
- if (!block.properties) { block.properties = {} }
-
- if (Array.isArray(block.properties)) {
- // PORT from old schema
- const properties: Record = {}
- for (const property of block.properties) {
- if (property.id) {
- properties[property.id] = property.value
- }
- }
- block.properties = properties
- }
+ if (!block.fields) { block.fields = {} }
+ const o = block as any
+ if (o.cardProperties) { block.fields.cardProperties = o.cardProperties; delete o.cardProperties }
+ if (o.properties) { block.fields.properties = o.properties; delete o.properties }
+ if (o.icon) { block.fields.icon = o.icon; delete o.icon }
+ if (o.url) { block.fields.url = o.url; delete o.url }
}
}
diff --git a/src/client/octoTypes.ts b/src/client/octoTypes.ts
index 7047c87c6..8b0ada85a 100644
--- a/src/client/octoTypes.ts
+++ b/src/client/octoTypes.ts
@@ -1,3 +1,5 @@
+import { Card } from "./card"
+
// A block is the fundamental data type
interface IBlock {
id: string
@@ -6,10 +8,7 @@ interface IBlock {
schema: number
type: string
title?: string
- url?: string // TODO: Move to properties (_url)
- icon?: string
order: number
- properties: Record
fields: Record
createAt: number
@@ -19,7 +18,7 @@ interface IBlock {
// These are methods exposed by the top-level page to components
interface IPageController {
- showCard(card: IBlock): Promise
+ showCard(card: Card): Promise
showBoard(boardId: string): void
showView(viewId: string): void
showFilter(anchorElement?: HTMLElement): void
diff --git a/src/client/octoUtils.tsx b/src/client/octoUtils.tsx
index 4fedbd32b..5072c4665 100644
--- a/src/client/octoUtils.tsx
+++ b/src/client/octoUtils.tsx
@@ -1,9 +1,10 @@
import React from "react"
import { IPropertyTemplate } from "./board"
import { BoardTree } from "./boardTree"
-import { BoardView, ISortOption } from "./boardView"
+import { ISortOption } from "./boardView"
+import { Card } from "./card"
import { Editable } from "./components/editable"
-import { Menu, MenuOption } from "./menu"
+import { Menu } from "./menu"
import { Mutator } from "./mutator"
import { IBlock } from "./octoTypes"
import { Utils } from "./utils"
@@ -25,15 +26,15 @@ class OctoUtils {
return displayValue
}
- static propertyValueReadonlyElement(card: IBlock, propertyTemplate: IPropertyTemplate, emptyDisplayValue: string = "Empty"): JSX.Element {
+ static propertyValueReadonlyElement(card: Card, propertyTemplate: IPropertyTemplate, emptyDisplayValue: string = "Empty"): JSX.Element {
return this.propertyValueElement(undefined, card, propertyTemplate, emptyDisplayValue)
}
- static propertyValueEditableElement(mutator: Mutator, card: IBlock, propertyTemplate: IPropertyTemplate, emptyDisplayValue?: string): JSX.Element {
+ static propertyValueEditableElement(mutator: Mutator, card: Card, propertyTemplate: IPropertyTemplate, emptyDisplayValue?: string): JSX.Element {
return this.propertyValueElement(mutator, card, propertyTemplate, emptyDisplayValue)
}
- private static propertyValueElement(mutator: Mutator | undefined, card: IBlock, propertyTemplate: IPropertyTemplate, emptyDisplayValue: string = "Empty"): JSX.Element {
+ private static propertyValueElement(mutator: Mutator | undefined, 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
diff --git a/src/client/pages/boardPage.tsx b/src/client/pages/boardPage.tsx
index ad4ee5f03..cd35e991b 100644
--- a/src/client/pages/boardPage.tsx
+++ b/src/client/pages/boardPage.tsx
@@ -2,6 +2,7 @@ import React from "react"
import ReactDOM from "react-dom"
import { BoardTree } from "../boardTree"
import { BoardView } from "../boardView"
+import { Card } from "../card"
import { CardTree } from "../cardTree"
import { CardDialog } from "../components/cardDialog"
import { FilterComponent } from "../components/filterComponent"
@@ -10,7 +11,6 @@ import { FlashMessage } from "../flashMessage"
import { Mutator } from "../mutator"
import { OctoClient } from "../octoClient"
import { OctoListener } from "../octoListener"
-import { IBlock } from "../octoTypes"
import { UndoManager } from "../undomanager"
import { Utils } from "../utils"
import { WorkspaceTree } from "../workspaceTree"
@@ -24,6 +24,7 @@ type State = {
workspaceTree: WorkspaceTree
boardTree?: BoardTree
shownCardTree?: CardTree
+ filterAnchorElement?: HTMLElement
}
export default class BoardPage extends React.Component {
@@ -32,7 +33,6 @@ export default class BoardPage extends React.Component {
updateTitleTimeout: number
updatePropertyLabelTimeout: number
- private filterAnchorElement?: HTMLElement
private octo = new OctoClient()
private boardListener = new OctoListener()
private cardListener = new OctoListener()
@@ -126,8 +126,8 @@ export default class BoardPage extends React.Component {
}
}
- if (this.filterAnchorElement) {
- const element = this.filterAnchorElement
+ if (this.state.filterAnchorElement) {
+ const element = this.state.filterAnchorElement
const bodyRect = document.body.getBoundingClientRect()
const rect = element.getBoundingClientRect()
// Show at bottom-left of element
@@ -210,7 +210,7 @@ export default class BoardPage extends React.Component {
// IPageController
- async showCard(card: IBlock) {
+ async showCard(card: Card) {
this.cardListener.close()
if (card) {
@@ -246,11 +246,12 @@ export default class BoardPage extends React.Component {
window.history.pushState({ path: newUrl }, "", newUrl)
}
- showFilter(ahchorElement?: HTMLElement) {
- this.filterAnchorElement = ahchorElement
+ showFilter(anchorElement?: HTMLElement) {
+ this.setState({...this.state, filterAnchorElement: anchorElement})
}
setSearchText(text?: string) {
this.state.boardTree?.setSearchText(text)
+ this.setState({...this.state, boardTree: this.state.boardTree})
}
}
diff --git a/src/client/pages/homePage.tsx b/src/client/pages/homePage.tsx
index df2b1e762..05fed1d02 100644
--- a/src/client/pages/homePage.tsx
+++ b/src/client/pages/homePage.tsx
@@ -1,76 +1,74 @@
-import React from 'react';
-
-import { IBlock } from "../octoTypes"
+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 { IBlock } from "../octoTypes"
import { UndoManager } from "../undomanager"
import { Utils } from "../utils"
-import Button from '../components/button';
-type Props = {};
+type Props = {}
type State = {
- boards: IBlock[];
-};
+ boards: IBlock[]
+}
export default class HomePage extends React.Component {
- constructor(props: Props) {
- super(props);
- this.state = {
- boards: [],
- }
- }
+ constructor(props: Props) {
+ super(props)
+ this.state = {
+ boards: [],
+ }
+ }
- componentDidMount() {
- this.loadBoards();
- }
+ componentDidMount() {
+ this.loadBoards()
+ }
- loadBoards = async () => {
- const octo = new OctoClient()
+ loadBoards = async () => {
+ const octo = new OctoClient()
const boards = await octo.getBlocks(null, "board")
- this.setState({boards});
- }
+ this.setState({ boards })
+ }
- importClicked = async () => {
- const octo = new OctoClient()
- const mutator = new Mutator(octo, UndoManager.shared)
- Archiver.importFullArchive(mutator, () => {
- this.loadBoards()
- })
- }
+ importClicked = async () => {
+ const octo = new OctoClient()
+ const mutator = new Mutator(octo, UndoManager.shared)
+ Archiver.importFullArchive(mutator, () => {
+ this.loadBoards()
+ })
+ }
- exportClicked = async () => {
- const octo = new OctoClient()
- const mutator = new Mutator(octo, UndoManager.shared)
- Archiver.exportFullArchive(mutator)
- }
+ exportClicked = async () => {
+ const octo = new OctoClient()
+ const mutator = new Mutator(octo, UndoManager.shared)
+ Archiver.exportFullArchive(mutator)
+ }
- addClicked = async () => {
- const octo = new OctoClient()
+ addClicked = async () => {
+ const octo = new OctoClient()
const board = new Board()
await octo.insertBlock(board)
}
- public render(): React.ReactNode {
- return (
-
- );
- }
+ render(): React.ReactNode {
+ return (
+
+ )
+ }
}