mirror of
https://github.com/anyproto/anytype-heart.git
synced 2025-06-10 18:10:49 +09:00
merge
This commit is contained in:
commit
288cb6231e
46 changed files with 24063 additions and 1742 deletions
|
@ -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++ {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
210
core/block/editor/table/block.go
Normal file
210
core/block/editor/table/block.go
Normal 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
|
||||
}
|
||||
}
|
118
core/block/editor/table/block_test.go
Normal file
118
core/block/editor/table/block_test.go
Normal 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())
|
||||
}
|
||||
}
|
822
core/block/editor/table/table.go
Normal file
822
core/block/editor/table/table.go
Normal 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()
|
||||
}
|
1038
core/block/editor/table/table_test.go
Normal file
1038
core/block/editor/table/table_test.go
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
75
core/block/simple/table/row.go
Normal file
75
core/block/simple/table/row.go
Normal 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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
},
|
||||
|
|
|
@ -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(" ")
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
315
core/table.go
Normal 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)
|
||||
}
|
1549
docs/proto.md
1549
docs/proto.md
File diff suppressed because it is too large
Load diff
2
go.mod
2
go.mod
|
@ -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
1
go.sum
|
@ -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=
|
||||
|
|
17328
pb/commands.pb.go
17328
pb/commands.pb.go
File diff suppressed because it is too large
Load diff
1379
pb/events.pb.go
1379
pb/events.pb.go
File diff suppressed because it is too large
Load diff
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -40,6 +40,7 @@ var RequiredInternalRelations = []RelationKey{
|
|||
RelationKeyIsFavorite,
|
||||
RelationKeyWorkspaceId,
|
||||
RelationKeyLinks,
|
||||
RelationKeyInternalFlags,
|
||||
}
|
||||
|
||||
var FormatFilePossibleTargetObjectTypes = []string{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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{}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
Binary file not shown.
|
@ -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)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue