1
0
Fork 0
mirror of https://github.com/anyproto/anytype-heart.git synced 2025-06-10 18:10:49 +09:00
This commit is contained in:
Sergey Cherepanov 2022-07-12 15:41:59 +03:00
commit 288cb6231e
No known key found for this signature in database
GPG key ID: 87F8EDE8FBDF637C
46 changed files with 24063 additions and 1742 deletions

View file

@ -188,6 +188,9 @@ func (mw *Middleware) getInfo() *model.AccountInfo {
cfg := config.ConfigRequired{}
files.GetFileConfig(filepath.Join(wallet.RepoPath(), config.ConfigFileName), &cfg);
if cfg.IPFSStorageAddr == "" {
cfg.IPFSStorageAddr = wallet.RepoPath()
}
pBlocks := at.PredefinedBlocks()
@ -201,6 +204,7 @@ func (mw *Middleware) getInfo() *model.AccountInfo {
GatewayUrl: gwAddr,
DeviceId: deviceId,
LocalStoragePath: cfg.IPFSStorageAddr,
TimeZone: cfg.TimeZone,
}
}
@ -678,7 +682,7 @@ func (mw *Middleware) AccountMove(req *pb.RpcAccountMoveRequest) *pb.RpcAccountM
dirs := clientds.GetDirsForMoving()
conf := mw.app.MustComponent(config.CName).(*config.Config)
configPath := filepath.Join(conf.RepoPath, config.ConfigFileName)
configPath := conf.GetConfigPath()
srcPath := conf.RepoPath
fileConf := config.ConfigRequired{}
if err := files.GetFileConfig(configPath, &fileConf); err != nil {
@ -792,6 +796,30 @@ func (mw *Middleware) AccountDelete(req *pb.RpcAccountDeleteRequest) *pb.RpcAcco
return response(st, pb.RpcAccountDeleteResponseError_NULL, nil)
}
func (mw *Middleware) AccountConfigUpdate(req *pb.RpcAccountConfigUpdateRequest) *pb.RpcAccountConfigUpdateResponse {
response := func(code pb.RpcAccountConfigUpdateResponseErrorCode, err error) *pb.RpcAccountConfigUpdateResponse {
m := &pb.RpcAccountConfigUpdateResponse{Error: &pb.RpcAccountConfigUpdateResponseError{Code: code}}
if err != nil {
m.Error.Description = err.Error()
}
return m
}
if mw.app == nil {
return response(pb.RpcAccountConfigUpdateResponseError_ACCOUNT_IS_NOT_RUNNING, fmt.Errorf("anytype node not set"))
}
conf := mw.app.MustComponent(config.CName).(*config.Config)
cfg := config.ConfigRequired{}
cfg.TimeZone = req.TimeZone
err := files.WriteJsonConfig(conf.GetConfigPath(), cfg)
if err != nil {
return response(pb.RpcAccountConfigUpdateResponseError_FAILED_TO_WRITE_CONFIG, err)
}
return response(pb.RpcAccountConfigUpdateResponseError_NULL, err)
}
func (mw *Middleware) getDerivedAccountsForMnemonic(count int) ([]wallet.Keypair, error) {
var firstAccounts = make([]wallet.Keypair, count)
for i := 0; i < count; i++ {

View file

@ -32,6 +32,7 @@ type FileConfig interface {
type ConfigRequired struct {
HostAddr string `json:",omitempty"`
IPFSStorageAddr string `json:",omitempty"`
TimeZone string `json:",omitempty"`
}
type Config struct {
@ -156,7 +157,7 @@ func (c *Config) initFromFileAndEnv(repoPath string) error {
c.RepoPath = repoPath
if !c.DisableFileConfig {
err := files.GetFileConfig(filepath.Join(c.RepoPath, ConfigFileName), &c.ConfigRequired)
err := files.GetFileConfig(c.GetConfigPath(), &c.ConfigRequired)
if err != nil {
return fmt.Errorf("failed to get config from file: %s", err.Error())
}
@ -170,7 +171,7 @@ func (c *Config) initFromFileAndEnv(repoPath string) error {
c.HostAddr = fmt.Sprintf("/ip4/0.0.0.0/tcp/%d", port)
err = files.WriteJsonConfig(filepath.Join(c.RepoPath, ConfigFileName), c.ConfigRequired)
err = files.WriteJsonConfig(c.GetConfigPath(), c.ConfigRequired)
if err != nil {
return fmt.Errorf("failed to save port to the cfg file: %s", err.Error())
}
@ -225,7 +226,7 @@ func (c *Config) DSConfig() clientds.Config {
func (c *Config) FSConfig() (clientds.FSConfig, error) {
res := ConfigRequired{}
err := files.GetFileConfig(filepath.Join(c.RepoPath, ConfigFileName), &res)
err := files.GetFileConfig(c.GetConfigPath(), &res)
if err != nil {
return clientds.FSConfig{}, err
}
@ -237,6 +238,10 @@ func (c *Config) ThreadsConfig() threads.Config {
return c.Threads
}
func (c *Config) GetConfigPath() string {
return filepath.Join(c.RepoPath, ConfigFileName)
}
func getRandomPort() (int, error) {
addr, err := net.ResolveTCPAddr("tcp", "0.0.0.0:0")
if err != nil {

View file

@ -581,6 +581,26 @@ func (mw *Middleware) BlockListSetAlign(req *pb.RpcBlockListSetAlignRequest) *pb
return response(pb.RpcBlockListSetAlignResponseError_NULL, nil)
}
func (mw *Middleware) BlockListSetVerticalAlign(req *pb.RpcBlockListSetVerticalAlignRequest) *pb.RpcBlockListSetVerticalAlignResponse {
ctx := state.NewContext(nil)
response := func(code pb.RpcBlockListSetVerticalAlignResponseErrorCode, err error) *pb.RpcBlockListSetVerticalAlignResponse {
m := &pb.RpcBlockListSetVerticalAlignResponse{Error: &pb.RpcBlockListSetVerticalAlignResponseError{Code: code}}
if err != nil {
m.Error.Description = err.Error()
} else {
m.Event = ctx.GetResponseEvent()
}
return m
}
err := mw.doBlockService(func(bs block.Service) (err error) {
return bs.SetVerticalAlign(ctx, req.ContextId, req.VerticalAlign, req.BlockIds...)
})
if err != nil {
return response(pb.RpcBlockListSetVerticalAlignResponseError_UNKNOWN_ERROR, err)
}
return response(pb.RpcBlockListSetVerticalAlignResponseError_NULL, nil)
}
func (mw *Middleware) FileDrop(req *pb.RpcFileDropRequest) *pb.RpcFileDropResponse {
ctx := state.NewContext(nil)
response := func(code pb.RpcFileDropResponseErrorCode, err error) *pb.RpcFileDropResponse {
@ -747,6 +767,46 @@ func (mw *Middleware) BlockTextListSetMark(req *pb.RpcBlockTextListSetMarkReques
return response(pb.RpcBlockTextListSetMarkResponseError_NULL, nil)
}
func (mw *Middleware) BlockTextListClearStyle(req *pb.RpcBlockTextListClearStyleRequest) *pb.RpcBlockTextListClearStyleResponse {
ctx := state.NewContext(nil)
response := func(code pb.RpcBlockTextListClearStyleResponseErrorCode, err error) *pb.RpcBlockTextListClearStyleResponse {
m := &pb.RpcBlockTextListClearStyleResponse{Error: &pb.RpcBlockTextListClearStyleResponseError{Code: code}}
if err != nil {
m.Error.Description = err.Error()
} else {
m.Event = ctx.GetResponseEvent()
}
return m
}
err := mw.doBlockService(func(bs block.Service) (err error) {
return bs.ClearTextStyle(ctx, req.ContextId, req.BlockIds...)
})
if err != nil {
return response(pb.RpcBlockTextListClearStyleResponseError_UNKNOWN_ERROR, err)
}
return response(pb.RpcBlockTextListClearStyleResponseError_NULL, nil)
}
func (mw *Middleware) BlockTextListClearContent(req *pb.RpcBlockTextListClearContentRequest) *pb.RpcBlockTextListClearContentResponse {
ctx := state.NewContext(nil)
response := func(code pb.RpcBlockTextListClearContentResponseErrorCode, err error) *pb.RpcBlockTextListClearContentResponse {
m := &pb.RpcBlockTextListClearContentResponse{Error: &pb.RpcBlockTextListClearContentResponseError{Code: code}}
if err != nil {
m.Error.Description = err.Error()
} else {
m.Event = ctx.GetResponseEvent()
}
return m
}
err := mw.doBlockService(func(bs block.Service) (err error) {
return bs.ClearTextContent(ctx, req.ContextId, req.BlockIds...)
})
if err != nil {
return response(pb.RpcBlockTextListClearContentResponseError_UNKNOWN_ERROR, err)
}
return response(pb.RpcBlockTextListClearContentResponseError_NULL, nil)
}
func (mw *Middleware) BlockTextSetText(req *pb.RpcBlockTextSetTextRequest) *pb.RpcBlockTextSetTextResponse {
ctx := state.NewContext(nil)
response := func(code pb.RpcBlockTextSetTextResponseErrorCode, err error) *pb.RpcBlockTextSetTextResponse {

View file

@ -14,6 +14,7 @@ import (
"github.com/anytypeio/go-anytype-middleware/core/block/editor/smartblock"
"github.com/anytypeio/go-anytype-middleware/core/block/editor/state"
"github.com/anytypeio/go-anytype-middleware/core/block/editor/stext"
"github.com/anytypeio/go-anytype-middleware/core/block/editor/table"
"github.com/anytypeio/go-anytype-middleware/core/block/editor/template"
"github.com/anytypeio/go-anytype-middleware/core/block/simple"
"github.com/anytypeio/go-anytype-middleware/core/block/simple/link"
@ -328,6 +329,27 @@ func (s *service) SetTextColor(ctx *state.Context, contextId string, color strin
})
}
func (s *service) ClearTextStyle(ctx *state.Context, contextId string, blockIds ...string) error {
return s.DoText(contextId, func(b stext.Text) error {
return b.UpdateTextBlocks(ctx, blockIds, true, func(t text.Block) error {
t.Model().BackgroundColor = ""
t.Model().Align = model.Block_AlignLeft
t.Model().VerticalAlign = model.Block_VerticalAlignTop
t.SetTextColor("")
t.SetStyle(model.BlockContentText_Paragraph)
return nil
})
})
}
func (s *service) ClearTextContent(ctx *state.Context, contextId string, blockIds ...string) error {
return s.DoText(contextId, func(b stext.Text) error {
return b.UpdateTextBlocks(ctx, blockIds, true, func(t text.Block) error {
return t.SetText("", nil)
})
})
}
func (s *service) SetTextMark(ctx *state.Context, contextId string, mark *model.BlockContentTextMark, blockIds ...string) error {
return s.DoText(contextId, func(b stext.Text) error {
return b.SetMark(ctx, mark, blockIds...)
@ -371,6 +393,12 @@ func (s *service) SetAlign(ctx *state.Context, contextId string, align model.Blo
})
}
func (s *service) SetVerticalAlign(ctx *state.Context, contextId string, align model.BlockVerticalAlign, blockIds ...string) (err error) {
return s.Do(contextId, func(sb smartblock.SmartBlock) error {
return sb.SetVerticalAlign(ctx, align, blockIds...)
})
}
func (s *service) SetLayout(ctx *state.Context, contextId string, layout model.ObjectTypeLayout) (err error) {
return s.Do(contextId, func(sb smartblock.SmartBlock) error {
return sb.SetLayout(ctx, layout)
@ -828,3 +856,103 @@ func (s *service) MoveBlocks(ctx *state.Context, req pb.RpcBlockListMoveToExisti
})
})
}
func (s *service) CreateTableBlock(ctx *state.Context, req pb.RpcBlockTableCreateRequest) (id string, err error) {
err = s.DoTable(req.ContextId, ctx, func(st *state.State, t table.Editor) error {
id, err = t.TableCreate(st, req)
return err
})
return
}
func (s *service) TableRowCreate(ctx *state.Context, req pb.RpcBlockTableRowCreateRequest) (err error) {
err = s.DoTable(req.ContextId, ctx, func(st *state.State, t table.Editor) error {
return t.RowCreate(st, req)
})
return
}
func (s *service) TableColumnCreate(ctx *state.Context, req pb.RpcBlockTableColumnCreateRequest) (err error) {
err = s.DoTable(req.ContextId, ctx, func(st *state.State, t table.Editor) error {
return t.ColumnCreate(st, req)
})
return
}
func (s *service) TableRowDelete(ctx *state.Context, req pb.RpcBlockTableRowDeleteRequest) (err error) {
err = s.DoTable(req.ContextId, ctx, func(st *state.State, t table.Editor) error {
return t.RowDelete(st, req)
})
return
}
func (s *service) TableColumnDelete(ctx *state.Context, req pb.RpcBlockTableColumnDeleteRequest) (err error) {
err = s.DoTable(req.ContextId, ctx, func(st *state.State, t table.Editor) error {
return t.ColumnDelete(st, req)
})
return
}
func (s *service) TableColumnMove(ctx *state.Context, req pb.RpcBlockTableColumnMoveRequest) (err error) {
err = s.DoTable(req.ContextId, ctx, func(st *state.State, t table.Editor) error {
return t.ColumnMove(st, req)
})
return
}
func (s *service) TableRowDuplicate(ctx *state.Context, req pb.RpcBlockTableRowDuplicateRequest) (err error) {
err = s.DoTable(req.ContextId, ctx, func(st *state.State, t table.Editor) error {
return t.RowDuplicate(st, req)
})
return
}
func (s *service) TableColumnDuplicate(ctx *state.Context, req pb.RpcBlockTableColumnDuplicateRequest) (id string, err error) {
err = s.DoTable(req.ContextId, ctx, func(st *state.State, t table.Editor) error {
id, err = t.ColumnDuplicate(st, req)
return err
})
return id, err
}
func (s *service) TableExpand(ctx *state.Context, req pb.RpcBlockTableExpandRequest) (err error) {
err = s.DoTable(req.ContextId, ctx, func(st *state.State, t table.Editor) error {
return t.Expand(st, req)
})
return err
}
func (s *service) TableRowListFill(ctx *state.Context, req pb.RpcBlockTableRowListFillRequest) (err error) {
err = s.DoTable(req.ContextId, ctx, func(st *state.State, t table.Editor) error {
return t.RowListFill(st, req)
})
return err
}
func (s *service) TableRowListClean(ctx *state.Context, req pb.RpcBlockTableRowListCleanRequest) (err error) {
err = s.DoTable(req.ContextId, ctx, func(st *state.State, t table.Editor) error {
return t.RowListClean(st, req)
})
return err
}
func (s *service) TableRowSetHeader(ctx *state.Context, req pb.RpcBlockTableRowSetHeaderRequest) (err error) {
err = s.DoTable(req.ContextId, ctx, func(st *state.State, t table.Editor) error {
return t.RowSetHeader(st, req)
})
return err
}
func (s *service) TableSort(ctx *state.Context, req pb.RpcBlockTableSortRequest) (err error) {
err = s.DoTable(req.ContextId, ctx, func(st *state.State, t table.Editor) error {
return t.Sort(st, req)
})
return err
}
func (s *service) TableColumnListFill(ctx *state.Context, req pb.RpcBlockTableColumnListFillRequest) (err error) {
err = s.DoTable(req.ContextId, ctx, func(st *state.State, t table.Editor) error {
return t.ColumnListFill(st, req)
})
return err
}

View file

@ -105,11 +105,27 @@ func (bs *basic) Duplicate(ctx *state.Context, req pb.RpcBlockListDuplicateReque
return
}
// some types of blocks need a special duplication mechanism
type duplicatable interface {
Duplicate(s *state.State) (newId string, visitedIds []string, blocks []simple.Block, err error)
}
func (bs *basic) copy(s *state.State, sourceId string) (id string, err error) {
b := s.Get(sourceId)
if b == nil {
return "", smartblock.ErrSimpleBlockNotFound
}
if v, ok := b.(duplicatable); ok {
newId, _, blocks, err := v.Duplicate(s)
if err != nil {
return "", fmt.Errorf("custom block duplication: %w", err)
}
for _, b := range blocks {
s.Add(b)
}
return newId, nil
}
m := b.Copy().Model()
m.Id = "" // reset id
copy := simple.New(m)

View file

@ -296,29 +296,78 @@ func (cb *clipboard) pasteText(ctx *state.Context, req *pb.RpcBlockPasteRequest,
}
}
return cb.pasteAny(ctx, req, groupId)
}
func (cb *clipboard) replaceIds(anySlot []*model.Block) (anySlotreplacedIds []*model.Block) {
var oldToNew = make(map[string]string)
for _, b := range anySlot {
if b.Id == "" {
b.Id = bson.NewObjectId().Hex()
}
oldToNew[b.Id] = bson.NewObjectId().Hex()
}
for _, b := range anySlot {
b.Id = oldToNew[b.Id]
for i := range b.ChildrenIds {
b.ChildrenIds[i] = oldToNew[b.ChildrenIds[i]]
}
}
return anySlot
// some types of blocks need a special duplication mechanism
type duplicatable interface {
Duplicate(s *state.State) (newId string, visitedIds []string, blocks []simple.Block, err error)
}
func (cb *clipboard) pasteAny(ctx *state.Context, req *pb.RpcBlockPasteRequest, groupId string) (blockIds []string, uploadArr []pb.RpcBlockUploadRequest, caretPosition int32, isSameBlockCaret bool, err error) {
s := cb.NewStateCtx(ctx).SetGroupId(groupId)
ctrl := &pasteCtrl{s: s, ps: cb.blocksToState(cb.replaceIds(req.AnySlot))}
destState := state.NewDoc("", nil).(*state.State)
for _, b := range req.AnySlot {
if b.Id == "" {
b.Id = bson.NewObjectId().Hex()
}
}
srcState := cb.blocksToState(req.AnySlot)
visited := map[string]struct{}{}
src := srcState.Blocks()
srcBlocks := make([]simple.Block, 0, len(src))
for _, b := range src {
srcBlocks = append(srcBlocks, simple.New(b))
}
oldToNew := map[string]string{}
// Handle blocks that have custom duplication code. For example, simple tables
// have to have special ID for cells
for _, b := range srcBlocks {
if d, ok := b.(duplicatable); ok {
id, visitedIds, blocks, err2 := d.Duplicate(srcState)
if err2 != nil {
err = fmt.Errorf("custom duplicate: %w", err2)
return
}
oldToNew[b.Model().Id] = id
for _, b := range blocks {
destState.Add(b)
}
for _, id := range visitedIds {
visited[id] = struct{}{}
}
}
}
// Collect and generate necessary IDs. Ignore ids of blocks that have been duplicated by custom code
for _, b := range srcBlocks {
if _, ok := visited[b.Model().Id]; ok {
continue
}
oldToNew[b.Model().Id] = bson.NewObjectId().Hex()
}
// Remap IDs
for _, b := range srcBlocks {
if _, ok := visited[b.Model().Id]; ok {
continue
}
b.Model().Id = oldToNew[b.Model().Id]
for i, id := range b.Model().ChildrenIds {
b.Model().ChildrenIds[i] = oldToNew[id]
}
destState.Add(b)
}
destState.BlocksInit(destState)
state.CleanupLayouts(destState)
destState.Normalize(false)
ctrl := &pasteCtrl{s: s, ps: destState}
if err = ctrl.Exec(req); err != nil {
return
}

View file

@ -52,7 +52,6 @@ func (p *Dashboard) init(s *state.State) (err error) {
template.WithDetailName("Home"),
template.WithDetailIconEmoji("🏠"),
template.WithNoRootLink(p.Anytype().PredefinedBlocks().Archive),
template.WithRootLink(p.Anytype().PredefinedBlocks().SetPages, model.BlockContentLink_Dataview),
template.WithRequiredRelations(),
template.WithNoDuplicateLinks(),
); err != nil {

View file

@ -27,7 +27,19 @@ const DefaultDetailsFieldName = "_defaultRecordFields"
var log = logging.Logger("anytype-mw-editor-dataview")
var ErrMultiupdateWasNotAllowed = fmt.Errorf("multiupdate was not allowed")
var DefaultDataviewRelations = append(bundle.RequiredInternalRelations, bundle.RelationKeyDone)
var DefaultDataviewRelations = make([]bundle.RelationKey, 0, len(bundle.RequiredInternalRelations))
func init() {
// fill DefaultDataviewRelations
// deprecated: we should remove this after we merge relations as objects
for _, rel := range bundle.RequiredInternalRelations {
if bundle.MustGetRelation(rel).Hidden {
continue
}
DefaultDataviewRelations = append(DefaultDataviewRelations, rel)
}
DefaultDataviewRelations = append(DefaultDataviewRelations, bundle.RelationKeyDone)
}
type Dataview interface {
SetSource(ctx *state.Context, blockId string, source []string) (err error)

View file

@ -10,6 +10,7 @@ import (
_import "github.com/anytypeio/go-anytype-middleware/core/block/editor/import"
"github.com/anytypeio/go-anytype-middleware/core/block/editor/smartblock"
"github.com/anytypeio/go-anytype-middleware/core/block/editor/stext"
"github.com/anytypeio/go-anytype-middleware/core/block/editor/table"
"github.com/anytypeio/go-anytype-middleware/core/block/editor/template"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/bundle"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/localstore/objectstore"
@ -35,6 +36,7 @@ func NewPage(
Bookmark: bookmark.NewBookmark(sb, pageManager, bookmarkSvc),
Import: _import.NewImport(sb, importServices),
Dataview: dataview.NewDataview(sb),
Editor: table.NewEditor(sb),
}
}
@ -48,6 +50,7 @@ type Page struct {
bookmark.Bookmark
_import.Import
dataview.Dataview
table.Editor
}
func (p *Page) Init(ctx *smartblock.InitContext) (err error) {

View file

@ -10,7 +10,6 @@ import (
"github.com/anytypeio/go-anytype-middleware/pkg/lib/bundle"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/pb/model"
"github.com/anytypeio/go-anytype-middleware/util/pbtypes"
"github.com/globalsign/mgo/bson"
)
var ErrAlreadyHasDataviewBlock = fmt.Errorf("already has the dataview block")
@ -49,36 +48,7 @@ func (p *Set) Init(ctx *smartblock.InitContext) (err error) {
template.WithBlockEditRestricted(p.Id()),
template.WithCreatorRemovedFromFeaturedRelations,
}
if p.Id() == p.Anytype().PredefinedBlocks().SetPages && p.Pick(template.DataviewBlockId) == nil {
rels := pbtypes.MergeRelations(bundle.MustGetType(bundle.TypeKeyNote).Relations, bundle.MustGetRelations(dataview.DefaultDataviewRelations))
dataview := model.BlockContentOfDataview{
Dataview: &model.BlockContentDataview{
Source: []string{bundle.TypeKeyNote.URL()},
Relations: rels,
Views: []*model.BlockContentDataviewView{
{
Id: bson.NewObjectId().Hex(),
Type: model.BlockContentDataviewView_Table,
Name: "All notes",
Sorts: []*model.BlockContentDataviewSort{
{
RelationKey: bundle.RelationKeyLastModifiedDate.String(),
Type: model.BlockContentDataviewSort_Desc,
},
},
Relations: GetDefaultViewRelations(rels),
Filters: nil,
},
},
},
}
templates = append(templates,
template.WithDataview(dataview, false),
template.WithDetailName("Notes"),
template.WithDetail(bundle.RelationKeySetOf, pbtypes.StringList([]string{bundle.TypeKeyNote.URL()})),
template.WithDetailIconEmoji("📝"))
} else if dvBlock := p.Pick(template.DataviewBlockId); dvBlock != nil {
if dvBlock := p.Pick(template.DataviewBlockId); dvBlock != nil {
setOf := dvBlock.Model().GetDataview().Source
templates = append(templates, template.WithForcedDetail(bundle.RelationKeySetOf, pbtypes.StringList(setOf)))
// add missing done relation

View file

@ -105,6 +105,7 @@ type SmartBlock interface {
TemplateCreateFromObjectState() (*state.State, error)
SetObjectTypes(ctx *state.Context, objectTypes []string) (err error)
SetAlign(ctx *state.Context, align model.BlockAlign, ids ...string) error
SetVerticalAlign(ctx *state.Context, align model.BlockVerticalAlign, ids ...string) error
SetLayout(ctx *state.Context, layout model.ObjectTypeLayout) error
SetIsDeleted()
IsDeleted() bool
@ -988,6 +989,16 @@ func (sb *smartBlock) setAlign(s *state.State, align model.BlockAlign, ids ...st
return
}
func (sb *smartBlock) SetVerticalAlign(ctx *state.Context, align model.BlockVerticalAlign, ids ...string) (err error) {
s := sb.NewStateCtx(ctx)
for _, id := range ids {
if b := s.Get(id); b != nil {
b.Model().VerticalAlign = align
}
}
return sb.Apply(s)
}
func (sb *smartBlock) SetLayout(ctx *state.Context, layout model.ObjectTypeLayout) (err error) {
if err = sb.Restrictions().Object.Check(model.Restrictions_LayoutChange); err != nil {
return

View file

@ -77,6 +77,10 @@ func (st *SmartTest) SetAlign(ctx *state.Context, align model.BlockAlign, ids ..
return nil
}
func (st *SmartTest) SetVerticalAlign(ctx *state.Context, align model.BlockVerticalAlign, ids ...string) error {
return nil
}
func (st *SmartTest) SetLayout(ctx *state.Context, layout model.ObjectTypeLayout) error {
return nil
}

View file

@ -390,6 +390,8 @@ func (s *State) fillChanges(msgs []simple.EventMessage) {
updMsgs = append(updMsgs, msg.Msg)
case *pb.EventMessageValueOfBlockSetBookmark:
updMsgs = append(updMsgs, msg.Msg)
case *pb.EventMessageValueOfBlockSetVerticalAlign:
updMsgs = append(updMsgs, msg.Msg)
case *pb.EventMessageValueOfBlockSetDiv:
updMsgs = append(updMsgs, msg.Msg)
case *pb.EventMessageValueOfBlockSetText:

View file

@ -4,6 +4,7 @@ import (
"fmt"
"github.com/anytypeio/go-anytype-middleware/core/block/simple/latex"
"github.com/anytypeio/go-anytype-middleware/core/block/simple/table"
"github.com/anytypeio/go-anytype-middleware/util/slice"
"github.com/gogo/protobuf/types"
@ -34,6 +35,13 @@ func (s *State) applyEvent(ev *pb.EventMessage) (err error) {
}); err != nil {
return
}
case *pb.EventMessageValueOfBlockSetVerticalAlign:
if err = apply(o.BlockSetVerticalAlign.Id, func(b simple.Block) error {
b.Model().VerticalAlign = o.BlockSetVerticalAlign.VerticalAlign
return nil
}); err != nil {
return
}
case *pb.EventMessageValueOfBlockSetBackgroundColor:
if err = apply(o.BlockSetBackgroundColor.Id, func(b simple.Block) error {
b.Model().BackgroundColor = o.BlockSetBackgroundColor.BackgroundColor
@ -50,6 +58,17 @@ func (s *State) applyEvent(ev *pb.EventMessage) (err error) {
}); err != nil {
return
}
case *pb.EventMessageValueOfBlockSetTableRow:
if err = apply(o.BlockSetTableRow.Id, func(b simple.Block) error {
if tr, ok := b.(table.RowBlock); ok {
return tr.ApplyEvent(o.BlockSetTableRow)
}
return fmt.Errorf("not a table row block")
}); err != nil {
return
}
case *pb.EventMessageValueOfBlockSetDiv:
if err = apply(o.BlockSetDiv.Id, func(b simple.Block) error {
if d, ok := b.(base.DivBlock); ok {

View file

@ -19,6 +19,10 @@ func (s *State) Normalize(withLayouts bool) (err error) {
return s.normalize(withLayouts)
}
type Normalizable interface {
Normalize(s *State) error
}
func (s *State) normalize(withLayouts bool) (err error) {
if err = s.Iterate(func(b simple.Block) (isContinue bool) {
return true
@ -29,6 +33,15 @@ func (s *State) normalize(withLayouts bool) (err error) {
for _, b := range s.blocks {
s.normalizeChildren(b)
}
for _, b := range s.blocks {
if n, ok := b.(Normalizable); ok {
if err := n.Normalize(s); err != nil {
return fmt.Errorf("custom normalization for block %s: %w", b.Model().Id, err)
}
}
}
// remove empty layouts
for _, b := range s.blocks {
if layout := b.Model().GetLayout(); layout != nil {

View file

@ -0,0 +1,210 @@
package table
import (
"fmt"
"sort"
"github.com/anytypeio/go-anytype-middleware/core/block/editor/state"
"github.com/anytypeio/go-anytype-middleware/core/block/simple"
"github.com/anytypeio/go-anytype-middleware/core/block/simple/base"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/pb/model"
"github.com/anytypeio/go-anytype-middleware/util/pbtypes"
"github.com/globalsign/mgo/bson"
)
func init() {
simple.RegisterCreator(NewBlock)
}
func NewBlock(b *model.Block) simple.Block {
if c := b.GetTable(); c != nil {
return &block{
Base: base.NewBase(b).(*base.Base),
content: c,
}
}
return nil
}
type Block interface {
simple.Block
Normalize(s *state.State) error
Duplicate(s *state.State) (newId string, visitedIds []string, blocks []simple.Block, err error)
}
type block struct {
*base.Base
content *model.BlockContentTable
}
func (b *block) Copy() simple.Block {
return NewBlock(pbtypes.CopyBlock(b.Model()))
}
func (b *block) Normalize(s *state.State) error {
tb, err := NewTable(s, b.Id)
if err != nil {
log.Errorf("normalize table %s: broken table state", b.Model().Id)
return nil
}
colIdx := map[string]int{}
for i, c := range tb.Columns().ChildrenIds {
colIdx[c] = i
}
for _, rowId := range tb.Rows().ChildrenIds {
row := s.Get(rowId)
normalizeRow(colIdx, row)
}
if err := normalizeRows(s, tb); err != nil {
return fmt.Errorf("normalize rows: %w", err)
}
return nil
}
func (b *block) Duplicate(s *state.State) (newId string, visitedIds []string, blocks []simple.Block, err error) {
tb, err := NewTable(s, b.Id)
if err != nil {
err = fmt.Errorf("init table: %w", err)
return
}
visitedIds = append(visitedIds, b.Id)
colMapping := map[string]string{}
genId := func() string {
return bson.NewObjectId().Hex()
}
cols := pbtypes.CopyBlock(tb.Columns())
visitedIds = append(visitedIds, cols.Id)
cols.Id = ""
for i, colId := range cols.ChildrenIds {
col := s.Pick(colId)
if col == nil {
err = fmt.Errorf("column %s is not found", colId)
return
}
visitedIds = append(visitedIds, colId)
col = col.Copy()
col.Model().Id = genId()
blocks = append(blocks, col)
colMapping[colId] = col.Model().Id
cols.ChildrenIds[i] = col.Model().Id
}
blocks = append(blocks, simple.New(cols))
rows := pbtypes.CopyBlock(tb.Rows())
visitedIds = append(visitedIds, rows.Id)
rows.Id = ""
for i, rowId := range rows.ChildrenIds {
visitedIds = append(visitedIds, rowId)
row := s.Pick(rowId)
row = row.Copy()
row.Model().Id = genId()
blocks = append(blocks, row)
for j, cellId := range row.Model().ChildrenIds {
_, oldColId, err2 := ParseCellId(cellId)
if err2 != nil {
err = fmt.Errorf("parse cell id %s: %w", cellId, err2)
return
}
newColId, ok := colMapping[oldColId]
if !ok {
err = fmt.Errorf("column mapping for %s is not found", oldColId)
return
}
visitedIds = append(visitedIds, cellId)
cell := s.Pick(cellId)
cell = cell.Copy()
cell.Model().Id = makeCellId(row.Model().Id, newColId)
blocks = append(blocks, cell)
row.Model().ChildrenIds[j] = cell.Model().Id
}
rows.ChildrenIds[i] = row.Model().Id
}
blocks = append(blocks, simple.New(rows))
block := tb.block.Copy()
block.Model().Id = genId()
block.Model().ChildrenIds = []string{cols.Id, rows.Id}
blocks = append(blocks, block)
return block.Model().Id, visitedIds, blocks, nil
}
type rowSort struct {
indices []int
cells []string
touched bool
}
func (r *rowSort) Len() int {
return len(r.cells)
}
func (r *rowSort) Less(i, j int) bool {
return r.indices[i] < r.indices[j]
}
func (r *rowSort) Swap(i, j int) {
r.touched = true
r.indices[i], r.indices[j] = r.indices[j], r.indices[i]
r.cells[i], r.cells[j] = r.cells[j], r.cells[i]
}
func normalizeRows(s *state.State, tb *Table) error {
rows := s.Get(tb.Rows().Id)
var headers []string
normal := make([]string, 0, len(rows.Model().ChildrenIds))
for _, rowId := range rows.Model().ChildrenIds {
row, err := pickRow(s, rowId)
if err != nil {
return fmt.Errorf("pick row %s: %w", rowId, err)
}
if row.Model().GetTableRow().IsHeader {
headers = append(headers, rowId)
} else {
normal = append(normal, rowId)
}
}
rows.Model().ChildrenIds = append(headers, normal...)
return nil
}
func normalizeRow(colIdx map[string]int, row simple.Block) {
rs := &rowSort{
cells: make([]string, 0, len(row.Model().ChildrenIds)),
indices: make([]int, 0, len(row.Model().ChildrenIds)),
}
for _, id := range row.Model().ChildrenIds {
_, colId, err := ParseCellId(id)
if err != nil {
log.Warnf("normalize row %s: discard cell %s: invalid id", row.Model().Id, id)
rs.touched = true
continue
}
v, ok := colIdx[colId]
if !ok {
log.Warnf("normalize row %s: discard cell %s: column %s not found", row.Model().Id, id, colId)
rs.touched = true
continue
}
rs.cells = append(rs.cells, id)
rs.indices = append(rs.indices, v)
}
sort.Sort(rs)
if rs.touched {
row.Model().ChildrenIds = rs.cells
}
}

View file

@ -0,0 +1,118 @@
package table
import (
"testing"
"github.com/anytypeio/go-anytype-middleware/core/block/editor/state"
"github.com/anytypeio/go-anytype-middleware/core/block/simple/base"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/pb/model"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNormalize(t *testing.T) {
for _, tc := range []struct {
name string
source *state.State
want *state.State
}{
{
name: "empty",
source: mkTestTable([]string{"col1", "col2"}, []string{"row1", "row2"}, [][]string{}),
want: mkTestTable([]string{"col1", "col2"}, []string{"row1", "row2"}, [][]string{}),
},
{
name: "invalid ids",
source: mkTestTable([]string{"col1", "col2"}, []string{"row1", "row2"}, [][]string{
{"row1-c11", "row1-col2"},
{"row2-col3"},
}),
want: mkTestTable([]string{"col1", "col2"}, []string{"row1", "row2"}, [][]string{
{"row1-col2"},
{},
}),
},
{
name: "wrong column order",
source: mkTestTable([]string{"col1", "col2", "col3"}, []string{"row1", "row2"}, [][]string{
{"row1-col3", "row1-col1", "row1-col2"},
{"row2-col3", "row2-c1", "row2-col1"},
}),
want: mkTestTable([]string{"col1", "col2", "col3"}, []string{"row1", "row2"}, [][]string{
{"row1-col1", "row1-col2", "row1-col3"},
{"row2-col1", "row2-col3"},
}),
},
{
name: "wrong place for header rows",
source: mkTestTable([]string{"col1", "col2", "col3"}, []string{"row1", "row2", "row3"}, nil,
withRowBlockContents(map[string]*model.BlockContentTableRow{
"row3": {IsHeader: true},
})),
want: mkTestTable([]string{"col1", "col2", "col3"}, []string{"row3", "row1", "row2"}, nil,
withRowBlockContents(map[string]*model.BlockContentTableRow{
"row3": {IsHeader: true},
})),
},
} {
t.Run(tc.name, func(t *testing.T) {
tb, err := NewTable(tc.source, "table")
require.NoError(t, err)
st := tc.source.Copy()
err = tb.block.(Block).Normalize(st)
require.NoError(t, err)
assert.Equal(t, tc.want.Blocks(), st.Blocks())
})
}
}
func TestDuplicate(t *testing.T) {
s := mkTestTable([]string{"col1", "col2", "col3"}, []string{"row1", "row2"},
[][]string{
{"row1-col1", "row1-col3"},
{"row2-col1", "row2-col2"},
}, withBlockContents(map[string]*model.Block{
"row1-col1": mkTextBlock("11"),
"row1-col3": mkTextBlock("13"),
"row2-col1": mkTextBlock("21"),
"row2-col2": mkTextBlock("22"),
}))
old, err := NewTable(s, "table")
require.NoError(t, err)
b := block{
Base: base.NewBase(&model.Block{Id: "table"}).(*base.Base),
}
newId, visitedId, blocks, err := b.Duplicate(s)
require.NoError(t, err)
for _, b := range blocks {
s.Add(b)
}
assert.ElementsMatch(t, []string{"table", "columns", "rows", "col1", "col2", "col3", "row1", "row2", "row1-col1", "row1-col3", "row2-col1", "row2-col2"}, visitedId)
got, err := NewTable(s, newId)
require.NoError(t, err)
assertNotEqual := func(old, new *model.Block) {
assert.NotEmpty(t, new.Id)
assert.NotEqual(t, old.Id, new.Id)
assert.Equal(t, len(old.ChildrenIds), len(new.ChildrenIds))
assert.NotEqual(t, old.ChildrenIds, new.ChildrenIds)
}
assertNotEqual(old.block.Model(), got.block.Model())
assertNotEqual(old.Columns(), got.Columns())
assertNotEqual(old.Rows(), got.Rows())
for i, oldId := range old.Rows().ChildrenIds {
newId := got.Rows().ChildrenIds[i]
oldRow := s.Pick(oldId)
newRow := s.Pick(newId)
assertNotEqual(oldRow.Model(), newRow.Model())
}
}

View file

@ -0,0 +1,822 @@
package table
import (
"fmt"
"sort"
"strings"
"github.com/anytypeio/go-anytype-middleware/core/block/editor/basic"
"github.com/anytypeio/go-anytype-middleware/core/block/editor/smartblock"
"github.com/anytypeio/go-anytype-middleware/core/block/editor/state"
"github.com/anytypeio/go-anytype-middleware/core/block/simple"
"github.com/anytypeio/go-anytype-middleware/core/block/simple/table"
"github.com/anytypeio/go-anytype-middleware/core/block/simple/text"
"github.com/anytypeio/go-anytype-middleware/pb"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/logging"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/pb/model"
"github.com/globalsign/mgo/bson"
)
var log = logging.Logger("anytype-simple-tables")
func NewEditor(sb smartblock.SmartBlock) Editor {
genId := func() string {
return bson.NewObjectId().Hex()
}
t := editor{
SmartBlock: sb,
generateRowId: genId,
generateColId: genId,
}
sb.AddHook(t.cleanupTables, smartblock.HookOnBlockClose)
return &t
}
type Editor interface {
TableCreate(s *state.State, req pb.RpcBlockTableCreateRequest) (id string, err error)
Expand(s *state.State, req pb.RpcBlockTableExpandRequest) error
RowCreate(s *state.State, req pb.RpcBlockTableRowCreateRequest) error
RowDelete(s *state.State, req pb.RpcBlockTableRowDeleteRequest) error
RowDuplicate(s *state.State, req pb.RpcBlockTableRowDuplicateRequest) error
RowListFill(s *state.State, req pb.RpcBlockTableRowListFillRequest) error
RowListClean(s *state.State, req pb.RpcBlockTableRowListCleanRequest) error
RowSetHeader(s *state.State, req pb.RpcBlockTableRowSetHeaderRequest) error
ColumnCreate(s *state.State, req pb.RpcBlockTableColumnCreateRequest) error
ColumnDelete(s *state.State, req pb.RpcBlockTableColumnDeleteRequest) error
ColumnMove(s *state.State, req pb.RpcBlockTableColumnMoveRequest) error
ColumnDuplicate(s *state.State, req pb.RpcBlockTableColumnDuplicateRequest) (id string, err error)
ColumnListFill(s *state.State, req pb.RpcBlockTableColumnListFillRequest) error
Sort(s *state.State, req pb.RpcBlockTableSortRequest) error
}
type editor struct {
smartblock.SmartBlock
generateRowId func() string
generateColId func() string
}
func (t *editor) TableCreate(s *state.State, req pb.RpcBlockTableCreateRequest) (id string, err error) {
if err = t.Restrictions().Object.Check(model.Restrictions_Blocks); err != nil {
return
}
if t.Type() == model.SmartBlockType_Set {
return "", basic.ErrNotSupported
}
id, err = basic.CreateBlock(s, "", pb.RpcBlockCreateRequest{
ContextId: req.ContextId,
TargetId: req.TargetId,
Position: req.Position,
Block: &model.Block{
Content: &model.BlockContentOfTable{
Table: &model.BlockContentTable{},
},
},
})
if err != nil {
return "", fmt.Errorf("create block: %w", err)
}
columnIds := make([]string, 0, req.Columns)
for i := uint32(0); i < req.Columns; i++ {
id, err := t.addColumnHeader(s)
if err != nil {
return "", err
}
columnIds = append(columnIds, id)
}
columnsLayout := simple.New(&model.Block{
ChildrenIds: columnIds,
Content: &model.BlockContentOfLayout{
Layout: &model.BlockContentLayout{
Style: model.BlockContentLayout_TableColumns,
},
},
})
if !s.Add(columnsLayout) {
return "", fmt.Errorf("add columns block")
}
rowIds := make([]string, 0, req.Rows)
for i := uint32(0); i < req.Rows; i++ {
id, err := t.addRow(s)
if err != nil {
return "", err
}
rowIds = append(rowIds, id)
}
rowsLayout := simple.New(&model.Block{
ChildrenIds: rowIds,
Content: &model.BlockContentOfLayout{
Layout: &model.BlockContentLayout{
Style: model.BlockContentLayout_TableRows,
},
},
})
if !s.Add(rowsLayout) {
return "", fmt.Errorf("add rows block")
}
table := s.Get(id)
table.Model().ChildrenIds = []string{columnsLayout.Model().Id, rowsLayout.Model().Id}
if req.WithHeaderRow {
headerId := rowIds[0]
err = t.RowSetHeader(s, pb.RpcBlockTableRowSetHeaderRequest{
TargetId: headerId,
IsHeader: true,
})
if err != nil {
return "", fmt.Errorf("row set header: %w", err)
}
err = t.RowListFill(s, pb.RpcBlockTableRowListFillRequest{
BlockIds: []string{headerId},
})
if err != nil {
return "", fmt.Errorf("fill header row: %w", err)
}
row, err := getRow(s, headerId)
if err != nil {
return "", fmt.Errorf("get header row: %w", err)
}
for _, cellId := range row.Model().ChildrenIds {
cell := s.Get(cellId)
if cell == nil {
return "", fmt.Errorf("get header cell id %s", cellId)
}
cell.Model().BackgroundColor = "grey"
}
}
return id, nil
}
func (t *editor) RowCreate(s *state.State, req pb.RpcBlockTableRowCreateRequest) error {
switch req.Position {
case model.Block_Top, model.Block_Bottom:
default:
return fmt.Errorf("position is not supported")
}
rowId, err := t.addRow(s)
if err != nil {
return err
}
if err = s.InsertTo(req.TargetId, req.Position, rowId); err != nil {
return fmt.Errorf("insert row: %w", err)
}
return nil
}
func (t *editor) RowDelete(s *state.State, req pb.RpcBlockTableRowDeleteRequest) error {
_, err := pickRow(s, req.TargetId)
if err != nil {
return fmt.Errorf("pick target row: %w", err)
}
if !s.Unlink(req.TargetId) {
return fmt.Errorf("unlink row block")
}
return nil
}
func (t *editor) ColumnDelete(s *state.State, req pb.RpcBlockTableColumnDeleteRequest) error {
_, err := pickColumn(s, req.TargetId)
if err != nil {
return fmt.Errorf("pick target column: %w", err)
}
tb, err := NewTable(s, req.TargetId)
if err != nil {
return fmt.Errorf("initialize table state: %w", err)
}
for _, rowId := range tb.Rows().ChildrenIds {
row, err := pickRow(s, rowId)
if err != nil {
return fmt.Errorf("pick row %s: %w", rowId, err)
}
for _, cellId := range row.Model().ChildrenIds {
_, colId, err := ParseCellId(cellId)
if err != nil {
return fmt.Errorf("parse cell id %s: %w", cellId, err)
}
if colId == req.TargetId {
if !s.Unlink(cellId) {
return fmt.Errorf("unlink cell %s", cellId)
}
break
}
}
}
if !s.Unlink(req.TargetId) {
return fmt.Errorf("unlink column header")
}
return nil
}
func (t *editor) ColumnMove(s *state.State, req pb.RpcBlockTableColumnMoveRequest) error {
switch req.Position {
case model.Block_Left:
req.Position = model.Block_Top
case model.Block_Right:
req.Position = model.Block_Bottom
default:
return fmt.Errorf("position is not supported")
}
_, err := pickColumn(s, req.TargetId)
if err != nil {
return fmt.Errorf("get target column: %w", err)
}
_, err = pickColumn(s, req.DropTargetId)
if err != nil {
return fmt.Errorf("get drop target column: %w", err)
}
tb, err := NewTable(s, req.TargetId)
if err != nil {
return fmt.Errorf("init table block: %w", err)
}
if !s.Unlink(req.TargetId) {
return fmt.Errorf("unlink target column")
}
if err = s.InsertTo(req.DropTargetId, req.Position, req.TargetId); err != nil {
return fmt.Errorf("insert column: %w", err)
}
colIdx := map[string]int{}
for i, c := range tb.Columns().ChildrenIds {
colIdx[c] = i
}
for _, id := range tb.Rows().ChildrenIds {
row, err := getRow(s, id)
if err != nil {
return fmt.Errorf("get row %s: %w", id, err)
}
normalizeRow(colIdx, row)
}
return nil
}
func (t *editor) RowDuplicate(s *state.State, req pb.RpcBlockTableRowDuplicateRequest) error {
srcRow, err := pickRow(s, req.BlockId)
if err != nil {
return fmt.Errorf("pick source row: %w", err)
}
newRow := srcRow.Copy()
newRow.Model().Id = t.generateRowId()
if !s.Add(newRow) {
return fmt.Errorf("add new row %s", newRow.Model().Id)
}
if err = s.InsertTo(req.TargetId, req.Position, newRow.Model().Id); err != nil {
return fmt.Errorf("insert column: %w", err)
}
for i, srcId := range newRow.Model().ChildrenIds {
cell := s.Pick(srcId)
if cell == nil {
return fmt.Errorf("cell %s is not found", srcId)
}
_, colId, err := ParseCellId(srcId)
if err != nil {
return fmt.Errorf("parse cell id %s: %w", srcId, err)
}
newCell := cell.Copy()
newCell.Model().Id = makeCellId(newRow.Model().Id, colId)
if !s.Add(newCell) {
return fmt.Errorf("add new cell %s", newCell.Model().Id)
}
newRow.Model().ChildrenIds[i] = newCell.Model().Id
}
return nil
}
func (t *editor) RowListFill(s *state.State, req pb.RpcBlockTableRowListFillRequest) error {
if len(req.BlockIds) == 0 {
return fmt.Errorf("empty row list")
}
tb, err := NewTable(s, req.BlockIds[0])
if err != nil {
return fmt.Errorf("init table: %w", err)
}
columns := tb.Columns().ChildrenIds
for _, rowId := range req.BlockIds {
row, err := getRow(s, rowId)
if err != nil {
return fmt.Errorf("get row %s: %w", rowId, err)
}
newIds := make([]string, 0, len(columns))
for _, colId := range columns {
id := makeCellId(rowId, colId)
newIds = append(newIds, id)
if !s.Exists(id) {
_, err := addCell(s, rowId, colId)
if err != nil {
return fmt.Errorf("add cell %s: %w", id, err)
}
}
}
row.Model().ChildrenIds = newIds
}
return nil
}
func (t *editor) RowListClean(s *state.State, req pb.RpcBlockTableRowListCleanRequest) error {
if len(req.BlockIds) == 0 {
return fmt.Errorf("empty row list")
}
for _, rowId := range req.BlockIds {
row, err := pickRow(s, rowId)
if err != nil {
return fmt.Errorf("pick row: %w", err)
}
for _, cellId := range row.Model().ChildrenIds {
cell := s.Pick(cellId)
if v, ok := cell.(text.Block); ok && v.IsEmpty() {
s.Unlink(cellId)
}
}
}
return nil
}
func (t *editor) RowSetHeader(s *state.State, req pb.RpcBlockTableRowSetHeaderRequest) error {
tb, err := NewTable(s, req.TargetId)
if err != nil {
return fmt.Errorf("init table: %w", err)
}
row, err := getRow(s, req.TargetId)
if err != nil {
return fmt.Errorf("get target row: %w", err)
}
if row.Model().GetTableRow().IsHeader != req.IsHeader {
row.Model().GetTableRow().IsHeader = req.IsHeader
err = normalizeRows(s, tb)
if err != nil {
return fmt.Errorf("normalize rows: %w", err)
}
}
return nil
}
func (t *editor) ColumnListFill(s *state.State, req pb.RpcBlockTableColumnListFillRequest) error {
if len(req.BlockIds) == 0 {
return fmt.Errorf("empty row list")
}
tb, err := NewTable(s, req.BlockIds[0])
if err != nil {
return fmt.Errorf("init table: %w", err)
}
rows := tb.Rows().ChildrenIds
for _, colId := range req.BlockIds {
for _, rowId := range rows {
id := makeCellId(rowId, colId)
if s.Exists(id) {
continue
}
_, err := addCell(s, rowId, colId)
if err != nil {
return fmt.Errorf("add cell %s: %w", id, err)
}
row, err := getRow(s, rowId)
if err != nil {
return fmt.Errorf("get row %s: %w", rowId, err)
}
row.Model().ChildrenIds = append(row.Model().ChildrenIds, id)
}
}
colIdx := map[string]int{}
for i, id := range tb.Columns().ChildrenIds {
colIdx[id] = i
}
for _, rowId := range rows {
row, err := getRow(s, rowId)
if err != nil {
return fmt.Errorf("get row %s: %w", rowId, err)
}
normalizeRow(colIdx, row)
}
return nil
}
func (t *editor) cleanupTables(_ smartblock.ApplyInfo) error {
s := t.NewState()
err := s.Iterate(func(b simple.Block) bool {
if b.Model().GetTable() == nil {
return true
}
tb, err := NewTable(s, b.Model().Id)
if err != nil {
log.Errorf("cleanup: init table %s: %s", b.Model().Id, err)
return true
}
err = t.RowListClean(s, pb.RpcBlockTableRowListCleanRequest{
BlockIds: tb.Rows().ChildrenIds,
})
if err != nil {
log.Errorf("cleanup table %s: %s", b.Model().Id, err)
return true
}
return true
})
if err != nil {
log.Errorf("cleanup iterate: %s", err)
}
if err = t.Apply(s); err != nil {
log.Errorf("cleanup apply: %s", err)
}
return nil
}
func (t *editor) ColumnCreate(s *state.State, req pb.RpcBlockTableColumnCreateRequest) error {
switch req.Position {
case model.Block_Left:
req.Position = model.Block_Top
case model.Block_Right:
req.Position = model.Block_Bottom
default:
return fmt.Errorf("position is not supported")
}
_, err := pickColumn(s, req.TargetId)
if err != nil {
return fmt.Errorf("pick column: %w", err)
}
colId, err := t.addColumnHeader(s)
if err != nil {
return err
}
if err = s.InsertTo(req.TargetId, req.Position, colId); err != nil {
return fmt.Errorf("insert column header: %w", err)
}
return nil
}
func (t *editor) ColumnDuplicate(s *state.State, req pb.RpcBlockTableColumnDuplicateRequest) (id string, err error) {
switch req.Position {
case model.Block_Left:
req.Position = model.Block_Top
case model.Block_Right:
req.Position = model.Block_Bottom
default:
return "", fmt.Errorf("position is not supported")
}
srcCol, err := pickColumn(s, req.BlockId)
if err != nil {
return "", fmt.Errorf("pick source column: %w", err)
}
_, err = pickColumn(s, req.TargetId)
if err != nil {
return "", fmt.Errorf("pick target column: %w", err)
}
tb, err := NewTable(s, req.TargetId)
if err != nil {
return "", fmt.Errorf("init table block: %w", err)
}
newCol := srcCol.Copy()
newCol.Model().Id = t.generateColId()
if !s.Add(newCol) {
return "", fmt.Errorf("add column block")
}
if err = s.InsertTo(req.TargetId, req.Position, newCol.Model().Id); err != nil {
return "", fmt.Errorf("insert column: %w", err)
}
colIdx := map[string]int{}
for i, c := range tb.Columns().ChildrenIds {
colIdx[c] = i
}
for _, rowId := range tb.Rows().ChildrenIds {
row, err := getRow(s, rowId)
if err != nil {
return "", fmt.Errorf("get row %s: %w", rowId, err)
}
var cellId string
for _, id := range row.Model().ChildrenIds {
_, colId, err := ParseCellId(id)
if err != nil {
return "", fmt.Errorf("parse cell %s in row %s: %w", cellId, rowId, err)
}
if colId == req.BlockId {
cellId = id
break
}
}
if cellId == "" {
continue
}
cell := s.Pick(cellId)
if cell == nil {
return "", fmt.Errorf("cell %s is not found", cellId)
}
cell = cell.Copy()
cell.Model().Id = makeCellId(rowId, newCol.Model().Id)
if !s.Add(cell) {
return "", fmt.Errorf("add cell block")
}
row.Model().ChildrenIds = append(row.Model().ChildrenIds, cell.Model().Id)
normalizeRow(colIdx, row)
}
return newCol.Model().Id, nil
}
func (t *editor) Expand(s *state.State, req pb.RpcBlockTableExpandRequest) error {
tb, err := NewTable(s, req.TargetId)
if err != nil {
return fmt.Errorf("init table block: %w", err)
}
for i := uint32(0); i < req.Columns; i++ {
cols := tb.Columns()
err := t.ColumnCreate(s, pb.RpcBlockTableColumnCreateRequest{
TargetId: cols.ChildrenIds[len(cols.ChildrenIds)-1],
Position: model.Block_Right,
})
if err != nil {
return fmt.Errorf("create column: %w", err)
}
}
for i := uint32(0); i < req.Rows; i++ {
rows := tb.Rows()
err := t.RowCreate(s, pb.RpcBlockTableRowCreateRequest{
TargetId: rows.ChildrenIds[len(rows.ChildrenIds)-1],
Position: model.Block_Bottom,
})
if err != nil {
return fmt.Errorf("create row: %w", err)
}
}
return nil
}
func (t *editor) Sort(s *state.State, req pb.RpcBlockTableSortRequest) error {
_, err := pickColumn(s, req.ColumnId)
if err != nil {
return fmt.Errorf("pick column: %w", err)
}
tb, err := NewTable(s, req.ColumnId)
if err != nil {
return fmt.Errorf("init table block: %w", err)
}
rows := s.Get(tb.Rows().Id)
sorter := tableSorter{
rowIds: make([]string, 0, len(rows.Model().ChildrenIds)),
values: make([]string, len(rows.Model().ChildrenIds)),
}
var headers []string
var i int
for _, rowId := range rows.Model().ChildrenIds {
row, err := pickRow(s, rowId)
if err != nil {
return fmt.Errorf("pick row %s: %w", rowId, err)
}
if row.Model().GetTableRow().GetIsHeader() {
headers = append(headers, rowId)
continue
}
sorter.rowIds = append(sorter.rowIds, rowId)
for _, cellId := range row.Model().ChildrenIds {
_, colId, err := ParseCellId(cellId)
if err != nil {
return fmt.Errorf("parse cell id %s: %w", cellId, err)
}
if colId == req.ColumnId {
cell := s.Pick(cellId)
if cell == nil {
return fmt.Errorf("cell %s is not found", cellId)
}
sorter.values[i] = cell.Model().GetText().GetText()
}
}
i++
}
if req.Type == model.BlockContentDataviewSort_Asc {
sort.Stable(sorter)
} else {
sort.Stable(sort.Reverse(sorter))
}
rows.Model().ChildrenIds = append(headers, sorter.rowIds...)
return nil
}
type tableSorter struct {
rowIds []string
values []string
}
func (t tableSorter) Len() int {
return len(t.rowIds)
}
func (t tableSorter) Less(i, j int) bool {
return t.values[i] < t.values[j]
}
func (t tableSorter) Swap(i, j int) {
t.values[i], t.values[j] = t.values[j], t.values[i]
t.rowIds[i], t.rowIds[j] = t.rowIds[j], t.rowIds[i]
}
func (t *editor) addColumnHeader(s *state.State) (string, error) {
b := simple.New(&model.Block{
Id: t.generateColId(),
Content: &model.BlockContentOfTableColumn{
TableColumn: &model.BlockContentTableColumn{},
},
})
if !s.Add(b) {
return "", fmt.Errorf("add column block")
}
return b.Model().Id, nil
}
func (t *editor) addRow(s *state.State) (string, error) {
row := simple.New(&model.Block{
Id: t.generateRowId(),
Content: &model.BlockContentOfTableRow{
TableRow: &model.BlockContentTableRow{},
},
})
if !s.Add(row) {
return "", fmt.Errorf("add row block")
}
return row.Model().Id, nil
}
func getRow(s *state.State, id string) (simple.Block, error) {
b := s.Get(id)
if b == nil {
return nil, fmt.Errorf("row is not found")
}
_, ok := b.(table.RowBlock)
if !ok {
return nil, fmt.Errorf("block is not a row")
}
return b, nil
}
func pickRow(s *state.State, id string) (simple.Block, error) {
b := s.Pick(id)
if b == nil {
return nil, fmt.Errorf("row is not found")
}
if b.Model().GetTableRow() == nil {
return nil, fmt.Errorf("block is not a row")
}
return b, nil
}
func pickColumn(s *state.State, id string) (simple.Block, error) {
b := s.Pick(id)
if b == nil {
return nil, fmt.Errorf("block is not found")
}
if b.Model().GetTableColumn() == nil {
return nil, fmt.Errorf("block is not a column")
}
return b, nil
}
func makeCellId(rowId, colId string) string {
return fmt.Sprintf("%s-%s", rowId, colId)
}
func ParseCellId(id string) (rowId string, colId string, err error) {
toks := strings.SplitN(id, "-", 2)
if len(toks) != 2 {
return "", "", fmt.Errorf("invalid id: must contains rowId and colId")
}
return toks[0], toks[1], nil
}
func addCell(s *state.State, rowId, colId string) (string, error) {
tb := simple.New(&model.Block{
Id: makeCellId(rowId, colId),
Content: &model.BlockContentOfText{
Text: &model.BlockContentText{},
},
})
if !s.Add(tb) {
return "", fmt.Errorf("add text block")
}
return tb.Model().Id, nil
}
// Table aggregates valid table structure in state
type Table struct {
s *state.State
block simple.Block
}
// NewTable creates helper for easy access to various parts of the table.
// It receives any id that belongs to table structure and search for the root table block
func NewTable(s *state.State, id string) (*Table, error) {
tb := Table{
s: s,
}
next := s.Pick(id)
for next != nil {
if next.Model().GetTable() != nil {
tb.block = next
break
}
next = s.PickParentOf(next.Model().Id)
}
if tb.block == nil {
return nil, fmt.Errorf("root table block is not found")
}
if len(tb.block.Model().ChildrenIds) != 2 {
return nil, fmt.Errorf("inconsistent state: table block")
}
if tb.Columns() == nil {
return nil, fmt.Errorf("columns block is not found")
}
if tb.Rows() == nil {
return nil, fmt.Errorf("rows block is not found")
}
return &tb, nil
}
func (tb Table) Block() simple.Block {
return tb.block
}
func (tb Table) Columns() *model.Block {
b := tb.s.Pick(tb.block.Model().ChildrenIds[0])
if b == nil ||
b.Model().GetLayout() == nil ||
b.Model().GetLayout().GetStyle() != model.BlockContentLayout_TableColumns {
return nil
}
return b.Model()
}
func (tb Table) Rows() *model.Block {
b := tb.s.Pick(tb.block.Model().ChildrenIds[1])
if b == nil ||
b.Model().GetLayout() == nil ||
b.Model().GetLayout().GetStyle() != model.BlockContentLayout_TableRows {
return nil
}
return b.Model()
}

File diff suppressed because it is too large Load diff

View file

@ -30,6 +30,8 @@ import (
"github.com/anytypeio/go-anytype-middleware/core/block/editor/smartblock"
"github.com/anytypeio/go-anytype-middleware/core/block/editor/state"
"github.com/anytypeio/go-anytype-middleware/core/block/editor/stext"
"github.com/anytypeio/go-anytype-middleware/core/block/editor/table"
_ "github.com/anytypeio/go-anytype-middleware/core/block/editor/table"
"github.com/anytypeio/go-anytype-middleware/core/block/process"
"github.com/anytypeio/go-anytype-middleware/core/block/restriction"
_ "github.com/anytypeio/go-anytype-middleware/core/block/simple/file"
@ -141,8 +143,11 @@ type Service interface {
SetTextColor(ctx *state.Context, contextId string, color string, blockIds ...string) error
SetTextMark(ctx *state.Context, id string, mark *model.BlockContentTextMark, ids ...string) error
SetTextIcon(ctx *state.Context, contextId, image, emoji string, blockIds ...string) error
ClearTextStyle(ctx *state.Context, contextId string, blockIds ...string) error
ClearTextContent(ctx *state.Context, contextId string, blockIds ...string) error
SetBackgroundColor(ctx *state.Context, contextId string, color string, blockIds ...string) error
SetAlign(ctx *state.Context, contextId string, align model.BlockAlign, blockIds ...string) (err error)
SetVerticalAlign(ctx *state.Context, contextId string, align model.BlockVerticalAlign, blockIds ...string) (err error)
SetLayout(ctx *state.Context, id string, layout model.ObjectTypeLayout) error
SetLinkAppearance(ctx *state.Context, req pb.RpcBlockLinkListSetAppearanceRequest) (err error)
@ -187,6 +192,21 @@ type Service interface {
ObjectBookmarkFetch(req pb.RpcObjectBookmarkFetchRequest) (err error)
ObjectToBookmark(id string, url string) (newId string, err error)
CreateTableBlock(ctx *state.Context, req pb.RpcBlockTableCreateRequest) (id string, err error)
TableExpand(ctx *state.Context, req pb.RpcBlockTableExpandRequest) (err error)
TableRowCreate(ctx *state.Context, req pb.RpcBlockTableRowCreateRequest) (err error)
TableRowDuplicate(ctx *state.Context, req pb.RpcBlockTableRowDuplicateRequest) (err error)
TableRowDelete(ctx *state.Context, req pb.RpcBlockTableRowDeleteRequest) (err error)
TableRowListFill(ctx *state.Context, req pb.RpcBlockTableRowListFillRequest) (err error)
TableRowListClean(ctx *state.Context, req pb.RpcBlockTableRowListCleanRequest) (err error)
TableRowSetHeader(ctx *state.Context, req pb.RpcBlockTableRowSetHeaderRequest) (err error)
TableColumnCreate(ctx *state.Context, req pb.RpcBlockTableColumnCreateRequest) (err error)
TableColumnDuplicate(ctx *state.Context, req pb.RpcBlockTableColumnDuplicateRequest) (id string, err error)
TableColumnMove(ctx *state.Context, req pb.RpcBlockTableColumnMoveRequest) (err error)
TableColumnDelete(ctx *state.Context, req pb.RpcBlockTableColumnDeleteRequest) (err error)
TableColumnListFill(ctx *state.Context, req pb.RpcBlockTableColumnListFillRequest) (err error)
TableSort(ctx *state.Context, req pb.RpcBlockTableSortRequest) (err error)
SetRelationKey(ctx *state.Context, request pb.RpcBlockRelationSetKeyRequest) error
AddRelationBlock(ctx *state.Context, request pb.RpcBlockRelationAddRequest) error
@ -295,7 +315,6 @@ func (s *service) initPredefinedBlocks() {
s.anytype.PredefinedBlocks().Profile,
s.anytype.PredefinedBlocks().Archive,
s.anytype.PredefinedBlocks().Home,
s.anytype.PredefinedBlocks().SetPages,
s.anytype.PredefinedBlocks().MarketplaceType,
s.anytype.PredefinedBlocks().MarketplaceRelation,
s.anytype.PredefinedBlocks().MarketplaceTemplate,
@ -863,15 +882,11 @@ func (s *service) CreateSmartBlockFromState(ctx context.Context, sbType coresb.S
}
}
_, err = objectstore.GetObjectType(s.anytype.ObjectStore(), objectTypes[0])
if err != nil {
return "", nil, fmt.Errorf("object type not found")
}
var workspaceId string
if details != nil && details.Fields != nil {
for k, v := range details.Fields {
createState.SetDetail(k, v)
// TODO: add relations to relationIds
}
detailsWorkspaceId := details.Fields[bundle.RelationKeyWorkspaceId.String()]
@ -933,12 +948,26 @@ func (s *service) CreateSmartBlockFromState(ctx context.Context, sbType coresb.S
func (s *service) CreateLinkToTheNewObject(ctx *state.Context, groupId string, req pb.RpcBlockLinkCreateWithObjectRequest) (linkId string, objectId string, err error) {
req.Details = internalflag.AddToDetails(req.Details, req.InternalFlags)
creator := func(ctx context.Context) (string, error) {
objectId, _, err = s.CreateSmartBlockFromTemplate(ctx, coresb.SmartBlockTypePage, req.Details, nil, req.TemplateId)
if err != nil {
return objectId, fmt.Errorf("create smartblock error: %v", err)
var creator func(ctx context.Context) (string, error)
if pbtypes.GetString(req.Details, bundle.RelationKeyType.String()) == bundle.TypeKeySet.URL() {
creator = func(ctx context.Context) (string, error) {
objectId, err = s.CreateSet(pb.RpcObjectCreateSetRequest{
Details: req.Details,
})
if err != nil {
return objectId, fmt.Errorf("create smartblock error: %v", err)
}
return objectId, nil
}
} else {
creator = func(ctx context.Context) (string, error) {
objectId, _, err = s.CreateSmartBlockFromTemplate(ctx, coresb.SmartBlockTypePage, req.Details, nil, req.TemplateId)
if err != nil {
return objectId, fmt.Errorf("create smartblock error: %v", err)
}
return objectId, nil
}
return objectId, nil
}
if req.ContextId != "" {
@ -1143,6 +1172,25 @@ func (s *service) DoBasic(id string, apply func(b basic.Basic) error) error {
return fmt.Errorf("basic operation not available for this block type: %T", sb)
}
func (s *service) DoTable(id string, ctx *state.Context, apply func(st *state.State, b table.Editor) error) error {
sb, release, err := s.pickBlock(context.TODO(), id)
if err != nil {
return err
}
defer release()
if bb, ok := sb.(table.Editor); ok {
sb.Lock()
defer sb.Unlock()
st := sb.NewStateCtx(ctx)
if err := apply(st, bb); err != nil {
return fmt.Errorf("apply function: %w", err)
}
return sb.Apply(st)
}
return fmt.Errorf("table operation not available for this block type: %T", sb)
}
func (s *service) DoLinksCollection(id string, apply func(b basic.Basic) error) error {
sb, release, err := s.pickBlock(context.TODO(), id)
if err != nil {

View file

@ -82,6 +82,13 @@ func (s *Base) Diff(block simple.Block) (msgs []simple.EventMessage, err error)
}}}
msgs = append(msgs, simple.EventMessage{Msg: m})
}
if s.VerticalAlign != m.VerticalAlign {
m := &pb.EventMessage{Value: &pb.EventMessageValueOfBlockSetVerticalAlign{BlockSetVerticalAlign: &pb.EventBlockSetVerticalAlign{
Id: s.Id,
VerticalAlign: m.VerticalAlign,
}}}
msgs = append(msgs, simple.EventMessage{Msg: m})
}
return
}

View file

@ -0,0 +1,75 @@
package table
import (
"fmt"
"github.com/anytypeio/go-anytype-middleware/core/block/simple"
"github.com/anytypeio/go-anytype-middleware/core/block/simple/base"
"github.com/anytypeio/go-anytype-middleware/pb"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/pb/model"
"github.com/anytypeio/go-anytype-middleware/util/pbtypes"
)
func init() {
simple.RegisterCreator(NewRowBlock)
}
func NewRowBlock(b *model.Block) simple.Block {
if c := b.GetTableRow(); c != nil {
return &rowBlock{
Base: base.NewBase(b).(*base.Base),
content: c,
}
}
return nil
}
type RowBlock interface {
simple.Block
ApplyEvent(e *pb.EventBlockSetTableRow) (err error)
SetIsHeader(v bool)
}
type rowBlock struct {
*base.Base
content *model.BlockContentTableRow
}
func (b *rowBlock) Copy() simple.Block {
return NewRowBlock(pbtypes.CopyBlock(b.Model()))
}
func (b *rowBlock) SetIsHeader(v bool) {
b.content.IsHeader = v
}
func (b *rowBlock) Diff(sb simple.Block) (msgs []simple.EventMessage, err error) {
other, ok := sb.(*rowBlock)
if !ok {
return nil, fmt.Errorf("can't make diff with different block type")
}
if msgs, err = b.Base.Diff(other); err != nil {
return
}
changes := &pb.EventBlockSetTableRow{
Id: other.Id,
}
hasChanges := false
if b.content.IsHeader != other.content.IsHeader {
hasChanges = true
changes.IsHeader = &pb.EventBlockSetTableRowIsHeader{Value: other.content.IsHeader}
}
if hasChanges {
msgs = append(msgs, simple.EventMessage{Msg: &pb.EventMessage{Value: &pb.EventMessageValueOfBlockSetTableRow{BlockSetTableRow: changes}}})
}
return
}
func (b *rowBlock) ApplyEvent(e *pb.EventBlockSetTableRow) (err error) {
if e.IsHeader != nil {
b.content.IsHeader = e.IsHeader.GetValue()
}
return
}

View file

@ -63,6 +63,8 @@ type Block interface {
FillSmartIds(ids []string) []string
HasSmartIds() bool
ApplyEvent(e *pb.EventBlockSetText) error
IsEmpty() bool
}
type Text struct {
@ -504,6 +506,24 @@ func (t *Text) splitMarks(marks []*model.BlockContentTextMark, r *model.Range, n
Type: m.Type,
Param: m.Param,
})
} else if (m.Range.From >= r.From) && (m.Range.To >= r.To) {
botMarks = append(botMarks, &model.BlockContentTextMark{
Range: &model.Range{
From: r.From + newTextLen,
To: m.Range.To - (r.To - r.From) + newTextLen,
},
Type: m.Type,
Param: m.Param,
})
} else if (m.Range.From < r.From) && (m.Range.To > r.From) && (m.Range.To <= r.To) {
topMarks = append(topMarks, &model.BlockContentTextMark{
Range: &model.Range{
From: m.Range.From,
To: r.From,
},
Type: m.Type,
Param: m.Param,
})
} else
// (*******<b>**)rem lorem</b> :---> __PASTE__ <b>em lorem</b>
if m.Range.From < r.To {
@ -661,3 +681,19 @@ func (t *Text) ApplyEvent(e *pb.EventBlockSetText) error {
}
return nil
}
func (t *Text) IsEmpty() bool {
if t.content.Text == "" &&
!t.content.Checked &&
t.content.Color == "" &&
t.content.Style == 0 &&
t.content.IconEmoji == "" &&
t.content.IconImage == "" &&
len(t.content.GetMarks().GetMarks()) == 0 &&
t.Model().BackgroundColor == "" &&
t.Model().Align == 0 &&
t.Model().VerticalAlign == 0 {
return true
}
return false
}

View file

@ -129,6 +129,76 @@ func TestText_Split(t *testing.T) {
})
}
func TestText_RangeSplit(t *testing.T) {
testBlock := func() *Text {
return NewText(&model.Block{
Restrictions: &model.BlockRestrictions{},
Content: &model.BlockContentOfText{Text: &model.BlockContentText{
Text: "1234567890",
Marks: &model.BlockContentTextMarks{
Marks: []*model.BlockContentTextMark{
{
Type: model.BlockContentTextMark_Bold,
Range: &model.Range{
From: 2,
To: 8,
},
},
},
},
}},
}).(*Text)
}
t.Run("should split block", func(t *testing.T) {
b := testBlock()
newBlock, err := b.RangeSplit(1, 5, false)
require.NoError(t, err)
nb := newBlock.(*Text)
assert.Equal(t, "67890", nb.content.Text)
assert.Equal(t, "1", b.content.Text)
require.Len(t, b.content.Marks.Marks, 0)
require.Len(t, nb.content.Marks.Marks, 1)
assert.Equal(t, model.Range{0, 3}, *nb.content.Marks.Marks[0].Range)
})
t.Run("split by range with marked and unmarked letters at the start of marked zone", func(t *testing.T) {
b := testBlock()
newBlock, err := b.RangeSplit(1, 3, false)
require.NoError(t, err)
nb := newBlock.(*Text)
assert.Equal(t, "4567890", nb.content.Text)
assert.Equal(t, "1", b.content.Text)
require.Len(t, b.content.Marks.Marks, 0)
require.Len(t, nb.content.Marks.Marks, 1)
assert.Equal(t, model.Range{0, 5}, *nb.content.Marks.Marks[0].Range)
})
t.Run("split by range with marked letters at the start of marked zone", func(t *testing.T) {
b := testBlock()
newBlock, err := b.RangeSplit(2, 3, false)
require.NoError(t, err)
nb := newBlock.(*Text)
assert.Equal(t, "4567890", nb.content.Text)
assert.Equal(t, "12", b.content.Text)
require.Len(t, b.content.Marks.Marks, 0)
require.Len(t, nb.content.Marks.Marks, 1)
assert.Equal(t, model.Range{0, 5}, *nb.content.Marks.Marks[0].Range)
})
t.Run("split by range with marked and unmarked letters at the end of marked zone", func(t *testing.T) {
b := testBlock()
newBlock, err := b.RangeSplit(7, 8, false)
require.NoError(t, err)
nb := newBlock.(*Text)
assert.Equal(t, "90", nb.content.Text)
assert.Equal(t, "1234567", b.content.Text)
require.Len(t, b.content.Marks.Marks, 1)
assert.Equal(t, model.Range{2, 7}, *b.content.Marks.Marks[0].Range)
})
t.Run("out of range", func(t *testing.T) {
b := testBlock()
_, err := b.RangeSplit(0, 11, false)
require.Equal(t, ErrOutOfRange, err)
})
}
func TestText_normalizeMarks(t *testing.T) {
b := NewText(&model.Block{
Restrictions: &model.BlockRestrictions{},
@ -242,7 +312,7 @@ func TestText_Merge(t *testing.T) {
b2 := NewText(&model.Block{
Content: &model.BlockContentOfText{
Text: &model.BlockContentText{
Text: "One",
Text: "One",
Style: model.BlockContentText_Header1,
},
},

View file

@ -10,6 +10,8 @@ import (
"strconv"
"github.com/anytypeio/go-anytype-middleware/core/block/editor/state"
"github.com/anytypeio/go-anytype-middleware/core/block/editor/table"
"github.com/anytypeio/go-anytype-middleware/core/block/simple"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/core"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/pb/model"
"github.com/anytypeio/go-anytype-middleware/util/pbtypes"
@ -74,7 +76,7 @@ func (h *HTML) Convert() (result string) {
}
h.buf = bytes.NewBuffer(nil)
h.buf.WriteString(wrapCopyStart)
h.renderChilds(h.s.Pick(h.s.RootId()).Model())
h.renderChildren(h.s.Pick(h.s.RootId()).Model())
h.buf.WriteString(wrapCopyEnd)
result = h.buf.String()
h.buf.Reset()
@ -84,7 +86,7 @@ func (h *HTML) Convert() (result string) {
func (h *HTML) Export() (result string) {
h.buf = bytes.NewBuffer(nil)
h.buf.WriteString(wrapExportStart)
h.renderChilds(h.s.Pick(h.s.RootId()).Model())
h.renderChildren(h.s.Pick(h.s.RootId()).Model())
h.buf.WriteString(wrapExportEnd)
return h.buf.String()
}
@ -110,13 +112,16 @@ func (h *HTML) render(rs *renderState, b *model.Block) {
case *model.BlockContentOfLink:
rs.Close()
h.renderLink(b)
case *model.BlockContentOfTable:
rs.Close()
h.renderTable(b)
default:
rs.Close()
h.renderLayout(b)
}
}
func (h *HTML) renderChilds(parent *model.Block) {
func (h *HTML) renderChildren(parent *model.Block) {
var rs = &renderState{h: h}
for _, chId := range parent.ChildrenIds {
b := h.s.Pick(chId)
@ -177,13 +182,13 @@ func (h *HTML) renderText(rs *renderState, b *model.Block) {
}
case model.BlockContentTextMark_TextColor:
if start {
fmt.Fprintf(h.buf, `<span style="color:%s">`, colorMapping(m.Param, true))
fmt.Fprintf(h.buf, `<span style="color:%s">`, textColor(m.Param))
} else {
h.buf.WriteString("</span>")
}
case model.BlockContentTextMark_BackgroundColor:
if start {
fmt.Fprintf(h.buf, `<span style="backgound-color:%s">`, colorMapping(m.Param, true))
fmt.Fprintf(h.buf, `<span style="backgound-color:%s">`, backgroundColor(m.Param))
} else {
h.buf.WriteString("</span>")
}
@ -219,67 +224,67 @@ func (h *HTML) renderText(rs *renderState, b *model.Block) {
rs.Close()
h.buf.WriteString(`<h1 style="` + styleHeader1 + `">`)
renderText()
h.renderChilds(b)
h.renderChildren(b)
h.buf.WriteString(`</h1>`)
case model.BlockContentText_Header2:
rs.Close()
h.buf.WriteString(`<h2 style="` + styleHeader2 + `">`)
renderText()
h.renderChilds(b)
h.renderChildren(b)
h.buf.WriteString(`</h2>`)
case model.BlockContentText_Header3:
rs.Close()
h.buf.WriteString(`<h3 style="` + styleHeader3 + `">`)
renderText()
h.renderChilds(b)
h.renderChildren(b)
h.buf.WriteString(`</h3>`)
case model.BlockContentText_Header4:
rs.Close()
h.buf.WriteString(`<h4 style="` + styleHeader4 + `">`)
renderText()
h.renderChilds(b)
h.renderChildren(b)
h.buf.WriteString(`</h4>`)
case model.BlockContentText_Quote:
rs.Close()
h.buf.WriteString(`<quote style="` + styleQuote + `">`)
renderText()
h.renderChilds(b)
h.renderChildren(b)
h.buf.WriteString(`</quote>`)
case model.BlockContentText_Code:
rs.Close()
h.buf.WriteString(`<code style="` + styleCode + `"><pre>`)
renderText()
h.renderChilds(b)
h.renderChildren(b)
h.buf.WriteString(`</pre></code>`)
case model.BlockContentText_Title:
rs.Close()
h.buf.WriteString(`<h1 style="` + styleTitle + `">`)
renderText()
h.renderChilds(b)
h.renderChildren(b)
h.buf.WriteString(`</h1>`)
case model.BlockContentText_Checkbox:
rs.Close()
h.buf.WriteString(`<div style="` + styleCheckbox + `" class="check"><input type="checkbox"/>`)
renderText()
h.renderChilds(b)
h.renderChildren(b)
h.buf.WriteString(`</div>`)
case model.BlockContentText_Marked:
rs.OpenUL()
h.buf.WriteString(`<li>`)
renderText()
h.renderChilds(b)
h.renderChildren(b)
h.buf.WriteString(`</li>`)
case model.BlockContentText_Numbered:
rs.OpenOL()
h.buf.WriteString(`<li>`)
renderText()
h.renderChilds(b)
h.renderChildren(b)
h.buf.WriteString(`</li>`)
case model.BlockContentText_Toggle:
rs.Close()
h.buf.WriteString(`<div style="` + styleToggle + `" class="toggle">`)
renderText()
h.renderChilds(b)
h.renderChildren(b)
h.buf.WriteString(`</div>`)
case model.BlockContentText_Callout:
rs.Close()
@ -291,13 +296,13 @@ func (h *HTML) renderText(rs *renderState, b *model.Block) {
fmt.Fprintf(h.buf, `<div style="%s">%s`, styleCallout, img)
renderText()
h.renderChilds(b)
h.renderChildren(b)
h.buf.WriteString(`</div>`)
default:
rs.Close()
h.buf.WriteString(`<div style="` + styleParagraph + `" class="paragraph" style="` + styleParagraph + `">`)
renderText()
h.renderChilds(b)
h.renderChildren(b)
h.buf.WriteString(`</div>`)
}
}
@ -318,33 +323,33 @@ func (h *HTML) renderFile(b *model.Block) {
h.buf.WriteString(html.EscapeString(file.Name))
h.buf.WriteString(`</div>`)
h.buf.WriteString(goToAnytypeMsg)
h.renderChilds(b)
h.renderChildren(b)
h.buf.WriteString("</div>")
case model.BlockContentFile_Image:
baseImg := h.getImageBase64(file.Hash)
fmt.Fprintf(h.buf, `<div><img alt="%s" src="%s" />`, html.EscapeString(file.Name), baseImg)
h.renderChilds(b)
h.renderChildren(b)
h.buf.WriteString("</div>")
case model.BlockContentFile_Video:
h.buf.WriteString(`<div class="video"><div class="name">`)
h.buf.WriteString(html.EscapeString(file.Name))
h.buf.WriteString(`</div>`)
h.buf.WriteString(goToAnytypeMsg)
h.renderChilds(b)
h.renderChildren(b)
h.buf.WriteString("</div>")
case model.BlockContentFile_Audio:
h.buf.WriteString(`<div class="audio"><div class="name">`)
h.buf.WriteString(html.EscapeString(file.Name))
h.buf.WriteString(`</div>`)
h.buf.WriteString(goToAnytypeMsg)
h.renderChilds(b)
h.renderChildren(b)
h.buf.WriteString("</div>")
case model.BlockContentFile_PDF:
h.buf.WriteString(`<div class="pdf"><div class="name">`)
h.buf.WriteString(html.EscapeString(file.Name))
h.buf.WriteString(`</div>`)
h.buf.WriteString(goToAnytypeMsg)
h.renderChilds(b)
h.renderChildren(b)
h.buf.WriteString("</div>")
}
}
@ -356,7 +361,7 @@ func (h *HTML) renderBookmark(b *model.Block) {
} else {
h.buf.WriteString("<div>")
}
h.renderChilds(b)
h.renderChildren(b)
h.buf.WriteString("</div>")
}
@ -367,7 +372,7 @@ func (h *HTML) renderDiv(b *model.Block) {
case model.BlockContentDiv_Line:
h.buf.WriteString(`<hr class="line">`)
}
h.renderChilds(b)
h.renderChildren(b)
}
func (h *HTML) renderLayout(b *model.Block) {
@ -388,17 +393,17 @@ func (h *HTML) renderLayout(b *model.Block) {
}
}
h.buf.WriteString(`<div class="column" ` + style + `>`)
h.renderChilds(b)
h.renderChildren(b)
h.buf.WriteString("</div>")
case model.BlockContentLayout_Row:
h.buf.WriteString(`<div class="row" style="display: flex">`)
h.renderChilds(b)
h.renderChildren(b)
h.buf.WriteString("</div>")
case model.BlockContentLayout_Div:
h.renderChilds(b)
h.renderChildren(b)
default:
h.buf.WriteString(`<div>`)
h.renderChilds(b)
h.renderChildren(b)
h.buf.WriteString("</div>")
}
}
@ -412,11 +417,84 @@ func (h *HTML) renderLink(b *model.Block) {
Follow <a href="https://anytype.io">link</a> to ask a permission to get the content
</div>`)
if len(b.ChildrenIds) > 0 {
h.renderChilds(b)
h.renderChildren(b)
h.buf.WriteString("</div>")
}
}
func (h *HTML) renderTable(b *model.Block) {
tb, err := table.NewTable(h.s, b.Id)
if err != nil {
return
}
h.buf.WriteString(`<table style="border-collapse: collapse; border: 1px solid #dfddd0;">`)
defer h.buf.WriteString("</table>")
cols := tb.Columns()
colWidth := map[string]float64{}
for _, colId := range cols.ChildrenIds {
col := h.s.Pick(colId)
if col == nil {
continue
}
colWidth[colId] = pbtypes.GetFloat64(col.Model().GetFields(), "width")
}
for _, rowId := range tb.Rows().ChildrenIds {
h.renderRow(rowId, cols, colWidth)
}
}
func (h *HTML) renderRow(rowId string, cols *model.Block, colWidth map[string]float64) {
row := h.s.Pick(rowId)
if row == nil {
return
}
h.buf.WriteString("<tr>")
defer h.buf.WriteString("</tr>")
colToCell := map[string]string{}
for _, cellId := range row.Model().ChildrenIds {
_, colId, err := table.ParseCellId(cellId)
if err != nil {
continue
}
colToCell[colId] = cellId
}
for _, colId := range cols.ChildrenIds {
h.renderCell(colWidth, colId, colToCell)
}
}
func (h *HTML) renderCell(colWidth map[string]float64, colId string, colToCell map[string]string) {
var extraAttr, extraStyle string
if w := colWidth[colId]; w > 0 {
extraAttr += fmt.Sprintf(` width="%d"`, int(w))
}
var cell simple.Block
cellId, ok := colToCell[colId]
if ok {
cell = h.s.Pick(cellId)
if cell != nil {
if bg := cell.Model().BackgroundColor; bg != "" {
extraStyle += fmt.Sprintf(`; background-color: %s`, backgroundColor(bg))
}
}
}
fmt.Fprintf(h.buf, `<td style="border: 1px solid #dfddd0; padding: 9px; font-size: 14px; line-height: 22px%s"%s>`, extraStyle, extraAttr)
defer h.buf.WriteString("</td>")
if cell != nil {
rs := &renderState{h: h}
h.render(rs, cell.Model())
} else {
h.buf.WriteString("&nbsp;")
}
}
func (h *HTML) getImageBase64(hash string) (res string) {
im, err := h.a.ImageByHash(context.TODO(), hash)
if err != nil {
@ -473,60 +551,58 @@ func (rs *renderState) Close() {
}
}
func colorMapping(color string, isText bool) (out string) {
if isText {
switch color {
case "grey":
out = "#aca996"
case "yellow":
out = "#ecd91b"
case "orange":
out = "#ffb522"
case "red":
out = "#f55522"
case "pink":
out = "#e51ca0"
case "purple":
out = "#ab50cc"
case "blue":
out = "#3e58"
case "ice":
out = "#2aa7ee"
case "teal":
out = "#0fc8ba"
case "lime":
out = "#5dd400"
case "black":
out = "#2c2b27"
default:
out = color
}
} else {
switch color {
case "grey":
out = "#f3f2ec"
case "yellow":
out = "#fef9cc"
case "orange":
out = "#fef3c5"
case "red":
out = "#ffebe5"
case "pink":
out = "#fee3f5"
case "purple":
out = "#f4e3fa"
case "blue":
out = "#f4e3fa"
case "ice":
out = "#d6effd"
case "teal":
out = "#d6f5f3"
case "lime":
out = "#e3f7d0"
default:
out = color
}
func textColor(color string) string {
switch color {
case "grey":
return "#aca996"
case "yellow":
return "#ecd91b"
case "orange":
return "#ffb522"
case "red":
return "#f55522"
case "pink":
return "#e51ca0"
case "purple":
return "#ab50cc"
case "blue":
return "#3e58"
case "ice":
return "#2aa7ee"
case "teal":
return "#0fc8ba"
case "lime":
return "#5dd400"
case "black":
return "#2c2b27"
default:
return color
}
}
func backgroundColor(color string) string {
switch color {
case "grey":
return "#f3f2ec"
case "yellow":
return "#fef9cc"
case "orange":
return "#fef3c5"
case "red":
return "#ffebe5"
case "pink":
return "#fee3f5"
case "purple":
return "#f4e3fa"
case "blue":
return "#f4e3fa"
case "ice":
return "#d6effd"
case "teal":
return "#d6f5f3"
case "lime":
return "#e3f7d0"
default:
return color
}
return out
}

View file

@ -130,7 +130,7 @@ func (s *service) fetchKey(key string) (relation *Relation, err error) {
},
},
}
f, err := database.NewFilters(q, nil)
f, err := database.NewFilters(q, nil, nil)
if err != nil {
return
}
@ -158,7 +158,7 @@ func (s *service) fetchOptionsByKey(key string) (relation *Relation, err error)
},
},
}
f, err := database.NewFilters(q, nil)
f, err := database.NewFilters(q, nil, nil)
if err != nil {
return
}

View file

@ -99,7 +99,7 @@ func (s *service) Search(req pb.RpcObjectSearchSubscribeRequest) (resp *pb.RpcOb
Limit: int(req.Limit),
}
f, err := database.NewFilters(q, nil)
f, err := database.NewFilters(q, nil, time.Now().Location())
if err != nil {
return
}

315
core/table.go Normal file
View file

@ -0,0 +1,315 @@
package core
import (
"github.com/anytypeio/go-anytype-middleware/core/block"
"github.com/anytypeio/go-anytype-middleware/core/block/editor/state"
"github.com/anytypeio/go-anytype-middleware/pb"
)
func (mw *Middleware) BlockTableCreate(req *pb.RpcBlockTableCreateRequest) *pb.RpcBlockTableCreateResponse {
ctx := state.NewContext(nil)
response := func(code pb.RpcBlockTableCreateResponseErrorCode, id string, err error) *pb.RpcBlockTableCreateResponse {
m := &pb.RpcBlockTableCreateResponse{Error: &pb.RpcBlockTableCreateResponseError{Code: code}, BlockId: id}
if err != nil {
m.Error.Description = err.Error()
} else {
m.Event = ctx.GetResponseEvent()
}
return m
}
var id string
err := mw.doBlockService(func(bs block.Service) (err error) {
id, err = bs.CreateTableBlock(ctx, *req)
return
})
if err != nil {
return response(pb.RpcBlockTableCreateResponseError_UNKNOWN_ERROR, "", err)
}
return response(pb.RpcBlockTableCreateResponseError_NULL, id, nil)
}
func (mw *Middleware) BlockTableRowCreate(req *pb.RpcBlockTableRowCreateRequest) *pb.RpcBlockTableRowCreateResponse {
ctx := state.NewContext(nil)
response := func(code pb.RpcBlockTableRowCreateResponseErrorCode, id string, err error) *pb.RpcBlockTableRowCreateResponse {
m := &pb.RpcBlockTableRowCreateResponse{Error: &pb.RpcBlockTableRowCreateResponseError{Code: code}}
if err != nil {
m.Error.Description = err.Error()
} else {
m.Event = ctx.GetResponseEvent()
}
return m
}
var id string
err := mw.doBlockService(func(bs block.Service) (err error) {
err = bs.TableRowCreate(ctx, *req)
return
})
if err != nil {
return response(pb.RpcBlockTableRowCreateResponseError_UNKNOWN_ERROR, "", err)
}
return response(pb.RpcBlockTableRowCreateResponseError_NULL, id, nil)
}
func (mw *Middleware) BlockTableColumnCreate(req *pb.RpcBlockTableColumnCreateRequest) *pb.RpcBlockTableColumnCreateResponse {
ctx := state.NewContext(nil)
response := func(code pb.RpcBlockTableColumnCreateResponseErrorCode, id string, err error) *pb.RpcBlockTableColumnCreateResponse {
m := &pb.RpcBlockTableColumnCreateResponse{Error: &pb.RpcBlockTableColumnCreateResponseError{Code: code}}
if err != nil {
m.Error.Description = err.Error()
} else {
m.Event = ctx.GetResponseEvent()
}
return m
}
var id string
err := mw.doBlockService(func(bs block.Service) (err error) {
err = bs.TableColumnCreate(ctx, *req)
return
})
if err != nil {
return response(pb.RpcBlockTableColumnCreateResponseError_UNKNOWN_ERROR, "", err)
}
return response(pb.RpcBlockTableColumnCreateResponseError_NULL, id, nil)
}
func (mw *Middleware) BlockTableRowDelete(req *pb.RpcBlockTableRowDeleteRequest) *pb.RpcBlockTableRowDeleteResponse {
ctx := state.NewContext(nil)
response := func(code pb.RpcBlockTableRowDeleteResponseErrorCode, id string, err error) *pb.RpcBlockTableRowDeleteResponse {
m := &pb.RpcBlockTableRowDeleteResponse{Error: &pb.RpcBlockTableRowDeleteResponseError{Code: code}}
if err != nil {
m.Error.Description = err.Error()
} else {
m.Event = ctx.GetResponseEvent()
}
return m
}
var id string
err := mw.doBlockService(func(bs block.Service) (err error) {
err = bs.TableRowDelete(ctx, *req)
return
})
if err != nil {
return response(pb.RpcBlockTableRowDeleteResponseError_UNKNOWN_ERROR, "", err)
}
return response(pb.RpcBlockTableRowDeleteResponseError_NULL, id, nil)
}
func (mw *Middleware) BlockTableColumnDelete(req *pb.RpcBlockTableColumnDeleteRequest) *pb.RpcBlockTableColumnDeleteResponse {
ctx := state.NewContext(nil)
response := func(code pb.RpcBlockTableColumnDeleteResponseErrorCode, id string, err error) *pb.RpcBlockTableColumnDeleteResponse {
m := &pb.RpcBlockTableColumnDeleteResponse{Error: &pb.RpcBlockTableColumnDeleteResponseError{Code: code}}
if err != nil {
m.Error.Description = err.Error()
} else {
m.Event = ctx.GetResponseEvent()
}
return m
}
var id string
err := mw.doBlockService(func(bs block.Service) (err error) {
err = bs.TableColumnDelete(ctx, *req)
return
})
if err != nil {
return response(pb.RpcBlockTableColumnDeleteResponseError_UNKNOWN_ERROR, "", err)
}
return response(pb.RpcBlockTableColumnDeleteResponseError_NULL, id, nil)
}
func (mw *Middleware) BlockTableColumnMove(req *pb.RpcBlockTableColumnMoveRequest) *pb.RpcBlockTableColumnMoveResponse {
ctx := state.NewContext(nil)
response := func(code pb.RpcBlockTableColumnMoveResponseErrorCode, id string, err error) *pb.RpcBlockTableColumnMoveResponse {
m := &pb.RpcBlockTableColumnMoveResponse{Error: &pb.RpcBlockTableColumnMoveResponseError{Code: code}}
if err != nil {
m.Error.Description = err.Error()
} else {
m.Event = ctx.GetResponseEvent()
}
return m
}
var id string
err := mw.doBlockService(func(bs block.Service) (err error) {
err = bs.TableColumnMove(ctx, *req)
return
})
if err != nil {
return response(pb.RpcBlockTableColumnMoveResponseError_UNKNOWN_ERROR, "", err)
}
return response(pb.RpcBlockTableColumnMoveResponseError_NULL, id, nil)
}
func (mw *Middleware) BlockTableRowDuplicate(req *pb.RpcBlockTableRowDuplicateRequest) *pb.RpcBlockTableRowDuplicateResponse {
ctx := state.NewContext(nil)
response := func(code pb.RpcBlockTableRowDuplicateResponseErrorCode, id string, err error) *pb.RpcBlockTableRowDuplicateResponse {
m := &pb.RpcBlockTableRowDuplicateResponse{Error: &pb.RpcBlockTableRowDuplicateResponseError{Code: code}}
if err != nil {
m.Error.Description = err.Error()
} else {
m.Event = ctx.GetResponseEvent()
}
return m
}
var id string
err := mw.doBlockService(func(bs block.Service) (err error) {
err = bs.TableRowDuplicate(ctx, *req)
return
})
if err != nil {
return response(pb.RpcBlockTableRowDuplicateResponseError_UNKNOWN_ERROR, "", err)
}
return response(pb.RpcBlockTableRowDuplicateResponseError_NULL, id, nil)
}
func (mw *Middleware) BlockTableColumnDuplicate(req *pb.RpcBlockTableColumnDuplicateRequest) *pb.RpcBlockTableColumnDuplicateResponse {
ctx := state.NewContext(nil)
response := func(code pb.RpcBlockTableColumnDuplicateResponseErrorCode, id string, err error) *pb.RpcBlockTableColumnDuplicateResponse {
m := &pb.RpcBlockTableColumnDuplicateResponse{BlockId: id, Error: &pb.RpcBlockTableColumnDuplicateResponseError{Code: code}}
if err != nil {
m.Error.Description = err.Error()
} else {
m.Event = ctx.GetResponseEvent()
}
return m
}
var id string
err := mw.doBlockService(func(bs block.Service) (err error) {
id, err = bs.TableColumnDuplicate(ctx, *req)
return
})
if err != nil {
return response(pb.RpcBlockTableColumnDuplicateResponseError_UNKNOWN_ERROR, "", err)
}
return response(pb.RpcBlockTableColumnDuplicateResponseError_NULL, id, nil)
}
func (mw *Middleware) BlockTableExpand(req *pb.RpcBlockTableExpandRequest) *pb.RpcBlockTableExpandResponse {
ctx := state.NewContext(nil)
response := func(code pb.RpcBlockTableExpandResponseErrorCode, id string, err error) *pb.RpcBlockTableExpandResponse {
m := &pb.RpcBlockTableExpandResponse{Error: &pb.RpcBlockTableExpandResponseError{Code: code}}
if err != nil {
m.Error.Description = err.Error()
} else {
m.Event = ctx.GetResponseEvent()
}
return m
}
var id string
err := mw.doBlockService(func(bs block.Service) (err error) {
err = bs.TableExpand(ctx, *req)
return
})
if err != nil {
return response(pb.RpcBlockTableExpandResponseError_UNKNOWN_ERROR, "", err)
}
return response(pb.RpcBlockTableExpandResponseError_NULL, id, nil)
}
func (mw *Middleware) BlockTableRowListFill(req *pb.RpcBlockTableRowListFillRequest) *pb.RpcBlockTableRowListFillResponse {
ctx := state.NewContext(nil)
response := func(code pb.RpcBlockTableRowListFillResponseErrorCode, id string, err error) *pb.RpcBlockTableRowListFillResponse {
m := &pb.RpcBlockTableRowListFillResponse{Error: &pb.RpcBlockTableRowListFillResponseError{Code: code}}
if err != nil {
m.Error.Description = err.Error()
} else {
m.Event = ctx.GetResponseEvent()
}
return m
}
var id string
err := mw.doBlockService(func(bs block.Service) (err error) {
err = bs.TableRowListFill(ctx, *req)
return
})
if err != nil {
return response(pb.RpcBlockTableRowListFillResponseError_UNKNOWN_ERROR, "", err)
}
return response(pb.RpcBlockTableRowListFillResponseError_NULL, id, nil)
}
func (mw *Middleware) BlockTableRowListClean(req *pb.RpcBlockTableRowListCleanRequest) *pb.RpcBlockTableRowListCleanResponse {
ctx := state.NewContext(nil)
response := func(code pb.RpcBlockTableRowListCleanResponseErrorCode, id string, err error) *pb.RpcBlockTableRowListCleanResponse {
m := &pb.RpcBlockTableRowListCleanResponse{Error: &pb.RpcBlockTableRowListCleanResponseError{Code: code}}
if err != nil {
m.Error.Description = err.Error()
} else {
m.Event = ctx.GetResponseEvent()
}
return m
}
var id string
err := mw.doBlockService(func(bs block.Service) (err error) {
err = bs.TableRowListClean(ctx, *req)
return
})
if err != nil {
return response(pb.RpcBlockTableRowListCleanResponseError_UNKNOWN_ERROR, "", err)
}
return response(pb.RpcBlockTableRowListCleanResponseError_NULL, id, nil)
}
func (mw *Middleware) BlockTableSort(req *pb.RpcBlockTableSortRequest) *pb.RpcBlockTableSortResponse {
ctx := state.NewContext(nil)
response := func(code pb.RpcBlockTableSortResponseErrorCode, id string, err error) *pb.RpcBlockTableSortResponse {
m := &pb.RpcBlockTableSortResponse{Error: &pb.RpcBlockTableSortResponseError{Code: code}}
if err != nil {
m.Error.Description = err.Error()
} else {
m.Event = ctx.GetResponseEvent()
}
return m
}
var id string
err := mw.doBlockService(func(bs block.Service) (err error) {
err = bs.TableSort(ctx, *req)
return
})
if err != nil {
return response(pb.RpcBlockTableSortResponseError_UNKNOWN_ERROR, "", err)
}
return response(pb.RpcBlockTableSortResponseError_NULL, id, nil)
}
func (mw *Middleware) BlockTableColumnListFill(req *pb.RpcBlockTableColumnListFillRequest) *pb.RpcBlockTableColumnListFillResponse {
ctx := state.NewContext(nil)
response := func(code pb.RpcBlockTableColumnListFillResponseErrorCode, id string, err error) *pb.RpcBlockTableColumnListFillResponse {
m := &pb.RpcBlockTableColumnListFillResponse{Error: &pb.RpcBlockTableColumnListFillResponseError{Code: code}}
if err != nil {
m.Error.Description = err.Error()
} else {
m.Event = ctx.GetResponseEvent()
}
return m
}
var id string
err := mw.doBlockService(func(bs block.Service) (err error) {
err = bs.TableColumnListFill(ctx, *req)
return
})
if err != nil {
return response(pb.RpcBlockTableColumnListFillResponseError_UNKNOWN_ERROR, "", err)
}
return response(pb.RpcBlockTableColumnListFillResponseError_NULL, id, nil)
}
func (mw *Middleware) BlockTableRowSetHeader(req *pb.RpcBlockTableRowSetHeaderRequest) *pb.RpcBlockTableRowSetHeaderResponse {
ctx := state.NewContext(nil)
response := func(code pb.RpcBlockTableRowSetHeaderResponseErrorCode, id string, err error) *pb.RpcBlockTableRowSetHeaderResponse {
m := &pb.RpcBlockTableRowSetHeaderResponse{Error: &pb.RpcBlockTableRowSetHeaderResponseError{Code: code}}
if err != nil {
m.Error.Description = err.Error()
} else {
m.Event = ctx.GetResponseEvent()
}
return m
}
var id string
err := mw.doBlockService(func(bs block.Service) (err error) {
err = bs.TableRowSetHeader(ctx, *req)
return
})
if err != nil {
return response(pb.RpcBlockTableRowSetHeaderResponseError_UNKNOWN_ERROR, "", err)
}
return response(pb.RpcBlockTableRowSetHeaderResponseError_NULL, id, nil)
}

File diff suppressed because it is too large Load diff

2
go.mod
View file

@ -75,7 +75,7 @@ require (
github.com/multiformats/go-multibase v0.0.3
github.com/multiformats/go-multihash v0.0.15
github.com/opentracing/opentracing-go v1.2.0
github.com/otiai10/copy v1.7.0 // indirect
github.com/otiai10/copy v1.7.0
github.com/otiai10/opengraph/v2 v2.1.0
github.com/prometheus/client_golang v1.10.0
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd

1
go.sum
View file

@ -1259,7 +1259,6 @@ github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6
github.com/otiai10/marmoset v0.4.0 h1:Hg59lQI7qQowBEdsAJ/+VDTEospTBzXX/A1Gsw4mlvA=
github.com/otiai10/marmoset v0.4.0/go.mod h1:t2q6dXWZ9YcFdRREDApX4bCmfQnL3isJ2dgl8ychlXg=
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
github.com/otiai10/mint v1.3.2 h1:VYWnrP5fXmz1MXvjuUvcBrXSjGE6xjON+axB/UrpO3E=
github.com/otiai10/mint v1.3.2/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
github.com/otiai10/mint v1.3.3 h1:7JgpsBaN0uMkyju4tbYHu0mnM55hNKVYLsXmwr15NQI=
github.com/otiai10/mint v1.3.3/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -399,6 +399,62 @@ message Rpc {
}
}
message ConfigUpdate {
message Request {
string timeZone = 1;
}
message Response {
Error error = 1;
message Error {
Code code = 1;
string description = 2;
enum Code {
NULL = 0;
UNKNOWN_ERROR = 1;
BAD_INPUT = 2;
ACCOUNT_IS_NOT_RUNNING = 101;
FAILED_TO_WRITE_CONFIG = 102;
FAILED_TO_GET_CONFIG = 103;
}
}
}
enum Timezones {
GMT = 0;
ECT = 1;
EET = 2;
EAT = 3;
MET = 4;
NET = 5;
PLT = 6;
IST = 7;
BST = 8;
VST = 9;
CTT = 10;
JST = 11;
ACT = 12;
AET = 13;
SST = 14;
NST = 15;
MIT = 16;
HST = 17;
AST = 18;
PST = 19;
MST = 20;
CST = 21;
IET = 22;
PRT = 23;
CNT = 24;
BET = 25;
BRT = 26;
CAT = 27;
}
}
message GetConfig {
message Get {
message Request {
@ -2583,6 +2639,31 @@ message Rpc {
}
}
message ListSetVerticalAlign {
message Request {
string contextId = 1; // id of the context object
repeated string blockIds = 2;
anytype.model.Block.VerticalAlign verticalAlign = 3;
}
message Response {
Error error = 1;
ResponseEvent event = 2;
message Error {
Code code = 1;
string description = 2;
enum Code {
NULL = 0;
UNKNOWN_ERROR = 1;
BAD_INPUT = 2;
// ...
}
}
}
}
message ListSetFields {
message Request {
string contextId = 1;
@ -3078,6 +3159,405 @@ message Rpc {
}
}
}
message ListClearStyle {
message Request {
string contextId = 1;
repeated string blockIds = 2;
}
message Response {
Error error = 1;
ResponseEvent event = 2;
message Error {
Code code = 1;
string description = 2;
enum Code {
NULL = 0;
UNKNOWN_ERROR = 1;
BAD_INPUT = 2;
// ...
}
}
}
}
message ListClearContent {
message Request {
string contextId = 1;
repeated string blockIds = 2;
}
message Response {
Error error = 1;
ResponseEvent event = 2;
message Error {
Code code = 1;
string description = 2;
enum Code {
NULL = 0;
UNKNOWN_ERROR = 1;
BAD_INPUT = 2;
// ...
}
}
}
}
}
message BlockTable {
message Create {
message Request {
string contextId = 1; // id of the context object
string targetId = 2; // id of the closest block
anytype.model.Block.Position position = 3;
uint32 rows = 4;
uint32 columns = 5;
bool withHeaderRow = 6;
}
message Response {
Error error = 1;
string blockId = 2;
ResponseEvent event = 3;
message Error {
Code code = 1;
string description = 2;
enum Code {
NULL = 0;
UNKNOWN_ERROR = 1;
BAD_INPUT = 2;
// ...
}
}
}
}
message RowCreate {
message Request {
string contextId = 1; // id of the context object
string targetId = 2; // id of the closest row
anytype.model.Block.Position position = 3;
}
message Response {
Error error = 1;
ResponseEvent event = 2;
message Error {
Code code = 1;
string description = 2;
enum Code {
NULL = 0;
UNKNOWN_ERROR = 1;
BAD_INPUT = 2;
// ...
}
}
}
}
message RowSetHeader {
message Request {
string contextId = 1; // id of the context object
string targetId = 2;
bool isHeader = 3;
}
message Response {
Error error = 1;
ResponseEvent event = 2;
message Error {
Code code = 1;
string description = 2;
enum Code {
NULL = 0;
UNKNOWN_ERROR = 1;
BAD_INPUT = 2;
// ...
}
}
}
}
message RowListFill {
message Request {
string contextId = 1; // id of the context object
repeated string blockIds = 2;
}
message Response {
Error error = 1;
ResponseEvent event = 2;
message Error {
Code code = 1;
string description = 2;
enum Code {
NULL = 0;
UNKNOWN_ERROR = 1;
BAD_INPUT = 2;
// ...
}
}
}
}
message RowListClean {
message Request {
string contextId = 1; // id of the context object
repeated string blockIds = 2;
}
message Response {
Error error = 1;
ResponseEvent event = 2;
message Error {
Code code = 1;
string description = 2;
enum Code {
NULL = 0;
UNKNOWN_ERROR = 1;
BAD_INPUT = 2;
// ...
}
}
}
}
message ColumnListFill {
message Request {
string contextId = 1; // id of the context object
repeated string blockIds = 2;
}
message Response {
Error error = 1;
ResponseEvent event = 2;
message Error {
Code code = 1;
string description = 2;
enum Code {
NULL = 0;
UNKNOWN_ERROR = 1;
BAD_INPUT = 2;
// ...
}
}
}
}
message ColumnCreate {
message Request {
string contextId = 1; // id of the context object
string targetId = 2; // id of the closest column
anytype.model.Block.Position position = 3;
}
message Response {
Error error = 1;
ResponseEvent event = 2;
message Error {
Code code = 1;
string description = 2;
enum Code {
NULL = 0;
UNKNOWN_ERROR = 1;
BAD_INPUT = 2;
// ...
}
}
}
}
message RowDelete {
message Request {
string contextId = 1; // id of the context object
string targetId = 2; // id of the closest row
}
message Response {
Error error = 1;
ResponseEvent event = 2;
message Error {
Code code = 1;
string description = 2;
enum Code {
NULL = 0;
UNKNOWN_ERROR = 1;
BAD_INPUT = 2;
// ...
}
}
}
}
message ColumnDelete {
message Request {
string contextId = 1; // id of the context object
string targetId = 2; // id of the closest column
}
message Response {
Error error = 1;
ResponseEvent event = 2;
message Error {
Code code = 1;
string description = 2;
enum Code {
NULL = 0;
UNKNOWN_ERROR = 1;
BAD_INPUT = 2;
// ...
}
}
}
}
message ColumnMove {
message Request {
string contextId = 1;
string targetId = 2;
string dropTargetId = 3;
anytype.model.Block.Position position = 4;
}
message Response {
Error error = 1;
ResponseEvent event = 2;
message Error {
Code code = 1;
string description = 2;
enum Code {
NULL = 0;
UNKNOWN_ERROR = 1;
BAD_INPUT = 2;
// ...
}
}
}
}
message RowDuplicate {
message Request {
string contextId = 1; // id of the context object
string targetId = 2;
string blockId = 3; // block to duplicate
anytype.model.Block.Position position = 4;
}
message Response {
Error error = 1;
ResponseEvent event = 2;
message Error {
Code code = 1;
string description = 2;
enum Code {
NULL = 0;
UNKNOWN_ERROR = 1;
BAD_INPUT = 2;
// ...
}
}
}
}
message ColumnDuplicate {
message Request {
string contextId = 1; // id of the context object
string targetId = 2;
string blockId = 3; // block to duplicate
anytype.model.Block.Position position = 4;
}
message Response {
Error error = 1;
string blockId = 2;
ResponseEvent event = 3;
message Error {
Code code = 1;
string description = 2;
enum Code {
NULL = 0;
UNKNOWN_ERROR = 1;
BAD_INPUT = 2;
// ...
}
}
}
}
message Expand {
message Request {
string contextId = 1; // id of the context object
string targetId = 2;
uint32 columns = 3; // number of columns to append
uint32 rows = 4; // number of rows to append
}
message Response {
Error error = 1;
ResponseEvent event = 2;
message Error {
Code code = 1;
string description = 2;
enum Code {
NULL = 0;
UNKNOWN_ERROR = 1;
BAD_INPUT = 2;
// ...
}
}
}
}
message Sort {
message Request {
string contextId = 1; // id of the context object
string columnId = 2;
anytype.model.Block.Content.Dataview.Sort.Type type = 3;
}
message Response {
Error error = 1;
ResponseEvent event = 2;
message Error {
Code code = 1;
string description = 2;
enum Code {
NULL = 0;
UNKNOWN_ERROR = 1;
BAD_INPUT = 2;
// ...
}
}
}
}
}
message BlockFile {

View file

@ -54,6 +54,8 @@ message Event {
Block.Set.Div blockSetDiv = 17;
Block.Set.Relation blockSetRelation = 21;
Block.Set.Latex blockSetLatex = 25;
Block.Set.VerticalAlign blockSetVerticalAlign = 36;
Block.Set.TableRow blockSetTableRow = 37;
Block.Dataview.SourceSet blockDataviewSourceSet = 35;
Block.Dataview.ViewSet blockDataviewViewSet = 19;
@ -297,6 +299,11 @@ message Event {
anytype.model.Block.Align align = 2;
}
message VerticalAlign {
string id = 1;
anytype.model.Block.VerticalAlign verticalAlign = 2;
}
message Text {
string id = 1;
@ -481,6 +488,15 @@ message Event {
anytype.model.Block.Content.Bookmark.State value = 1;
}
}
message TableRow {
string id = 1;
IsHeader isHeader = 2;
message IsHeader {
bool value = 1;
}
}
}
message Fill {

View file

@ -40,6 +40,7 @@ var RequiredInternalRelations = []RelationKey{
RelationKeyIsFavorite,
RelationKeyWorkspaceId,
RelationKeyLinks,
RelationKeyInternalFlags,
}
var FormatFilePossibleTargetObjectTypes = []string{

View file

@ -41,15 +41,6 @@ const (
tmpDir = "tmp"
)
type PredefinedBlockIds struct {
Account string
Profile string
Home string
Archive string
SetPages string
}
type Service interface {
Account() string // deprecated, use wallet component
Device() string // deprecated, use wallet component

View file

@ -85,7 +85,7 @@ type Query struct {
func (q Query) DSQuery(sch schema.Schema) (qq query.Query, err error) {
qq.Limit = q.Limit
qq.Offset = q.Offset
f, err := NewFilters(q, sch)
f, err := NewFilters(q, sch, nil)
if err != nil {
return
}
@ -131,10 +131,10 @@ func injectDefaultFilters(filters []*model.BlockContentDataviewFilter) []*model.
return filters
}
func NewFilters(q Query, sch schema.Schema) (f *Filters, err error) {
func NewFilters(q Query, sch schema.Schema, loc *time.Location) (f *Filters, err error) {
q.Filters = injectDefaultFilters(q.Filters)
q.Filters = filter.TransformQuickOption(q.Filters)
q.Filters = filter.TransformQuickOption(q.Filters, loc)
f = new(Filters)
mainFilter := filter.AndFilters{}

View file

@ -7,15 +7,14 @@ import (
"time"
)
func TransformQuickOption(reqFilters []*model.BlockContentDataviewFilter) []*model.BlockContentDataviewFilter {
func TransformQuickOption(reqFilters []*model.BlockContentDataviewFilter, loc *time.Location) []*model.BlockContentDataviewFilter {
if reqFilters == nil {
return nil
}
for _, f := range reqFilters {
if f.QuickOption > model.BlockContentDataviewFilter_ExactDate {
d1, d2 := getRange(f)
d1, d2 := getRange(f, loc)
switch f.Condition {
case model.BlockContentDataviewFilter_Equal:
f.Condition = model.BlockContentDataviewFilter_GreaterOrEqual
@ -51,44 +50,45 @@ func TransformQuickOption(reqFilters []*model.BlockContentDataviewFilter) []*mod
return reqFilters
}
func getRange(f *model.BlockContentDataviewFilter) (int64, int64) {
func getRange(f *model.BlockContentDataviewFilter, loc *time.Location) (int64, int64) {
var d1, d2 time.Time
calendar := timeutil.NewCalendar(time.Now(), loc)
switch f.QuickOption {
case model.BlockContentDataviewFilter_Yesterday:
d1 = timeutil.DayNumStart(-1)
d2 = timeutil.DayNumEnd(-1)
d1 = calendar.DayNumStart(-1)
d2 = calendar.DayNumEnd(-1)
case model.BlockContentDataviewFilter_Today:
d1 = timeutil.DayNumStart(0)
d2 = timeutil.DayNumEnd(0)
d1 = calendar.DayNumStart(0)
d2 = calendar.DayNumEnd(0)
case model.BlockContentDataviewFilter_Tomorrow:
d1 = timeutil.DayNumStart(1)
d2 = timeutil.DayNumEnd(1)
d1 = calendar.DayNumStart(1)
d2 = calendar.DayNumEnd(1)
case model.BlockContentDataviewFilter_LastWeek:
d1 = timeutil.WeekNumStart(-1)
d2 = timeutil.WeekNumEnd(-1)
d1 = calendar.WeekNumStart(-1)
d2 = calendar.WeekNumEnd(-1)
case model.BlockContentDataviewFilter_CurrentWeek:
d1 = timeutil.WeekNumStart(0)
d2 = timeutil.WeekNumEnd(0)
d1 = calendar.WeekNumStart(0)
d2 = calendar.WeekNumEnd(0)
case model.BlockContentDataviewFilter_NextWeek:
d1 = timeutil.WeekNumStart(1)
d2 = timeutil.WeekNumEnd(1)
d1 = calendar.WeekNumStart(1)
d2 = calendar.WeekNumEnd(1)
case model.BlockContentDataviewFilter_LastMonth:
d1 = timeutil.MonthNumStart(-1)
d2 = timeutil.MonthNumEnd(-1)
d1 = calendar.MonthNumStart(-1)
d2 = calendar.MonthNumEnd(-1)
case model.BlockContentDataviewFilter_CurrentMonth:
d1 = timeutil.MonthNumStart(0)
d2 = timeutil.MonthNumEnd(0)
d1 = calendar.MonthNumStart(0)
d2 = calendar.MonthNumEnd(0)
case model.BlockContentDataviewFilter_NextMonth:
d1 = timeutil.MonthNumStart(1)
d2 = timeutil.MonthNumEnd(1)
d1 = calendar.MonthNumStart(1)
d2 = calendar.MonthNumEnd(1)
case model.BlockContentDataviewFilter_NumberOfDaysAgo:
daysCnt := f.Value.GetNumberValue()
d1 = timeutil.DayNumStart(-int(daysCnt))
d2 = timeutil.DayNumEnd(-1)
d1 = calendar.DayNumStart(-int(daysCnt))
d2 = calendar.DayNumEnd(-1)
case model.BlockContentDataviewFilter_NumberOfDaysNow:
daysCnt := f.Value.GetNumberValue()
d1 = timeutil.DayNumStart(0)
d2 = timeutil.DayNumEnd(int(daysCnt))
d1 = calendar.DayNumStart(0)
d2 = calendar.DayNumEnd(int(daysCnt))
//case model.BlockContentDataviewFilter_ExactDate:
// timestamp := f.GetValue().GetNumberValue()
// t := time.Unix(int64(timestamp), 0)

View file

@ -133,6 +133,10 @@ var (
}
for _, opt := range v.SelectDict {
if opt.Scope != model.RelationOption_local {
continue
}
indexes = append(indexes, localstore.IndexKeyParts([]string{v.Key, opt.Id}))
}
return indexes

File diff suppressed because it is too large Load diff

View file

@ -53,6 +53,7 @@ message Block {
repeated string childrenIds = 4;
string backgroundColor = 5;
Align align = 6;
VerticalAlign verticalAlign = 7;
oneof content {
Content.Smartblock smartblock = 11;
@ -69,6 +70,9 @@ message Block {
Content.FeaturedRelations featuredRelations = 23;
Content.Latex latex = 24;
Content.TableOfContents tableOfContents = 25;
Content.Table table = 26;
Content.TableColumn tableColumn = 27;
Content.TableRow tableRow = 28;
}
message Restrictions {
@ -103,6 +107,12 @@ message Block {
AlignRight = 2;
}
enum VerticalAlign {
VerticalAlignTop = 0;
VerticalAlignMiddle = 1;
VerticalAlignBottom = 2;
}
message Content {
/*
* Layout have no visual representation, but affects on blocks, that it contains.
@ -116,6 +126,8 @@ message Block {
Column = 1;
Div = 2;
Header = 3;
TableRows = 4;
TableColumns = 5;
}
}
@ -414,6 +426,12 @@ message Block {
message TableOfContents {
}
message Table {}
message TableColumn {}
message TableRow {
bool isHeader = 1;
}
}
}
@ -484,6 +502,7 @@ message Account {
string deviceId = 8;
string gatewayUrl = 101; // gateway url for fetching static files
string localStoragePath = 103; // path to local storage
string timeZone = 104; // time zone from config
}
}

View file

@ -29,7 +29,7 @@ const (
threadDerivedIndexAccountOld threadDerivedIndex = 3
threadDerivedIndexAccount threadDerivedIndex = 4
threadDerivedIndexSetPages threadDerivedIndex = 20
threadDerivedIndexSetPages threadDerivedIndex = 20 // deprecated
threadDerivedIndexMarketplaceType threadDerivedIndex = 30
threadDerivedIndexMarketplaceRelation threadDerivedIndex = 31
@ -52,7 +52,6 @@ type DerivedSmartblockIds struct {
Profile string
Home string
Archive string
SetPages string
MarketplaceType string
MarketplaceRelation string
MarketplaceTemplate string
@ -96,10 +95,6 @@ func (s *service) DerivePredefinedThreadIds() (DerivedSmartblockIds, error) {
if err != nil {
return DerivedSmartblockIds{}, err
}
setPages, err := s.derivedThreadIdByIndex(threadDerivedIndexSetPages)
if err != nil {
return DerivedSmartblockIds{}, err
}
mpType, err := s.derivedThreadIdByIndex(threadDerivedIndexMarketplaceType)
if err != nil {
return DerivedSmartblockIds{}, err
@ -119,7 +114,6 @@ func (s *service) DerivePredefinedThreadIds() (DerivedSmartblockIds, error) {
Profile: profile.String(),
Home: home.String(),
Archive: archive.String(),
SetPages: setPages.String(),
MarketplaceType: mpType.String(),
MarketplaceRelation: mpRelation.String(),
MarketplaceTemplate: mpTemplate.String(),
@ -199,13 +193,6 @@ func (s *service) EnsurePredefinedThreads(ctx context.Context, newAccount bool)
}
accountIds.Archive = archive.ID.String()
// set pages
setPages, _, err := s.derivedThreadEnsure(cctx, threadDerivedIndexSetPages, newAccount, true)
if err != nil {
return accountIds, err
}
accountIds.SetPages = setPages.ID.String()
// marketplace
marketplace, _, err := s.derivedThreadEnsure(cctx, threadDerivedIndexMarketplaceType, newAccount, true)
if err != nil {

View file

@ -8,6 +8,7 @@ import (
"fmt"
"github.com/anytypeio/go-anytype-middleware/core/anytype/config"
"github.com/anytypeio/go-anytype-middleware/core/block"
"github.com/anytypeio/go-anytype-middleware/core/block/simple/bookmark"
"github.com/anytypeio/go-anytype-middleware/core/block/simple/link"
"github.com/anytypeio/go-anytype-middleware/core/block/simple/text"
"github.com/gogo/protobuf/types"
@ -176,6 +177,16 @@ func (b *builtinObjects) createObject(ctx context.Context, rd io.ReadCloser) (er
a.Model().GetLink().TargetBlockId = newTarget
st.Set(simple.New(a.Model()))
case bookmark.Block:
newTarget := b.idsMap[a.Model().GetBookmark().TargetObjectId]
if newTarget == "" {
// maybe we should panic here?
log.Errorf("cant find target id for link: %s", a.Model().GetLink().TargetBlockId)
return true
}
a.Model().GetBookmark().TargetObjectId = newTarget
st.Set(simple.New(a.Model()))
case text.Block:
for i, mark := range a.Model().GetText().GetMarks().GetMarks() {
if mark.Type != model.BlockContentTextMark_Mention && mark.Type != model.BlockContentTextMark_Object {

View file

@ -1,41 +1,39 @@
package time
//TODO wrap all to structure and make common locale
import "time"
var Day = time.Hour * 24
var Week = Day * 7
func DayNumStart(dayNum int) time.Time {
t := time.Now()
year, month, day := t.Date()
func NewCalendar(t time.Time, loc *time.Location) Calendar {
if loc == nil {
loc = time.UTC
}
return Calendar{t:t, loc: loc}
}
type Calendar struct {
t time.Time
loc *time.Location
}
func (c *Calendar) DayNumStart(dayNum int) time.Time {
year, month, day := c.t.Date()
day = day + dayNum
return time.Date(year, month, day, 0, 0, 0, 0, time.UTC)
return time.Date(year, month, day, 0, 0, 0, 0, c.loc)
}
func DayNumEnd(dayNum int) time.Time {
t := time.Now()
year, month, day := t.Date()
func (c *Calendar) DayNumEnd(dayNum int) time.Time {
year, month, day := c.t.Date()
day = day + dayNum
return time.Date(year, month, day, 23, 59, 59, 0, time.UTC)
return time.Date(year, month, day, 23, 59, 59, 0, c.loc)
}
func DayStart(t time.Time) time.Time{
year, month, day := t.Date()
return time.Date(year, month, day, 0, 0, 0, 0, time.UTC)
}
func DayEnd(needDate time.Time) time.Time {
year, month, day := needDate.Date()
return time.Date(year, month, day, 23, 59, 59, 0, time.UTC)
}
func WeekNumStart(weekNum int) time.Time {
year, week := time.Now().ISOWeek()
func (c *Calendar) WeekNumStart(weekNum int) time.Time {
year, week := c.t.ISOWeek()
week = week + weekNum
// Start from the middle of the year:
t := time.Date(year, 7, 1, 0, 0, 0, 0, time.UTC)
t := time.Date(year, 7, 1, 0, 0, 0, 0, c.loc)
// Roll back to Monday:
if wd := t.Weekday(); wd == time.Sunday {
@ -51,49 +49,16 @@ func WeekNumStart(weekNum int) time.Time {
return t
}
func WeekNumEnd(weekNum int) time.Time {
return WeekNumStart(weekNum).Add(Week).Add(time.Nanosecond * -1)
func (c *Calendar) WeekNumEnd(weekNum int) time.Time {
return c.WeekNumStart(weekNum).Add(Week).Add(time.Nanosecond * -1)
}
func WeekStart(needDate time.Time) time.Time {
year, week := needDate.ISOWeek()
// Start from the middle of the year:
t := time.Date(year, 7, 1, 0, 0, 0, 0, time.UTC)
// Roll back to Monday:
if wd := t.Weekday(); wd == time.Sunday {
t = t.AddDate(0, 0, -6)
} else {
t = t.AddDate(0, 0, -int(wd)+1)
}
// Difference in weeks:
_, w := t.ISOWeek()
t = t.AddDate(0, 0, (week-w)*7)
return t
func (c *Calendar) MonthNumStart(monthNum int) time.Time {
needMonth := c.t.Month() + time.Month(monthNum)
return time.Date(c.t.Year(), needMonth, 1, 0, 0, 0, 0, c.loc)
}
func WeekEnd(needDate time.Time) time.Time {
return WeekStart(needDate).Add(Week).Add(time.Nanosecond * -1)
}
func MonthNumStart(monthNum int) time.Time {
t := time.Now()
needMonth := t.Month() + time.Month(monthNum)
return time.Date(t.Year(), needMonth, 1, 0, 0, 0, 0, time.UTC)
}
func MonthNumEnd(monthNum int) time.Time {
firstDay := MonthNumStart(monthNum)
func (c *Calendar) MonthNumEnd(monthNum int) time.Time {
firstDay := c.MonthNumStart(monthNum)
return firstDay.AddDate(0, 1, 0).Add(time.Nanosecond * -1)
}
func MonthStart(t time.Time) time.Time {
return time.Date(t.Year(), t.Month(), 1, 0, 0, 0, 0, time.UTC)
}
func MonthEnd(t time.Time) time.Time {
firstDay := MonthStart(t)
return firstDay.AddDate(0, 1, 0).Add(time.Nanosecond * -1)
}