1
0
Fork 0
mirror of https://github.com/anyproto/anytype-heart.git synced 2025-06-08 05:47:07 +09:00

GO-4727 Merge branch 'main' of github.com:anyproto/anytype-heart into go-4727-local-counters-for-chat

This commit is contained in:
Sergey 2025-03-14 11:26:51 +01:00
commit 04aff3dabc
No known key found for this signature in database
GPG key ID: 3B6BEF79160221C6
51 changed files with 2379 additions and 1584 deletions

View file

@ -305,48 +305,82 @@ func getFileSizes() map[string]uint64 {
func getTableSizes(mw *core.Middleware) (tables map[string]uint64) {
tables = make(map[string]uint64)
cfg := mw.GetApp().MustComponent(config.CName).(*config.Config)
db, err := sql.Open("sqlite3", cfg.GetNewSpaceStorePath())
sqliteDBs, err := findSQLiteFiles(cfg.GetNewSpaceStorePath())
if err != nil {
fmt.Println("Error opening database:", err)
fmt.Printf("sqlite find error: %v\n", err)
return
}
for _, dbPath := range sqliteDBs {
err := processDatabase(dbPath, tables)
if err != nil {
fmt.Printf("error handling database %s: %v", dbPath, err)
}
}
fmt.Println("Tables:")
for table, count := range tables {
fmt.Printf("%s: %d\n", table, count)
}
return
}
func processDatabase(dbPath string, tables map[string]uint64) error {
db, err := sql.Open("sqlite3", dbPath)
if err != nil {
return fmt.Errorf("dp open error: %w", err)
}
defer db.Close()
err = db.Ping()
if err != nil {
fmt.Println("Error pinging database:", err)
return
return fmt.Errorf("error ping db: %w", err)
}
rows, err := db.Query("SELECT name FROM sqlite_master WHERE type='table';")
if err != nil {
fmt.Println("Error querying database:", err)
return fmt.Errorf("sql query error: %w", err)
}
defer rows.Close()
fmt.Println("Tables:")
for rows.Next() {
var tableName string
err := rows.Scan(&tableName)
if err != nil {
fmt.Println("Error scanning row:", err)
if err := rows.Scan(&tableName); err != nil {
fmt.Printf("table reading err: %v", err)
continue
}
var count int
var count uint64
err = db.QueryRow(fmt.Sprintf("SELECT COUNT(*) FROM %s;", tableName)).Scan(&count)
if err != nil {
fmt.Println("Error querying row:", err)
fmt.Printf("table query err in %s: %v", tableName, err)
continue
}
tables[fmt.Sprintf("t_%s", tableName)] = uint64(count)
key := fmt.Sprintf("t_%s", tableName)
tables[key] += count
fmt.Printf("%s: %d\n", tableName, count)
fmt.Printf("DB %s -> %s: %d records\n", dbPath, tableName, count)
}
if err := rows.Err(); err != nil {
fmt.Println("Error iterating over rows:", err)
return fmt.Errorf("row traverse error: %w", err)
}
return
return nil
}
func findSQLiteFiles(root string) ([]string, error) {
var dbFiles []string
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && filepath.Ext(path) == ".db" {
dbFiles = append(dbFiles, path)
}
return nil
})
return dbFiles, err
}
func init() {

View file

@ -132,7 +132,7 @@ func (s *Service) setAccountAndProfileDetails(ctx context.Context, req *pb.RpcAc
profileDetails = append(profileDetails, commonDetails...)
if req.GetAvatarLocalPath() != "" {
hash, _, err := bs.UploadFile(context.Background(), techSpaceId, block.FileUploadRequest{
hash, _, _, err := bs.UploadFile(context.Background(), techSpaceId, block.FileUploadRequest{
RpcFileUploadRequest: pb.RpcFileUploadRequest{
LocalPath: req.GetAvatarLocalPath(),
Type: model.BlockContentFile_Image,

View file

@ -206,7 +206,7 @@ func (s *Service) CreateCollection(details *domain.Details, flags []*model.Inter
tmpls := []template.StateTransformer{}
blockContent := template.MakeDataviewContent(true, nil, nil)
blockContent := template.MakeDataviewContent(true, nil, nil, "")
tmpls = append(tmpls,
template.WithDataview(blockContent, false),
)

View file

@ -4,19 +4,27 @@ import (
"fmt"
"github.com/anyproto/any-sync/app"
"github.com/anyproto/any-sync/app/logger"
"go.uber.org/zap"
"github.com/anyproto/anytype-heart/core/block/cache"
"github.com/anyproto/anytype-heart/core/block/editor/dataview"
"github.com/anyproto/anytype-heart/core/block/editor/smartblock"
"github.com/anyproto/anytype-heart/core/block/editor/state"
"github.com/anyproto/anytype-heart/core/block/editor/template"
"github.com/anyproto/anytype-heart/core/block/object/idresolver"
dvblock "github.com/anyproto/anytype-heart/core/block/simple/dataview"
"github.com/anyproto/anytype-heart/core/session"
"github.com/anyproto/anytype-heart/pb"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/objectstore"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/objectstore/spaceindex"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
)
const CName = "dataview.service"
var log = logger.NewNamed(CName)
type Service interface {
app.Component
AddDataviewFilter(ctx session.Context, objectId, blockId, viewId string, filter *model.BlockContentDataviewFilter) error
@ -57,11 +65,15 @@ func New() Service {
}
type service struct {
getter cache.ObjectGetter
getter cache.ObjectGetter
objectStore objectstore.ObjectStore
idResolver idresolver.Resolver
}
func (s *service) Init(a *app.App) error {
s.getter = app.MustComponent[cache.ObjectGetter](a)
s.objectStore = app.MustComponent[objectstore.ObjectStore](a)
s.idResolver = app.MustComponent[idresolver.Resolver](a)
return nil
}
@ -196,13 +208,16 @@ func (s *service) AddDataviewViewRelation(
objectId, blockId, viewId string,
relation *model.BlockContentDataviewRelation,
) (err error) {
return cache.DoStateCtx(s.getter, ctx, objectId, func(s *state.State, d dataview.Dataview) error {
dv, err := d.GetDataviewBlock(s, blockId)
return cache.DoStateCtx(s.getter, ctx, objectId, func(st *state.State, d dataview.Dataview) error {
dv, err := d.GetDataviewBlock(st, blockId)
if err != nil {
return err
}
return dv.AddViewRelation(viewId, relation)
if err = dv.AddViewRelation(viewId, relation); err != nil {
return err
}
s.syncViewRelationsAndRelationLinks(objectId, viewId, dv)
return nil
})
}
@ -211,13 +226,16 @@ func (s *service) RemoveDataviewViewRelations(
objectId, blockId, viewId string,
relationKeys []string,
) (err error) {
return cache.DoStateCtx(s.getter, ctx, objectId, func(s *state.State, d dataview.Dataview) error {
dv, err := d.GetDataviewBlock(s, blockId)
return cache.DoStateCtx(s.getter, ctx, objectId, func(st *state.State, d dataview.Dataview) error {
dv, err := d.GetDataviewBlock(st, blockId)
if err != nil {
return err
}
return dv.RemoveViewRelations(viewId, relationKeys)
if err = dv.RemoveViewRelations(viewId, relationKeys); err != nil {
return err
}
s.syncViewRelationsAndRelationLinks(objectId, viewId, dv)
return nil
})
}
@ -227,13 +245,16 @@ func (s *service) ReplaceDataviewViewRelation(
relationKey string,
relation *model.BlockContentDataviewRelation,
) (err error) {
return cache.DoStateCtx(s.getter, ctx, objectId, func(s *state.State, d dataview.Dataview) error {
dv, err := d.GetDataviewBlock(s, blockId)
return cache.DoStateCtx(s.getter, ctx, objectId, func(st *state.State, d dataview.Dataview) error {
dv, err := d.GetDataviewBlock(st, blockId)
if err != nil {
return err
}
return dv.ReplaceViewRelation(viewId, relationKey, relation)
if err = dv.ReplaceViewRelation(viewId, relationKey, relation); err != nil {
return err
}
s.syncViewRelationsAndRelationLinks(objectId, viewId, dv)
return nil
})
}
@ -242,16 +263,71 @@ func (s *service) ReorderDataviewViewRelations(
objectId, blockId, viewId string,
relationKeys []string,
) (err error) {
return cache.DoStateCtx(s.getter, ctx, objectId, func(s *state.State, d dataview.Dataview) error {
dv, err := d.GetDataviewBlock(s, blockId)
return cache.DoStateCtx(s.getter, ctx, objectId, func(st *state.State, d dataview.Dataview) error {
dv, err := d.GetDataviewBlock(st, blockId)
if err != nil {
return err
}
return dv.ReorderViewRelations(viewId, relationKeys)
if err = dv.ReorderViewRelations(viewId, relationKeys); err != nil {
return err
}
s.syncViewRelationsAndRelationLinks(objectId, viewId, dv)
return nil
})
}
func (s *service) syncViewRelationsAndRelationLinks(objectId, viewId string, dv dvblock.Block) {
view, err := dv.GetView(viewId)
if err != nil {
log.Error("failed to get view", zap.String("viewId", viewId), zap.Error(err))
return
}
relationLinksKeys := make(map[string]struct{}, len(dv.ListRelationLinks()))
for _, relLink := range dv.ListRelationLinks() {
relationLinksKeys[relLink.Key] = struct{}{}
}
var spaceIndex spaceindex.Store
getRelationLink := func(key string) (*model.RelationLink, error) {
if spaceIndex == nil {
spaceId, err := s.idResolver.ResolveSpaceID(objectId)
if err != nil {
return nil, fmt.Errorf("failed to resolve space id: %w", err)
}
spaceIndex = s.objectStore.SpaceIndex(spaceId)
}
return spaceIndex.GetRelationLink(key)
}
currentViewKeys := make(map[string]struct{}, len(view.Relations))
newViewRelations := view.Relations[:0]
for _, rel := range view.Relations {
newViewRelations = append(newViewRelations, rel)
currentViewKeys[rel.Key] = struct{}{}
if _, ok := relationLinksKeys[rel.Key]; !ok {
relLink, err := getRelationLink(rel.Key)
if err != nil {
log.Error("failed to get relation link", zap.String("key", rel.Key), zap.Error(err))
continue
}
_ = dv.AddRelation(relLink) // nolint:errcheck
}
}
for _, relLink := range dv.ListRelationLinks() {
_, ok := currentViewKeys[relLink.Key]
if !ok {
newViewRelations = append(newViewRelations, &model.BlockContentDataviewRelation{
Key: relLink.Key,
Width: dvblock.DefaultViewRelationWidth,
IsVisible: false,
})
}
}
view.Relations = newViewRelations
}
func (s *service) UpdateDataviewView(ctx session.Context, req pb.RpcBlockDataviewViewUpdateRequest) error {
return cache.Do(s.getter, req.ContextId, func(b dataview.Dataview) error {
return b.UpdateView(ctx, req.BlockId, req.ViewId, req.View, true)

View file

@ -0,0 +1,92 @@
package dataviewservice
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/anyproto/anytype-heart/core/block/object/idresolver/mock_idresolver"
dvblock "github.com/anyproto/anytype-heart/core/block/simple/dataview"
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/objectstore"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
)
const (
spaceId = "spc1"
objectId = "obj1"
blockId = "dataview"
testViewId = "view1"
)
type fixture struct {
store *objectstore.StoreFixture
idResolver *mock_idresolver.MockResolver
*service
}
func newFixture(t *testing.T) *fixture {
store := objectstore.NewStoreFixture(t)
idResolver := mock_idresolver.NewMockResolver(t)
return &fixture{
store: store,
idResolver: idResolver,
service: &service{
objectStore: store,
idResolver: idResolver,
},
}
}
func TestService_syncViewRelationsAndRelationLinks(t *testing.T) {
t.Run("relations are synced", func(t *testing.T) {
// given
fx := newFixture(t)
fx.idResolver.EXPECT().ResolveSpaceID(objectId).Return(spaceId, nil)
dv := makeDataviewForViewRelationsTest([]*model.RelationLink{
{Key: bundle.RelationKeyName.String(), Format: model.RelationFormat_longtext},
{Key: bundle.RelationKeyBacklinks.String(), Format: model.RelationFormat_object},
// relation links do not include CreatedDate and Type, so they should be inserted using objectStore
}, []*model.BlockContentDataviewRelation{
{Key: bundle.RelationKeyName.String(), IsVisible: true, Width: dvblock.DefaultViewRelationWidth},
{Key: bundle.RelationKeyCreatedDate.String(), IsVisible: true, Width: dvblock.DefaultViewRelationWidth},
{Key: bundle.RelationKeyType.String(), IsVisible: false, Width: dvblock.DefaultViewRelationWidth},
// view relations do not include Backlinks, so it should be inserted with default settings
})
want := makeDataviewForViewRelationsTest([]*model.RelationLink{
{Key: bundle.RelationKeyName.String(), Format: model.RelationFormat_longtext},
{Key: bundle.RelationKeyBacklinks.String(), Format: model.RelationFormat_object},
{Key: bundle.RelationKeyCreatedDate.String(), Format: model.RelationFormat_date},
{Key: bundle.RelationKeyType.String(), Format: model.RelationFormat_object},
}, []*model.BlockContentDataviewRelation{
{Key: bundle.RelationKeyName.String(), IsVisible: true, Width: dvblock.DefaultViewRelationWidth},
{Key: bundle.RelationKeyCreatedDate.String(), IsVisible: true, Width: dvblock.DefaultViewRelationWidth},
{Key: bundle.RelationKeyType.String(), IsVisible: false, Width: dvblock.DefaultViewRelationWidth},
{Key: bundle.RelationKeyBacklinks.String(), IsVisible: false, Width: dvblock.DefaultViewRelationWidth},
})
// when
fx.syncViewRelationsAndRelationLinks(objectId, testViewId, dv)
// then
assert.Equal(t, want, dv)
})
}
func makeDataviewForViewRelationsTest(relationLinks []*model.RelationLink, relations []*model.BlockContentDataviewRelation) dvblock.Block {
return dvblock.NewDataview(&model.Block{
Id: blockId,
Content: &model.BlockContentOfDataview{
Dataview: &model.BlockContentDataview{
RelationLinks: relationLinks,
Views: []*model.BlockContentDataviewView{
{
Id: testViewId,
Relations: relations,
},
},
},
},
}).(dvblock.Block)
}

View file

@ -146,9 +146,6 @@ func (s *service) ObjectTypeListConflictingRelations(spaceId, typeObjectId strin
if err != nil {
return nil, fmt.Errorf("failed to fetch relations by keys: %w", err)
}
if len(records) != len(allRelationKeys) {
return nil, fmt.Errorf("failed to query relations from store, number of records is less than expected")
}
conflictingRelations := make([]string, 0, len(records))
for _, record := range records {

View file

@ -364,7 +364,7 @@ func (s *Service) CreateAndUploadFile(
return
}
func (s *Service) UploadFile(ctx context.Context, spaceId string, req FileUploadRequest) (objectId string, details *domain.Details, err error) {
func (s *Service) UploadFile(ctx context.Context, spaceId string, req FileUploadRequest) (objectId string, fileType model.BlockContentFileType, details *domain.Details, err error) {
upl := s.fileUploaderService.NewUploader(spaceId, req.ObjectOrigin)
if req.DisableEncryption {
log.Errorf("DisableEncryption is deprecated and has no effect")
@ -388,9 +388,10 @@ func (s *Service) UploadFile(ctx context.Context, spaceId string, req FileUpload
}
res := upl.Upload(ctx)
if res.Err != nil {
return "", nil, res.Err
return "", 0, nil, res.Err
}
return res.FileObjectId, res.FileObjectDetails, nil
return res.FileObjectId, res.Type, res.FileObjectDetails, nil
}
func (s *Service) DropFiles(req pb.RpcFileDropRequest) (err error) {

View file

@ -183,7 +183,7 @@ func (c *layoutConverter) fromAnyToSet(st *state.State) error {
source := st.Details().GetStringList(bundle.RelationKeySetOf)
addFeaturedRelationSetOf(st)
dvBlock, err := dataview.BlockBySource(c.objectStore.SpaceIndex(st.SpaceID()), source)
dvBlock, err := dataview.BlockBySource(c.objectStore.SpaceIndex(st.SpaceID()), source, "")
if err != nil {
return err
}
@ -296,7 +296,7 @@ func (c *layoutConverter) fromNoteToCollection(st *state.State) error {
}
func (c *layoutConverter) fromAnyToCollection(st *state.State) error {
blockContent := template.MakeDataviewContent(true, nil, nil)
blockContent := template.MakeDataviewContent(true, nil, nil, "")
if err := c.insertTypeLevelFieldsToDataview(blockContent, st); err != nil {
log.Error("failed to insert type level fields to dataview block", zap.Error(err))
}

View file

@ -93,7 +93,7 @@ func (d *sdataview) SetSource(ctx session.Context, blockId string, source []stri
return d.Apply(s, smartblock.NoRestrictions, smartblock.KeepInternalFlags)
}
dvContent, err := BlockBySource(d.objectStore, source)
dvContent, err := BlockBySource(d.objectStore, source, "")
if err != nil {
return
}
@ -119,7 +119,7 @@ func (d *sdataview) SetSourceInSet(ctx session.Context, source []string) (err er
}
var viewRelations []*model.BlockContentDataviewRelation
if srcBlock, err := BlockBySource(d.objectStore, source); err != nil {
if srcBlock, err := BlockBySource(d.objectStore, source, ""); err != nil {
log.Errorf("failed to build dataview block to modify view relation lists: %v", err)
} else {
dv.SetRelations(srcBlock.Dataview.RelationLinks)
@ -466,16 +466,16 @@ func getDataviewBlock(s *state.State, id string) (dataview.Block, error) {
return nil, fmt.Errorf("not a dataview block")
}
func BlockBySource(objectStore spaceindex.Store, sources []string) (*model.BlockContentOfDataview, error) {
func BlockBySource(objectStore spaceindex.Store, sources []string, forceViewId string) (*model.BlockContentOfDataview, error) {
// Empty schema
if len(sources) == 0 {
return template.MakeDataviewContent(false, nil, nil), nil
return template.MakeDataviewContent(false, nil, nil, forceViewId), nil
}
// Try object type
objectType, err := objectStore.GetObjectType(sources[0])
if err == nil {
return template.MakeDataviewContent(false, objectType, nil), nil
return template.MakeDataviewContent(false, objectType, nil, forceViewId), nil
}
// Finally, try relations
@ -488,5 +488,5 @@ func BlockBySource(objectStore spaceindex.Store, sources []string) (*model.Block
relations = append(relations, (&relationutils.Relation{Relation: rel}).RelationLink())
}
return template.MakeDataviewContent(false, objectType, relations), nil
return template.MakeDataviewContent(false, objectType, relations, forceViewId), nil
}

View file

@ -30,6 +30,8 @@ import (
"github.com/anyproto/anytype-heart/util/pbtypes"
)
const ObjectTypeAllViewId = "all"
var typeRequiredRelations = append(typeAndRelationRequiredRelations,
bundle.RelationKeyRecommendedRelations,
bundle.RelationKeyRecommendedFeaturedRelations,
@ -105,6 +107,7 @@ func (ot *ObjectType) CreationStateMigration(ctx *smartblock.InitContext) migrat
template.WithTitle,
template.WithLayout(model.ObjectType_objectType),
}
templates = append(templates, ot.dataviewTemplates()...)
template.InitTemplate(s, templates...)
},
@ -121,6 +124,12 @@ func (ot *ObjectType) StateMigrations() migration.Migrations {
Version: 3,
Proc: ot.featuredRelationsMigration,
},
{
Version: 4,
Proc: func(s *state.State) {
template.InitTemplate(s, ot.dataviewTemplates()...)
},
},
})
}
@ -389,6 +398,30 @@ func (ot *ObjectType) queryObjectsAndTemplates() ([]database.Record, error) {
return append(records, templates...), nil
}
func (ot *ObjectType) dataviewTemplates() []template.StateTransformer {
details := ot.Details()
name := details.GetString(bundle.RelationKeyName)
key := details.GetString(bundle.RelationKeyUniqueKey)
dvContent := template.MakeDataviewContent(false, &model.ObjectType{
Url: ot.Id(),
Name: name,
// todo: add RelationLinks, because they are not indexed at this moment :(
Key: key,
}, []*model.RelationLink{
{
Key: bundle.RelationKeyName.String(),
Format: model.RelationFormat_longtext,
},
}, ObjectTypeAllViewId)
dvContent.Dataview.TargetObjectId = ot.Id()
return []template.StateTransformer{
template.WithDataviewID(state.DataviewBlockID, dvContent, false),
template.WithForcedDetail(bundle.RelationKeySetOf, domain.StringList([]string{ot.Id()})),
}
}
type layoutRelationsChanges struct {
relationsToRemove []domain.RelationKey
isLayoutFound bool

View file

@ -174,7 +174,7 @@ func (p *Page) deleteRelationOptions(spaceID string, relationKey string) error {
func (p *Page) CreationStateMigration(ctx *smartblock.InitContext) migration.Migration {
return migration.Migration{
Version: 2,
Version: 4,
Proc: func(s *state.State) {
layout, ok := ctx.State.Layout()
if !ok {
@ -260,10 +260,5 @@ func (p *Page) CreationStateMigration(ctx *smartblock.InitContext) migration.Mig
}
func (p *Page) StateMigrations() migration.Migrations {
return migration.MakeMigrations([]migration.Migration{
{
Version: 2,
Proc: func(s *state.State) {},
},
})
return migration.Migrations{Migrations: []migration.Migration{}}
}

View file

@ -39,6 +39,7 @@ var spaceViewRequiredRelations = []domain.RelationKey{
bundle.RelationKeySpaceAccountStatus,
bundle.RelationKeySpaceShareableStatus,
bundle.RelationKeySpaceAccessType,
bundle.RelationKeySpaceUxType,
bundle.RelationKeyLatestAclHeadId,
bundle.RelationKeyChatId,
bundle.RelationKeyReadersLimit,
@ -256,6 +257,7 @@ var workspaceKeysToCopy = []domain.RelationKey{
bundle.RelationKeyIconImage,
bundle.RelationKeyIconOption,
bundle.RelationKeySpaceDashboardId,
bundle.RelationKeySpaceUxType,
bundle.RelationKeyCreatedDate,
bundle.RelationKeyChatId,
bundle.RelationKeyDescription,

View file

@ -46,7 +46,7 @@ var (
}
)
func MakeDataviewContent(isCollection bool, ot *model.ObjectType, relLinks []*model.RelationLink) *model.BlockContentOfDataview {
func MakeDataviewContent(isCollection bool, ot *model.ObjectType, relLinks []*model.RelationLink, forceViewId string) *model.BlockContentOfDataview {
var (
defaultRelations = defaultCollectionRelations
visibleRelations = defaultVisibleRelations
@ -67,14 +67,17 @@ func MakeDataviewContent(isCollection bool, ot *model.ObjectType, relLinks []*mo
}
relationLinks, viewRelations := generateRelationLists(defaultRelations, relLinks, visibleRelations)
viewId := forceViewId
if viewId == "" {
viewId = bson.NewObjectId().Hex()
}
return &model.BlockContentOfDataview{
Dataview: &model.BlockContentDataview{
IsCollection: isCollection,
RelationLinks: relationLinks,
Views: []*model.BlockContentDataviewView{
{
Id: bson.NewObjectId().Hex(),
Id: viewId,
Type: model.BlockContentDataviewView_Table,
Name: defaultViewName,
Sorts: sorts,

View file

@ -92,7 +92,7 @@ func TestMakeDataviewContent(t *testing.T) {
},
} {
t.Run(tc.name, func(t *testing.T) {
block := MakeDataviewContent(tc.isCollection, tc.ot, tc.relLinks)
block := MakeDataviewContent(tc.isCollection, tc.ot, tc.relLinks, "")
assertDataviewBlock(t, block, tc.isCollection, tc.expectedRelations, tc.isVisible)
})
}

View file

@ -1,6 +1,9 @@
package editor
import (
"context"
"slices"
"github.com/anyproto/anytype-heart/core/block/editor/basic"
"github.com/anyproto/anytype-heart/core/block/editor/converter"
"github.com/anyproto/anytype-heart/core/block/editor/smartblock"
@ -8,6 +11,7 @@ import (
"github.com/anyproto/anytype-heart/core/block/editor/template"
"github.com/anyproto/anytype-heart/core/block/editor/widget"
"github.com/anyproto/anytype-heart/core/block/migration"
"github.com/anyproto/anytype-heart/core/block/simple"
"github.com/anyproto/anytype-heart/core/domain"
"github.com/anyproto/anytype-heart/core/session"
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
@ -49,7 +53,7 @@ func (w *WidgetObject) Init(ctx *smartblock.InitContext) (err error) {
func (w *WidgetObject) CreationStateMigration(ctx *smartblock.InitContext) migration.Migration {
return migration.Migration{
Version: 1,
Version: 2,
Proc: func(st *state.State) {
template.InitTemplate(st,
template.WithEmpty,
@ -61,8 +65,54 @@ func (w *WidgetObject) CreationStateMigration(ctx *smartblock.InitContext) migra
}
}
func replaceWidgetTarget(st *state.State, targetFrom string, targetTo string, viewId string, layout model.BlockContentWidgetLayout) {
st.Iterate(func(b simple.Block) (isContinue bool) {
if wc, ok := b.Model().Content.(*model.BlockContentOfWidget); ok {
// get child
if len(b.Model().GetChildrenIds()) > 0 {
child := st.Get(b.Model().GetChildrenIds()[0])
childBlock := st.Get(child.Model().Id)
if linkBlock, ok := childBlock.Model().Content.(*model.BlockContentOfLink); ok {
if linkBlock.Link.TargetBlockId == targetFrom {
targets := st.Details().Get(bundle.RelationKeyAutoWidgetTargets).StringList()
if slices.Contains(targets, targetTo) {
return false
}
targets = append(targets, targetTo)
st.SetDetail(bundle.RelationKeyAutoWidgetTargets, domain.StringList(targets))
linkBlock.Link.TargetBlockId = targetTo
wc.Widget.ViewId = viewId
wc.Widget.Layout = layout
return false
}
}
}
}
return true
})
}
func (w *WidgetObject) StateMigrations() migration.Migrations {
return migration.MakeMigrations(nil)
return migration.MakeMigrations([]migration.Migration{
{
Version: 2,
Proc: func(s *state.State) {
spc := w.Space()
setTypeId, err := spc.GetTypeIdByKey(context.Background(), bundle.TypeKeySet)
if err != nil {
return
}
collectionTypeId, err := spc.GetTypeIdByKey(context.Background(), bundle.TypeKeyCollection)
if err != nil {
return
}
replaceWidgetTarget(s, widget.DefaultWidgetCollection, collectionTypeId, ObjectTypeAllViewId, model.BlockContentWidget_View)
replaceWidgetTarget(s, widget.DefaultWidgetSet, setTypeId, ObjectTypeAllViewId, model.BlockContentWidget_View)
},
},
},
)
}
func (w *WidgetObject) Unlink(ctx session.Context, ids ...string) (err error) {

View file

@ -72,7 +72,6 @@ func (w *widget) CreateBlock(s *state.State, req *pb.RpcBlockCreateWidgetRequest
return "", fmt.Errorf("unsupported widget content: %T", req.Block.Content)
}
req.Block.Id = ""
req.Block.ChildrenIds = nil
b := simple.New(req.Block)
if err := b.Validate(); err != nil {

View file

@ -164,6 +164,7 @@ type exportContext struct {
linkStateFilters *state.Filters
isLinkProcess bool
includeBackLinks bool
includeSpace bool
relations map[string]struct{}
setOfList map[string]struct{}
objectTypes map[string]struct{}
@ -185,6 +186,7 @@ func newExportContext(e *export, req pb.RpcObjectListExportRequest) *exportConte
zip: req.Zip,
linkStateFilters: pbFiltersToState(req.LinksStateFilters),
includeBackLinks: req.IncludeBacklinks,
includeSpace: req.IncludeSpace,
setOfList: make(map[string]struct{}),
objectTypes: make(map[string]struct{}),
relations: make(map[string]struct{}),
@ -211,6 +213,7 @@ func (e *exportContext) copy() *exportContext {
relations: e.relations,
setOfList: e.setOfList,
objectTypes: e.objectTypes,
includeSpace: e.includeSpace,
}
}
@ -233,7 +236,7 @@ func (e *exportContext) exportObjects(ctx context.Context, queue process.Queue)
cleanupFile(wr)
}
}()
err = e.docsForExport()
err = e.docsForExport(ctx)
if err != nil {
return "", 0, err
}
@ -356,19 +359,19 @@ func isAnyblockExport(format model.ExportFormat) bool {
return format == model.Export_Protobuf || format == model.Export_JSON
}
func (e *exportContext) docsForExport() (err error) {
func (e *exportContext) docsForExport(ctx context.Context) (err error) {
isProtobuf := isAnyblockExport(e.format)
if len(e.reqIds) == 0 {
return e.getExistedObjects(isProtobuf)
}
if len(e.reqIds) > 0 {
return e.getObjectsByIDs(isProtobuf)
return e.getObjectsByIDs(ctx, isProtobuf)
}
return
}
func (e *exportContext) getObjectsByIDs(isProtobuf bool) error {
func (e *exportContext) getObjectsByIDs(ctx context.Context, isProtobuf bool) error {
res, err := e.queryAndFilterObjectsByRelation(e.spaceId, e.reqIds, bundle.RelationKeyId)
if err != nil {
return err
@ -377,6 +380,12 @@ func (e *exportContext) getObjectsByIDs(isProtobuf bool) error {
id := object.Details.GetString(bundle.RelationKeyId)
e.docs[id] = &Doc{Details: object.Details}
}
if e.includeSpace {
err = e.addSpaceToDocs(ctx)
if err != nil {
return err
}
}
if isProtobuf {
return e.processProtobuf()
}
@ -417,6 +426,23 @@ func (e *exportContext) queryObjectsByRelation(spaceId string, reqIds []string,
})
}
func (e *exportContext) addSpaceToDocs(ctx context.Context) error {
space, err := e.spaceService.Get(ctx, e.spaceId)
if err != nil {
return err
}
workspaceId := space.DerivedIDs().Workspace
records, err := e.objectStore.SpaceIndex(e.spaceId).QueryByIds([]string{workspaceId})
if err != nil {
return err
}
if len(records) == 0 {
return fmt.Errorf("no objects found for space %s", workspaceId)
}
e.docs[workspaceId] = &Doc{Details: records[0].Details, isLink: true}
return nil
}
func (e *exportContext) processNotProtobuf() error {
ids := listObjectIds(e.docs)
if e.includeFiles {
@ -734,7 +760,7 @@ func (e *exportContext) getRelationsFromStore(relations []string) ([]database.Re
func (e *exportContext) addRelation(relation database.Record) {
relationKey := domain.RelationKey(relation.Details.GetString(bundle.RelationKeyRelationKey))
if relationKey != "" && !bundle.HasRelation(relationKey) {
if relationKey != "" {
id := relation.Details.GetString(bundle.RelationKeyId)
e.docs[id] = &Doc{Details: relation.Details, isLink: e.isLinkProcess}
}

View file

@ -29,6 +29,9 @@ import (
"github.com/anyproto/anytype-heart/pkg/lib/localstore/objectstore"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/objectstore/spaceindex"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
"github.com/anyproto/anytype-heart/pkg/lib/threads"
"github.com/anyproto/anytype-heart/space/clientspace/mock_clientspace"
"github.com/anyproto/anytype-heart/space/mock_space"
"github.com/anyproto/anytype-heart/space/spacecore/typeprovider/mock_typeprovider"
"github.com/anyproto/anytype-heart/tests/testutil"
)
@ -810,6 +813,142 @@ func TestExport_Export(t *testing.T) {
objectPath := filepath.Join(objectsDirectory, link1+".pb.json")
assert.False(t, fileNames[objectPath])
})
t.Run("export with space", func(t *testing.T) {
// given
storeFixture := objectstore.NewStoreFixture(t)
objectTypeId := "customObjectType"
objectTypeUniqueKey, err := domain.NewUniqueKey(smartblock.SmartBlockTypeObjectType, objectTypeId)
assert.Nil(t, err)
objectID := "id"
workspaceId := "workspaceId"
storeFixture.AddObjects(t, spaceId, []spaceindex.TestObject{
{
bundle.RelationKeyId: domain.String(objectID),
bundle.RelationKeyType: domain.String(objectTypeId),
bundle.RelationKeySpaceId: domain.String(spaceId),
},
{
bundle.RelationKeyId: domain.String(workspaceId),
bundle.RelationKeyType: domain.String(objectTypeId),
bundle.RelationKeySpaceId: domain.String(spaceId),
bundle.RelationKeyResolvedLayout: domain.Int64(int64(model.ObjectType_space)),
},
{
bundle.RelationKeyId: domain.String(objectTypeId),
bundle.RelationKeyUniqueKey: domain.String(objectTypeUniqueKey.Marshal()),
bundle.RelationKeyResolvedLayout: domain.Int64(int64(model.ObjectType_objectType)),
bundle.RelationKeyRecommendedRelations: domain.StringList([]string{addr.MissingObject}),
bundle.RelationKeySpaceId: domain.String(spaceId),
},
})
objectGetter := mock_cache.NewMockObjectGetter(t)
smartBlockTest := smarttest.New(objectID)
doc := smartBlockTest.NewState().SetDetails(domain.NewDetailsFromMap(map[domain.RelationKey]domain.Value{
bundle.RelationKeyId: domain.String(objectID),
bundle.RelationKeyType: domain.String(objectTypeId),
}))
doc.AddRelationLinks(&model.RelationLink{
Key: bundle.RelationKeyId.String(),
Format: model.RelationFormat_longtext,
}, &model.RelationLink{
Key: bundle.RelationKeyType.String(),
Format: model.RelationFormat_longtext,
})
smartBlockTest.Doc = doc
workspaceTest := smarttest.New(workspaceId)
workspaceDoc := workspaceTest.NewState().SetDetails(domain.NewDetailsFromMap(map[domain.RelationKey]domain.Value{
bundle.RelationKeyId: domain.String(workspaceId),
bundle.RelationKeyType: domain.String(objectTypeId),
}))
workspaceDoc.AddRelationLinks(&model.RelationLink{
Key: bundle.RelationKeyId.String(),
Format: model.RelationFormat_longtext,
}, &model.RelationLink{
Key: bundle.RelationKeyType.String(),
Format: model.RelationFormat_longtext,
})
workspaceTest.Doc = workspaceDoc
objectType := smarttest.New(objectTypeId)
objectTypeDoc := objectType.NewState().SetDetails(domain.NewDetailsFromMap(map[domain.RelationKey]domain.Value{
bundle.RelationKeyId: domain.String(objectTypeId),
bundle.RelationKeyType: domain.String(objectTypeId),
}))
objectTypeDoc.AddRelationLinks(&model.RelationLink{
Key: bundle.RelationKeyId.String(),
Format: model.RelationFormat_longtext,
}, &model.RelationLink{
Key: bundle.RelationKeyType.String(),
Format: model.RelationFormat_longtext,
})
objectType.Doc = objectTypeDoc
objectType.SetType(smartblock.SmartBlockTypeObjectType)
objectGetter.EXPECT().GetObject(context.Background(), objectID).Return(smartBlockTest, nil)
objectGetter.EXPECT().GetObject(context.Background(), objectTypeId).Return(objectType, nil)
objectGetter.EXPECT().GetObject(context.Background(), workspaceId).Return(workspaceTest, nil)
a := &app.App{}
mockSender := mock_event.NewMockSender(t)
mockSender.EXPECT().Broadcast(mock.Anything).Return()
a.Register(testutil.PrepareMock(context.Background(), a, mockSender))
service := process.New()
err = service.Init(a)
assert.Nil(t, err)
notifications := mock_notifications.NewMockNotifications(t)
notificationSend := make(chan struct{})
notifications.EXPECT().CreateAndSend(mock.Anything).RunAndReturn(func(notification *model.Notification) error {
close(notificationSend)
return nil
})
spaceService := mock_space.NewMockService(t)
space := mock_clientspace.NewMockSpace(t)
space.EXPECT().DerivedIDs().Return(threads.DerivedSmartblockIds{Workspace: workspaceId})
spaceService.EXPECT().Get(context.Background(), spaceId).Return(space, nil)
e := &export{
objectStore: storeFixture,
picker: objectGetter,
processService: service,
notificationService: notifications,
spaceService: spaceService,
}
// when
path, success, err := e.Export(context.Background(), pb.RpcObjectListExportRequest{
SpaceId: spaceId,
Path: t.TempDir(),
ObjectIds: []string{objectID},
Format: model.Export_Protobuf,
Zip: true,
IncludeNested: true,
IncludeFiles: true,
IsJson: true,
IncludeSpace: true,
})
// then
<-notificationSend
assert.Nil(t, err)
assert.Equal(t, 3, success)
reader, err := zip.OpenReader(path)
assert.Nil(t, err)
assert.Len(t, reader.File, 3)
fileNames := make(map[string]bool, 3)
for _, file := range reader.File {
fileNames[file.Name] = true
}
objectPath := filepath.Join(objectsDirectory, workspaceId+".pb.json")
assert.True(t, fileNames[objectPath])
})
}
func Test_docsForExport(t *testing.T) {
@ -880,7 +1019,7 @@ func Test_docsForExport(t *testing.T) {
})
// when
err := expCtx.docsForExport()
err := expCtx.docsForExport(nil)
// then
assert.Nil(t, err)
@ -935,7 +1074,7 @@ func Test_docsForExport(t *testing.T) {
})
// when
err := expCtx.docsForExport()
err := expCtx.docsForExport(nil)
// then
assert.Nil(t, err)
@ -1001,7 +1140,7 @@ func Test_docsForExport(t *testing.T) {
})
// when
err = expCtx.docsForExport()
err = expCtx.docsForExport(nil)
// then
assert.Nil(t, err)
@ -1077,7 +1216,7 @@ func Test_docsForExport(t *testing.T) {
})
// when
err = expCtx.docsForExport()
err = expCtx.docsForExport(nil)
// then
assert.Nil(t, err)
@ -1154,7 +1293,7 @@ func Test_docsForExport(t *testing.T) {
})
// when
err = expCtx.docsForExport()
err = expCtx.docsForExport(nil)
// then
assert.Nil(t, err)
@ -1241,7 +1380,7 @@ func Test_docsForExport(t *testing.T) {
})
// when
err = expCtx.docsForExport()
err = expCtx.docsForExport(nil)
// then
assert.Nil(t, err)
@ -1410,7 +1549,7 @@ func Test_docsForExport(t *testing.T) {
})
// when
err = expCtx.docsForExport()
err = expCtx.docsForExport(nil)
// then
assert.Nil(t, err)
@ -1484,7 +1623,7 @@ func Test_docsForExport(t *testing.T) {
})
// when
err = expCtx.docsForExport()
err = expCtx.docsForExport(nil)
// then
assert.Nil(t, err)
@ -1564,7 +1703,7 @@ func Test_docsForExport(t *testing.T) {
})
// when
err = expCtx.docsForExport()
err = expCtx.docsForExport(nil)
// then
assert.Nil(t, err)
@ -1686,11 +1825,11 @@ func Test_docsForExport(t *testing.T) {
})
// when
err = expCtx.docsForExport()
err = expCtx.docsForExport(nil)
// then
assert.Nil(t, err)
assert.Equal(t, 3, len(expCtx.docs))
assert.Equal(t, 4, len(expCtx.docs))
})
t.Run("objects without file", func(t *testing.T) {
// given
@ -1766,7 +1905,7 @@ func Test_docsForExport(t *testing.T) {
})
// when
err = expCtx.docsForExport()
err = expCtx.docsForExport(nil)
// then
assert.Nil(t, err)
@ -1824,7 +1963,7 @@ func Test_docsForExport(t *testing.T) {
})
// when
err = expCtx.docsForExport()
err = expCtx.docsForExport(nil)
// then
assert.Nil(t, err)
@ -1962,7 +2101,7 @@ func Test_docsForExport(t *testing.T) {
})
// when
err = expCtx.docsForExport()
err = expCtx.docsForExport(nil)
// then
assert.Nil(t, err)
assert.Equal(t, 5, len(expCtx.docs))
@ -2061,7 +2200,7 @@ func Test_docsForExport(t *testing.T) {
})
// when
err = expCtx.docsForExport()
err = expCtx.docsForExport(nil)
// then
assert.Nil(t, err)
@ -2227,7 +2366,7 @@ func Test_docsForExport(t *testing.T) {
})
// when
err = expCtx.docsForExport()
err = expCtx.docsForExport(nil)
// then
assert.Nil(t, err)
@ -2393,7 +2532,7 @@ func Test_docsForExport(t *testing.T) {
})
// when
err = expCtx.docsForExport()
err = expCtx.docsForExport(nil)
// then
assert.Nil(t, err)
@ -2486,7 +2625,7 @@ func Test_docsForExport(t *testing.T) {
})
// when
err = expCtx.docsForExport()
err = expCtx.docsForExport(nil)
// then
assert.Nil(t, err)
@ -2595,7 +2734,7 @@ func Test_docsForExport(t *testing.T) {
})
// when
err = expCtx.docsForExport()
err = expCtx.docsForExport(nil)
// then
assert.Nil(t, err)

View file

@ -85,7 +85,7 @@ func uploadFile(
CustomEncryptionKeys: encryptionKeys,
}
fileObjectId, _, err := blockService.UploadFile(ctx, spaceId, dto)
fileObjectId, _, _, err := blockService.UploadFile(ctx, spaceId, dto)
if err != nil {
return "", err
}

View file

@ -101,7 +101,7 @@ func (s *IconSyncer) handleIconImage(spaceId string, newIdsSet map[string]struct
RpcFileUploadRequest: req,
ObjectOrigin: origin,
}
fileObjectId, _, err := s.service.UploadFile(context.Background(), spaceId, dto)
fileObjectId, _, _, err := s.service.UploadFile(context.Background(), spaceId, dto)
if err != nil {
return "", anyerror.CleanupError(err)
}

View file

@ -146,7 +146,7 @@ func TestIconSyncer_Sync(t *testing.T) {
Url: "http://url.com",
},
ObjectOrigin: objectorigin.Import(model.Import_Pb),
}).Return("newFileObjectId", nil, nil)
}).Return("newFileObjectId", model.BlockContentFile_Image, nil, nil)
syncer := NewIconSyncer(fileUploader, service)
@ -191,7 +191,7 @@ func TestIconSyncer_Sync(t *testing.T) {
Url: "http://url.com",
},
ObjectOrigin: objectorigin.Import(model.Import_Pb),
}).Return("", nil, fmt.Errorf("failed to upload"))
}).Return("", model.BlockContentFile_Image, nil, fmt.Errorf("failed to upload"))
syncer := NewIconSyncer(fileUploader, service)

View file

@ -11,6 +11,8 @@ import (
mock "github.com/stretchr/testify/mock"
model "github.com/anyproto/anytype-heart/pkg/lib/pb/model"
session "github.com/anyproto/anytype-heart/core/session"
smartblock "github.com/anyproto/anytype-heart/core/block/editor/smartblock"
@ -207,7 +209,7 @@ func (_c *MockBlockService_UploadBlockFile_Call) RunAndReturn(run func(session.C
}
// UploadFile provides a mock function with given fields: ctx, spaceId, req
func (_m *MockBlockService) UploadFile(ctx context.Context, spaceId string, req block.FileUploadRequest) (string, *domain.Details, error) {
func (_m *MockBlockService) UploadFile(ctx context.Context, spaceId string, req block.FileUploadRequest) (string, model.BlockContentFileType, *domain.Details, error) {
ret := _m.Called(ctx, spaceId, req)
if len(ret) == 0 {
@ -215,9 +217,10 @@ func (_m *MockBlockService) UploadFile(ctx context.Context, spaceId string, req
}
var r0 string
var r1 *domain.Details
var r2 error
if rf, ok := ret.Get(0).(func(context.Context, string, block.FileUploadRequest) (string, *domain.Details, error)); ok {
var r1 model.BlockContentFileType
var r2 *domain.Details
var r3 error
if rf, ok := ret.Get(0).(func(context.Context, string, block.FileUploadRequest) (string, model.BlockContentFileType, *domain.Details, error)); ok {
return rf(ctx, spaceId, req)
}
if rf, ok := ret.Get(0).(func(context.Context, string, block.FileUploadRequest) string); ok {
@ -226,21 +229,27 @@ func (_m *MockBlockService) UploadFile(ctx context.Context, spaceId string, req
r0 = ret.Get(0).(string)
}
if rf, ok := ret.Get(1).(func(context.Context, string, block.FileUploadRequest) *domain.Details); ok {
if rf, ok := ret.Get(1).(func(context.Context, string, block.FileUploadRequest) model.BlockContentFileType); ok {
r1 = rf(ctx, spaceId, req)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*domain.Details)
r1 = ret.Get(1).(model.BlockContentFileType)
}
if rf, ok := ret.Get(2).(func(context.Context, string, block.FileUploadRequest) *domain.Details); ok {
r2 = rf(ctx, spaceId, req)
} else {
if ret.Get(2) != nil {
r2 = ret.Get(2).(*domain.Details)
}
}
if rf, ok := ret.Get(2).(func(context.Context, string, block.FileUploadRequest) error); ok {
r2 = rf(ctx, spaceId, req)
if rf, ok := ret.Get(3).(func(context.Context, string, block.FileUploadRequest) error); ok {
r3 = rf(ctx, spaceId, req)
} else {
r2 = ret.Error(2)
r3 = ret.Error(3)
}
return r0, r1, r2
return r0, r1, r2, r3
}
// MockBlockService_UploadFile_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UploadFile'
@ -263,12 +272,12 @@ func (_c *MockBlockService_UploadFile_Call) Run(run func(ctx context.Context, sp
return _c
}
func (_c *MockBlockService_UploadFile_Call) Return(objectId string, details *domain.Details, err error) *MockBlockService_UploadFile_Call {
_c.Call.Return(objectId, details, err)
func (_c *MockBlockService_UploadFile_Call) Return(objectId string, fileType model.BlockContentFileType, details *domain.Details, err error) *MockBlockService_UploadFile_Call {
_c.Call.Return(objectId, fileType, details, err)
return _c
}
func (_c *MockBlockService_UploadFile_Call) RunAndReturn(run func(context.Context, string, block.FileUploadRequest) (string, *domain.Details, error)) *MockBlockService_UploadFile_Call {
func (_c *MockBlockService_UploadFile_Call) RunAndReturn(run func(context.Context, string, block.FileUploadRequest) (string, model.BlockContentFileType, *domain.Details, error)) *MockBlockService_UploadFile_Call {
_c.Call.Return(run)
return _c
}

View file

@ -44,7 +44,7 @@ func (fs *FileRelationSyncer) Sync(spaceID string, fileId string, newIdsSet map[
RpcFileUploadRequest: pb.RpcFileUploadRequest{Url: fileId},
ObjectOrigin: origin,
}
fileObjectId, _, err = fs.service.UploadFile(context.Background(), spaceID, req)
fileObjectId, _, _, err = fs.service.UploadFile(context.Background(), spaceID, req)
if err != nil {
log.Errorf("file uploading %s", err)
}
@ -64,7 +64,7 @@ func (fs *FileRelationSyncer) Sync(spaceID string, fileId string, newIdsSet map[
RpcFileUploadRequest: pb.RpcFileUploadRequest{LocalPath: fileId},
ObjectOrigin: origin,
}
fileObjectId, _, err = fs.service.UploadFile(context.Background(), spaceID, req)
fileObjectId, _, _, err = fs.service.UploadFile(context.Background(), spaceID, req)
if err != nil {
log.Errorf("file uploading %s", err)
}

View file

@ -66,7 +66,7 @@ func TestFileRelationSyncer_Sync(t *testing.T) {
Url: "http://url.com",
},
ObjectOrigin: objectorigin.Import(model.Import_Pb),
}).Return("newFileObjectId", nil, nil)
}).Return("newFileObjectId", model.BlockContentFile_File, nil, nil)
syncer := NewFileRelationSyncer(fileUploader, nil)
// when
@ -84,7 +84,7 @@ func TestFileRelationSyncer_Sync(t *testing.T) {
LocalPath: "local path",
},
ObjectOrigin: objectorigin.Import(model.Import_Pb),
}).Return("newFileObjectId", nil, nil)
}).Return("newFileObjectId", model.BlockContentFile_File, nil, nil)
syncer := NewFileRelationSyncer(fileUploader, nil)
// when

View file

@ -9,12 +9,13 @@ import (
"github.com/anyproto/anytype-heart/core/domain"
"github.com/anyproto/anytype-heart/core/domain/objectorigin"
"github.com/anyproto/anytype-heart/core/session"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
)
type BlockService interface {
GetObject(ctx context.Context, objectID string) (sb smartblock.SmartBlock, err error)
GetObjectByFullID(ctx context.Context, id domain.FullID) (sb smartblock.SmartBlock, err error)
UploadFile(ctx context.Context, spaceId string, req block.FileUploadRequest) (objectId string, details *domain.Details, err error)
UploadFile(ctx context.Context, spaceId string, req block.FileUploadRequest) (objectId string, fileType model.BlockContentFileType, details *domain.Details, err error)
UploadBlockFile(ctx session.Context, req block.UploadRequest, groupID string, isSync bool) (fileObjectId string, err error)
}

View file

@ -201,7 +201,7 @@ func (c *ChildDatabase) GetBlock(importContext *api.NotionImportContext, pageID,
id := bson.NewObjectId().Hex()
if err != nil || targetBlockID == "" {
block := template.MakeDataviewContent(true, nil, nil)
block := template.MakeDataviewContent(true, nil, nil, "")
block.Dataview.TargetObjectId = targetBlockID
return &model.Block{
Id: id,

View file

@ -54,6 +54,7 @@ type Block interface {
AddRelation(relation *model.RelationLink) error
DeleteRelation(relationKey string) error
SetRelations(relationLinks []*model.RelationLink)
ListRelationLinks() []*model.RelationLink
GetSource() []string
SetSource(source []string) error
@ -345,6 +346,10 @@ func (d *Dataview) SetRelations(relationLinks []*model.RelationLink) {
d.content.RelationLinks = relationLinks
}
func (d *Dataview) ListRelationLinks() []*model.RelationLink {
return d.content.RelationLinks
}
func (td *Dataview) ModelToSave() *model.Block {
b := pbtypes.CopyBlock(td.Model())
b.Content.(*model.BlockContentOfDataview).Dataview.Relations = nil

View file

@ -162,7 +162,6 @@ func (l *Dataview) RemoveViewRelations(viewID string, relationKeys []string) err
if err != nil {
return err
}
l.syncViewRelationWithRelationLinks(view)
view.Relations = slice.Filter(view.Relations, func(f *model.BlockContentDataviewRelation) bool {
return slice.FindPos(relationKeys, f.Key) == -1
@ -175,7 +174,6 @@ func (l *Dataview) ReplaceViewRelation(viewID string, relationKey string, relati
if err != nil {
return err
}
l.syncViewRelationWithRelationLinks(view)
idx := slice.Find(view.Relations, func(f *model.BlockContentDataviewRelation) bool {
return f.Key == relationKey
@ -195,7 +193,6 @@ func (l *Dataview) ReorderViewRelations(viewID string, relationKeys []string) er
if err != nil {
return err
}
l.syncViewRelationWithRelationLinks(view)
relationsMap := make(map[string]*model.BlockContentDataviewRelation)
for _, r := range view.Relations {
@ -218,35 +215,6 @@ func (l *Dataview) ReorderViewRelations(viewID string, relationKeys []string) er
return nil
}
func (l *Dataview) syncViewRelationWithRelationLinks(view *model.BlockContentDataviewView) {
relationLinksKeys := map[string]struct{}{}
for _, relLink := range l.content.RelationLinks {
relationLinksKeys[relLink.Key] = struct{}{}
}
currentViewKeys := map[string]struct{}{}
newViewRelations := view.Relations[:0]
for _, rel := range view.Relations {
// Don't add relations that are not in relation links
if _, ok := relationLinksKeys[rel.Key]; ok {
newViewRelations = append(newViewRelations, rel)
currentViewKeys[rel.Key] = struct{}{}
}
}
for _, relLink := range l.content.RelationLinks {
_, ok := currentViewKeys[relLink.Key]
if !ok {
newViewRelations = append(newViewRelations, &model.BlockContentDataviewRelation{
Key: relLink.Key,
Width: DefaultViewRelationWidth,
IsVisible: false,
})
}
}
view.Relations = newViewRelations
}
func (l *Dataview) setRelationFormat(filter *model.BlockContentDataviewFilter) {
for _, relLink := range l.content.RelationLinks {
if relLink.Key == filter.RelationKey {

View file

@ -29,14 +29,11 @@ func makeDataviewForViewRelationsTest(relationLinks []*model.RelationLink, relat
}
func TestReorderViewRelations(t *testing.T) {
t.Run("reorder: add missing relation from relation links", func(t *testing.T) {
dv := makeDataviewForViewRelationsTest(
[]*model.RelationLink{
{Key: bundle.RelationKeyName.String(), Format: model.RelationFormat_longtext},
{Key: bundle.RelationKeyCreator.String(), Format: model.RelationFormat_object},
{Key: bundle.RelationKeyCreatedDate.String(), Format: model.RelationFormat_date},
},
t.Run("reorder: add missing relations from view relations", func(t *testing.T) {
dv := makeDataviewForViewRelationsTest(nil,
[]*model.BlockContentDataviewRelation{
{Key: bundle.RelationKeyCreatedDate.String(), IsVisible: false, Width: DefaultViewRelationWidth},
{Key: bundle.RelationKeyCreator.String(), IsVisible: false, Width: DefaultViewRelationWidth},
{Key: bundle.RelationKeyName.String(), IsVisible: true, Width: DefaultViewRelationWidth},
},
)
@ -44,12 +41,7 @@ func TestReorderViewRelations(t *testing.T) {
err := dv.ReorderViewRelations(testViewId, []string{bundle.RelationKeyCreator.String(), bundle.RelationKeyName.String()})
require.NoError(t, err)
want := makeDataviewForViewRelationsTest(
[]*model.RelationLink{
{Key: bundle.RelationKeyName.String(), Format: model.RelationFormat_longtext},
{Key: bundle.RelationKeyCreator.String(), Format: model.RelationFormat_object},
{Key: bundle.RelationKeyCreatedDate.String(), Format: model.RelationFormat_date},
},
want := makeDataviewForViewRelationsTest(nil,
[]*model.BlockContentDataviewRelation{
{Key: bundle.RelationKeyCreator.String(), IsVisible: false, Width: DefaultViewRelationWidth},
{Key: bundle.RelationKeyName.String(), IsVisible: true, Width: DefaultViewRelationWidth},
@ -60,12 +52,8 @@ func TestReorderViewRelations(t *testing.T) {
assert.Equal(t, want, dv)
})
t.Run("reorder: remove extra relation that don't exist in relation links", func(t *testing.T) {
dv := makeDataviewForViewRelationsTest(
[]*model.RelationLink{
{Key: bundle.RelationKeyName.String(), Format: model.RelationFormat_longtext},
{Key: bundle.RelationKeyCreatedDate.String(), Format: model.RelationFormat_date},
},
t.Run("reorder: remove extra relation that don't exist in view relations", func(t *testing.T) {
dv := makeDataviewForViewRelationsTest(nil,
[]*model.BlockContentDataviewRelation{
{Key: bundle.RelationKeyCreator.String(), IsVisible: false, Width: DefaultViewRelationWidth},
{Key: bundle.RelationKeyName.String(), IsVisible: true, Width: DefaultViewRelationWidth},
@ -76,13 +64,10 @@ func TestReorderViewRelations(t *testing.T) {
err := dv.ReorderViewRelations(testViewId, []string{bundle.RelationKeyName.String(), bundle.RelationKeyCreator.String(), bundle.RelationKeyDescription.String()})
require.NoError(t, err)
want := makeDataviewForViewRelationsTest(
[]*model.RelationLink{
{Key: bundle.RelationKeyName.String(), Format: model.RelationFormat_longtext},
{Key: bundle.RelationKeyCreatedDate.String(), Format: model.RelationFormat_date},
},
want := makeDataviewForViewRelationsTest(nil,
[]*model.BlockContentDataviewRelation{
{Key: bundle.RelationKeyName.String(), IsVisible: true, Width: DefaultViewRelationWidth},
{Key: bundle.RelationKeyCreator.String(), IsVisible: false, Width: DefaultViewRelationWidth},
{Key: bundle.RelationKeyCreatedDate.String(), IsVisible: false, Width: DefaultViewRelationWidth},
},
)
@ -93,12 +78,7 @@ func TestReorderViewRelations(t *testing.T) {
func TestReplaceViewRelation(t *testing.T) {
t.Run("add new relation", func(t *testing.T) {
dv := makeDataviewForViewRelationsTest(
[]*model.RelationLink{
{Key: bundle.RelationKeyName.String(), Format: model.RelationFormat_longtext},
},
[]*model.BlockContentDataviewRelation{},
)
dv := makeDataviewForViewRelationsTest(nil, []*model.BlockContentDataviewRelation{})
err := dv.ReplaceViewRelation(testViewId, bundle.RelationKeyDescription.String(), &model.BlockContentDataviewRelation{
Key: bundle.RelationKeyDescription.String(),
@ -107,17 +87,8 @@ func TestReplaceViewRelation(t *testing.T) {
})
require.NoError(t, err)
want := makeDataviewForViewRelationsTest(
[]*model.RelationLink{
{Key: bundle.RelationKeyName.String(), Format: model.RelationFormat_longtext},
},
want := makeDataviewForViewRelationsTest(nil,
[]*model.BlockContentDataviewRelation{
// Added automatically from relation links
{
Key: bundle.RelationKeyName.String(),
Width: DefaultViewRelationWidth,
IsVisible: false,
},
{
Key: bundle.RelationKeyDescription.String(),
Width: DefaultViewRelationWidth,

View file

@ -1,14 +1,20 @@
package block
import (
"context"
"fmt"
"github.com/anyproto/anytype-heart/core/block/cache"
"github.com/anyproto/anytype-heart/core/block/editor"
"github.com/anyproto/anytype-heart/core/block/editor/basic"
"github.com/anyproto/anytype-heart/core/block/editor/smartblock"
"github.com/anyproto/anytype-heart/core/block/editor/state"
"github.com/anyproto/anytype-heart/core/block/editor/widget"
"github.com/anyproto/anytype-heart/core/block/simple"
"github.com/anyproto/anytype-heart/core/domain"
"github.com/anyproto/anytype-heart/core/session"
"github.com/anyproto/anytype-heart/pb"
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
)
@ -63,3 +69,68 @@ func (s *Service) SetWidgetBlockViewId(ctx session.Context, req *pb.RpcBlockWidg
}, req.BlockId)
})
}
func (s *Service) CreateTypeWidgetIfMissing(ctx context.Context, spaceId string, key domain.TypeKey) error {
space, err := s.spaceService.Get(ctx, spaceId)
if err != nil {
return err
}
widgetObjectId := space.DerivedIDs().Widgets
typeId, err := space.GetTypeIdByKey(ctx, key)
if err != nil {
return err
}
widgetDetails, err := s.objectStore.SpaceIndex(spaceId).GetDetails(widgetObjectId)
if err == nil {
keys := widgetDetails.Get(bundle.RelationKeyAutoWidgetTargets).StringList()
for _, k := range keys {
if k == typeId {
return nil
}
}
}
widgetBlockId := key.String()
return cache.DoState(s, widgetObjectId, func(st *state.State, w widget.Widget) (err error) {
targets := st.Details().Get(bundle.RelationKeyAutoWidgetTargets).StringList()
targets = append(targets, typeId)
st.SetDetail(bundle.RelationKeyAutoWidgetTargets, domain.StringList(targets))
var typeBlockAlreadyExists bool
err = st.Iterate(func(b simple.Block) (isContinue bool) {
link := b.Model().GetLink()
if link == nil {
return true
}
if link.TargetBlockId == typeId {
// check by targetBlockId in case user created the same block manually
typeBlockAlreadyExists = true
return false
}
return true
})
if err != nil {
return err
}
if typeBlockAlreadyExists {
return nil
}
_, err = w.CreateBlock(st, &pb.RpcBlockCreateWidgetRequest{
ContextId: widgetObjectId,
ObjectLimit: 6,
WidgetLayout: model.BlockContentWidget_View,
Position: model.Block_Bottom,
ViewId: editor.ObjectTypeAllViewId,
Block: &model.Block{
Id: widgetBlockId, // hardcode id to avoid duplicates
Content: &model.BlockContentOfLink{Link: &model.BlockContentLink{
TargetBlockId: typeId,
}},
},
})
return err
})
}

View file

@ -40,6 +40,19 @@ func (mw *Middleware) ObjectCreate(cctx context.Context, req *pb.RpcObjectCreate
if req.WithChat {
return response(pb.RpcObjectCreateResponseError_UNKNOWN_ERROR, "", nil, fmt.Errorf("WithChat is not implemented"))
}
if req.CreateTypeWidgetIfMissing {
err := mw.doBlockService(func(bs *block.Service) (err error) {
typeKey, err := domain.GetTypeKeyFromRawUniqueKey(req.ObjectTypeUniqueKey)
if err != nil {
return err
}
err = bs.CreateTypeWidgetIfMissing(cctx, req.SpaceId, typeKey)
return err
})
if err != nil {
return response(pb.RpcObjectCreateResponseError_UNKNOWN_ERROR, "", nil, err)
}
}
return response(pb.RpcObjectCreateResponseError_NULL, id, newDetails.ToProto(), nil)
}

View file

@ -75,7 +75,7 @@ func (mw *Middleware) UnsplashDownload(cctx context.Context, req *pb.RpcUnsplash
defer os.Remove(imagePath)
err = mw.doBlockService(func(bs *block.Service) (err error) {
objectId, _, err = bs.UploadFile(cctx, req.SpaceId, block.FileUploadRequest{
objectId, _, _, err = bs.UploadFile(cctx, req.SpaceId, block.FileUploadRequest{
RpcFileUploadRequest: pb.RpcFileUploadRequest{
LocalPath: imagePath,
Type: model.BlockContentFile_Image,

View file

@ -13,6 +13,8 @@ import (
"github.com/anyproto/anytype-heart/core/files/fileoffloader"
"github.com/anyproto/anytype-heart/core/files/reconciler"
"github.com/anyproto/anytype-heart/pb"
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
)
func (mw *Middleware) FileDownload(cctx context.Context, req *pb.RpcFileDownloadRequest) *pb.RpcFileDownloadResponse {
@ -130,12 +132,40 @@ func (mw *Middleware) FileUpload(cctx context.Context, req *pb.RpcFileUploadRequ
var (
objectId string
details *domain.Details
fileType model.BlockContentFileType
)
err := mw.doBlockService(func(bs *block.Service) (err error) {
dto := block.FileUploadRequest{RpcFileUploadRequest: *req, ObjectOrigin: objectorigin.ObjectOrigin{Origin: req.Origin}}
objectId, details, err = bs.UploadFile(cctx, req.SpaceId, dto)
objectId, fileType, details, err = bs.UploadFile(cctx, req.SpaceId, dto)
return
})
if req.CreateTypeWidgetIfMissing {
var typeKey domain.TypeKey
switch fileType {
case model.BlockContentFile_Audio:
typeKey = bundle.TypeKeyAudio
case model.BlockContentFile_Image:
typeKey = bundle.TypeKeyImage
case model.BlockContentFile_Video:
typeKey = bundle.TypeKeyVideo
case model.BlockContentFile_PDF, model.BlockContentFile_File:
typeKey = bundle.TypeKeyFile
default:
}
if typeKey != "" {
// do not create widget if type is not detected. Shouldn't happen, but just in case
err := mw.doBlockService(func(bs *block.Service) (err error) {
err = bs.CreateTypeWidgetIfMissing(cctx, req.SpaceId, typeKey)
return err
})
if err != nil {
return response(objectId, nil, pb.RpcFileUploadResponseError_UNKNOWN_ERROR, err)
}
}
}
if err != nil {
return response("", nil, pb.RpcFileUploadResponseError_UNKNOWN_ERROR, err)
}

View file

@ -13,6 +13,7 @@ var allObjectsRelationsWhiteList = []string{
bundle.RelationKeySpaceId.String(),
bundle.RelationKeyId.String(),
bundle.RelationKeyLayout.String(),
bundle.RelationKeyResolvedLayout.String(),
bundle.RelationKeyIsArchived.String(),
bundle.RelationKeyIsDeleted.String(),
bundle.RelationKeyName.String(),
@ -39,6 +40,15 @@ var derivedObjectsWhiteList = append(slices.Clone(allObjectsRelationsWhiteList),
var relationsWhiteList = append(slices.Clone(derivedObjectsWhiteList), bundle.RelationKeyRelationFormat.String())
var relationOptionWhiteList = append(slices.Clone(derivedObjectsWhiteList), bundle.RelationKeyRelationOptionColor.String())
var objectTypeWhiteList = append(slices.Clone(derivedObjectsWhiteList),
bundle.RelationKeyRecommendedRelations.String(),
bundle.RelationKeyRecommendedFeaturedRelations.String(),
bundle.RelationKeyRecommendedLayout.String(),
bundle.RelationKeyLayoutWidth.String(),
bundle.RelationKeyLayoutAlign.String(),
bundle.RelationKeyIconName.String(),
bundle.RelationKeyIconOption.String(),
)
var fileRelationsWhiteList = append(
slices.Clone(documentRelationsWhiteList),
@ -54,20 +64,22 @@ var imageRelationsWhiteList = append(slices.Clone(fileRelationsWhiteList),
bundle.RelationKeyMediaArtistURL.String(),
)
var spacedWhiteList = append(slices.Clone(documentRelationsWhiteList), bundle.RelationKeyIconImage.String(), bundle.RelationKeyIconOption.String())
var publishingRelationsWhiteList = map[model.ObjectTypeLayout][]string{
model.ObjectType_basic: documentRelationsWhiteList,
model.ObjectType_profile: documentRelationsWhiteList,
model.ObjectType_todo: todoRelationsWhiteList,
model.ObjectType_set: documentRelationsWhiteList,
model.ObjectType_collection: documentRelationsWhiteList,
model.ObjectType_objectType: derivedObjectsWhiteList,
model.ObjectType_objectType: objectTypeWhiteList,
model.ObjectType_relation: relationsWhiteList,
model.ObjectType_file: fileRelationsWhiteList,
model.ObjectType_pdf: fileRelationsWhiteList,
model.ObjectType_dashboard: allObjectsRelationsWhiteList,
model.ObjectType_image: imageRelationsWhiteList,
model.ObjectType_note: documentRelationsWhiteList,
model.ObjectType_space: allObjectsRelationsWhiteList,
model.ObjectType_space: spacedWhiteList,
model.ObjectType_bookmark: bookmarkRelationsWhiteList,
model.ObjectType_relationOption: relationOptionWhiteList,

View file

@ -137,6 +137,7 @@ func (s *service) exportToDir(ctx context.Context, spaceId, pageId string) (dirE
NoProgress: true,
IncludeNested: true,
IncludeBacklinks: true,
IncludeSpace: true,
LinksStateFilters: &pb.RpcObjectListExportStateFilters{
RelationsWhiteList: relationsWhiteListToPbModel(),
RemoveBlocks: true,
@ -406,8 +407,6 @@ func (s *service) getPublishLimit(globalName string) (int64, error) {
}
func (s *service) Publish(ctx context.Context, spaceId, pageId, uri string, joinSpace bool) (res PublishResult, err error) {
log.Info("Publish called", zap.String("pageId", pageId))
identity, _, details := s.identityService.GetMyProfileDetails(ctx)
globalName := details.GetString(bundle.RelationKeyGlobalName)

View file

@ -55,10 +55,11 @@ import (
)
const (
spaceId = "spaceId"
objectId = "objectId"
id = "identity"
objectName = "test"
spaceId = "spaceId"
objectId = "objectId"
id = "identity"
objectName = "test"
workspaceId = "workspaceId"
)
type mockPublishClient struct {
@ -677,6 +678,7 @@ func prepareSpaceService(t *testing.T, isPersonal bool) (*mock_space.MockService
st.EXPECT().TreeStorage(mock.Anything, mock.Anything).Return(mockSt, nil)
mockSt.EXPECT().Heads(gomock.Any()).Return([]string{"heads"}, nil)
space.EXPECT().Storage().Return(st)
space.EXPECT().DerivedIDs().Return(threads.DerivedSmartblockIds{Workspace: workspaceId})
spaceService.EXPECT().Get(context.Background(), spaceId).Return(space, nil)
return spaceService, nil
}
@ -699,6 +701,12 @@ func prepareExporter(t *testing.T, objectTypeId string, spaceService *mock_space
bundle.RelationKeyRecommendedRelations: domain.StringList([]string{addr.MissingObject}),
bundle.RelationKeySpaceId: domain.String(spaceId),
},
{
bundle.RelationKeyId: domain.String(workspaceId),
bundle.RelationKeyUniqueKey: domain.String(objectTypeUniqueKey.Marshal()),
bundle.RelationKeyLayout: domain.Int64(int64(model.ObjectType_space)),
bundle.RelationKeySpaceId: domain.String(spaceId),
},
})
objectGetter := mock_cache.NewMockObjectGetterComponent(t)
@ -731,8 +739,24 @@ func prepareExporter(t *testing.T, objectTypeId string, spaceService *mock_space
})
objectType.Doc = objectTypeDoc
objectType.SetType(smartblock.SmartBlockTypeObjectType)
workspaceTest := smarttest.New(workspaceId)
workspaceDoc := workspaceTest.NewState().SetDetails(domain.NewDetailsFromMap(map[domain.RelationKey]domain.Value{
bundle.RelationKeyId: domain.String(workspaceId),
bundle.RelationKeyType: domain.String(objectTypeId),
}))
workspaceDoc.AddRelationLinks(&model.RelationLink{
Key: bundle.RelationKeyId.String(),
Format: model.RelationFormat_longtext,
}, &model.RelationLink{
Key: bundle.RelationKeyType.String(),
Format: model.RelationFormat_longtext,
})
workspaceTest.Doc = workspaceDoc
objectGetter.EXPECT().GetObject(context.Background(), objectId).Return(smartBlockTest, nil)
objectGetter.EXPECT().GetObject(context.Background(), objectTypeId).Return(objectType, nil)
objectGetter.EXPECT().GetObject(context.Background(), workspaceId).Return(workspaceTest, nil)
a := &app.App{}
mockSender := mock_event.NewMockSender(t)
@ -776,6 +800,12 @@ func prepareExporterWithFile(t *testing.T, objectTypeId string, spaceService *mo
bundle.RelationKeyRecommendedRelations: domain.StringList([]string{addr.MissingObject}),
bundle.RelationKeySpaceId: domain.String(spaceId),
},
{
bundle.RelationKeyId: domain.String(workspaceId),
bundle.RelationKeyUniqueKey: domain.String(objectTypeUniqueKey.Marshal()),
bundle.RelationKeyLayout: domain.Int64(int64(model.ObjectType_space)),
bundle.RelationKeySpaceId: domain.String(spaceId),
},
})
objectGetter := mock_cache.NewMockObjectGetterComponent(t)
@ -811,6 +841,20 @@ func prepareExporterWithFile(t *testing.T, objectTypeId string, spaceService *mo
objectType.Doc = objectTypeDoc
objectType.SetType(smartblock.SmartBlockTypeObjectType)
workspaceTest := smarttest.New(workspaceId)
workspaceDoc := workspaceTest.NewState().SetDetails(domain.NewDetailsFromMap(map[domain.RelationKey]domain.Value{
bundle.RelationKeyId: domain.String(workspaceId),
bundle.RelationKeyType: domain.String(objectTypeId),
}))
workspaceDoc.AddRelationLinks(&model.RelationLink{
Key: bundle.RelationKeyId.String(),
Format: model.RelationFormat_longtext,
}, &model.RelationLink{
Key: bundle.RelationKeyType.String(),
Format: model.RelationFormat_longtext,
})
workspaceTest.Doc = workspaceDoc
file := smarttest.New(fileId)
fileDoc := file.NewState().SetDetails(domain.NewDetailsFromMap(map[domain.RelationKey]domain.Value{
bundle.RelationKeyId: domain.String(fileId),
@ -829,12 +873,14 @@ func prepareExporterWithFile(t *testing.T, objectTypeId string, spaceService *mo
file.SetSpaceId(spaceId)
space := mock_clientspace.NewMockSpace(t)
space.EXPECT().Id().Return(spaceId)
space.EXPECT().DerivedIDs().Return(threads.DerivedSmartblockIds{})
space.EXPECT().DerivedIDs().Return(threads.DerivedSmartblockIds{Workspace: workspaceId})
file.SetSpace(space)
spaceService.EXPECT().Get(context.Background(), spaceId).Return(space, nil)
objectGetter.EXPECT().GetObject(context.Background(), objectId).Return(smartBlockTest, nil).Times(4)
objectGetter.EXPECT().GetObject(context.Background(), objectTypeId).Return(objectType, nil).Times(2)
objectGetter.EXPECT().GetObject(context.Background(), fileId).Return(file, nil)
objectGetter.EXPECT().GetObject(context.Background(), workspaceId).Return(workspaceTest, nil)
fileService := mock_files.NewMockService(t)
fileObject := mock_files.NewMockFile(t)

View file

@ -20,6 +20,9 @@ func (mw *Middleware) BlockCreateWidget(cctx context.Context, req *pb.RpcBlockCr
}
var id string
err := mw.doBlockService(func(bs *block.Service) (err error) {
if req.Block != nil {
req.Block.Id = ""
}
id, err = bs.CreateWidgetBlock(ctx, req)
return err
})

View file

@ -2017,6 +2017,7 @@
- [SpaceAccessType](#anytype-model-SpaceAccessType)
- [SpaceShareableStatus](#anytype-model-SpaceShareableStatus)
- [SpaceStatus](#anytype-model-SpaceStatus)
- [SpaceUxType](#anytype-model-SpaceUxType)
- [Scalar Value Types](#scalar-value-types)
@ -12364,6 +12365,7 @@ Get marks list in the selected range in text block.
| details | [google.protobuf.Struct](#google-protobuf-Struct) | | additional details for file object |
| origin | [model.ObjectOrigin](#anytype-model-ObjectOrigin) | | |
| imageKind | [model.ImageKind](#anytype-model-ImageKind) | | |
| createTypeWidgetIfMissing | [bool](#bool) | | experimental flag to auto-create type widget if missing |
@ -14450,6 +14452,7 @@ Get the info for page alongside with info for all inbound and outbound links fro
| spaceId | [string](#string) | | |
| objectTypeUniqueKey | [string](#string) | | |
| withChat | [bool](#bool) | | |
| createTypeWidgetIfMissing | [bool](#bool) | | experimental flag to auto-create type widget if missing |
@ -15858,6 +15861,7 @@ Deletes the object, keys from the local store and unsubscribe from remote change
| noProgress | [bool](#bool) | | for integrations like raycast and web publishing |
| linksStateFilters | [Rpc.Object.ListExport.StateFilters](#anytype-Rpc-Object-ListExport-StateFilters) | | |
| includeBacklinks | [bool](#bool) | | |
| includeSpace | [bool](#bool) | | |
@ -29414,14 +29418,14 @@ Bookmark is to keep a web-link and to preview a content.
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| source | [string](#string) | repeated | |
| source | [string](#string) | repeated | can be set for detached(without TargetObjectId) inline sets |
| views | [Block.Content.Dataview.View](#anytype-model-Block-Content-Dataview-View) | repeated | |
| activeView | [string](#string) | | do not generate changes for this field |
| relations | [Relation](#anytype-model-Relation) | repeated | deprecated |
| groupOrders | [Block.Content.Dataview.GroupOrder](#anytype-model-Block-Content-Dataview-GroupOrder) | repeated | |
| objectOrders | [Block.Content.Dataview.ObjectOrder](#anytype-model-Block-Content-Dataview-ObjectOrder) | repeated | |
| relationLinks | [RelationLink](#anytype-model-RelationLink) | repeated | |
| TargetObjectId | [string](#string) | | |
| TargetObjectId | [string](#string) | | empty for original set/collection objects and for detached inline sets |
| isCollection | [bool](#bool) | | |
@ -31988,6 +31992,19 @@ RelationFormat describes how the underlying data is stored in the google.protobu
| SpaceRemoving | 10 | SpaceRemoving - the account is removing from space or the space is removed from network |
<a name="anytype-model-SpaceUxType"></a>
### SpaceUxType
| Name | Number | Description |
| ---- | ------ | ----------- |
| Chat | 0 | chat-first UX |
| Data | 1 | objects-first UX |
| Stream | 2 | stream UX (chat with limited amount of owners) |

16
go.mod
View file

@ -7,8 +7,8 @@ require (
github.com/PuerkitoBio/goquery v1.10.2
github.com/VividCortex/ewma v1.2.0
github.com/adrium/goheif v0.0.0-20230113233934-ca402e77a786
github.com/anyproto/any-store v0.1.8
github.com/anyproto/any-sync v0.6.1
github.com/anyproto/any-store v0.1.10
github.com/anyproto/any-sync v0.6.4
github.com/anyproto/anytype-publish-server/publishclient v0.0.0-20250131145601-de288583ff2a
github.com/anyproto/go-chash v0.1.0
github.com/anyproto/go-naturaldate/v2 v2.0.2-0.20230524105841-9829cfd13438
@ -39,7 +39,7 @@ require (
github.com/gogo/status v1.1.1
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb
github.com/golang/snappy v1.0.0
github.com/google/uuid v1.6.0
github.com/gosimple/slug v1.15.0
github.com/grokify/html-strip-tags-go v0.1.0
@ -47,6 +47,7 @@ require (
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645
github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/golang-lru/v2 v2.0.7
github.com/hashicorp/yamux v0.1.2
github.com/hbagdi/go-unsplash v0.0.0-20230414214043-474fc02c9119
github.com/huandu/skiplist v1.2.1
@ -193,7 +194,6 @@ require (
github.com/gorilla/css v1.0.1 // indirect
github.com/gosimple/unidecode v1.0.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/holiman/uint256 v1.2.4 // indirect
github.com/huandu/xstrings v1.4.0 // indirect
@ -280,11 +280,11 @@ require (
go.opentelemetry.io/otel/trace v1.34.0 // indirect
golang.org/x/arch v0.8.0 // indirect
golang.org/x/crypto v0.36.0 // indirect
golang.org/x/mod v0.23.0 // indirect
golang.org/x/mod v0.24.0 // indirect
golang.org/x/sync v0.12.0 // indirect
golang.org/x/term v0.30.0 // indirect
golang.org/x/time v0.10.0 // indirect
golang.org/x/tools v0.30.0 // indirect
golang.org/x/time v0.11.0 // indirect
golang.org/x/tools v0.31.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
google.golang.org/protobuf v1.36.5 // indirect
@ -321,5 +321,3 @@ replace google.golang.org/genproto/googleapis/rpc => google.golang.org/genproto/
replace github.com/btcsuite/btcutil => github.com/btcsuite/btcd/btcutil v1.1.5
replace github.com/dsoprea/go-jpeg-image-structure/v2 => github.com/dchesterton/go-jpeg-image-structure/v2 v2.0.0-20240318203529-c3eea088bd38
replace zombiezen.com/go/sqlite => github.com/anyproto/go-sqlite v0.0.0-20250226111550-9b81a8e3cff4

31
go.sum
View file

@ -82,12 +82,10 @@ github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5/go.mod h
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
github.com/anyproto/any-store v0.1.8 h1:/bxUVq6sBTwYkmPL2g1xUAWNb3axF+zPhP2dvdEBH68=
github.com/anyproto/any-store v0.1.8/go.mod h1:GpnVhcGm5aUQtOwCnKeTt4jsWgVXZ773WbQVLFdeCFo=
github.com/anyproto/any-store v0.1.7 h1:E3DntI+JXo3h7v0WTUJWH+nm7G4MV0PNOXZ6SFzQ2OU=
github.com/anyproto/any-store v0.1.7/go.mod h1:nbyRoJYOlvSWU1xDOrmgPP96UeoTf4eYZ9k+qqLK9k8=
github.com/anyproto/any-sync v0.6.1 h1:Dasbp7qGQme8diGGpzaDQfSDs5o7PAK3E5rxHHrB/+4=
github.com/anyproto/any-sync v0.6.1/go.mod h1:5js8TNBdqe75zwlr9XEQSVDtwhsvEU2qLeC2wTnT/Fo=
github.com/anyproto/any-store v0.1.10 h1:X3vjmoFMrgXbF4jdqD3AoiwPa9ODcojeu99tW+Qusz0=
github.com/anyproto/any-store v0.1.10/go.mod h1:GpnVhcGm5aUQtOwCnKeTt4jsWgVXZ773WbQVLFdeCFo=
github.com/anyproto/any-sync v0.6.4 h1:lZwkjDEeCj2KwWeZz6ftg2SZS//V+sxnwiKCIjMzvJs=
github.com/anyproto/any-sync v0.6.4/go.mod h1:mjYy/w4k56xxpSfnVLCNnbyvLIVDpm5XG8Dw8mREz0Q=
github.com/anyproto/anytype-publish-server/publishclient v0.0.0-20250131145601-de288583ff2a h1:ZZM+0OUCQMWSLSflpkf0ZMVo3V76qEDDIXPpQOClNs0=
github.com/anyproto/anytype-publish-server/publishclient v0.0.0-20250131145601-de288583ff2a/go.mod h1:4fkueCZcGniSMXkrwESO8zzERrh/L7WHimRNWecfGM0=
github.com/anyproto/badger/v4 v4.2.1-0.20240110160636-80743fa3d580 h1:Ba80IlCCxkZ9H1GF+7vFu/TSpPvbpDCxXJ5ogc4euYc=
@ -106,8 +104,6 @@ github.com/anyproto/go-slip10 v1.0.0 h1:uAEtSuudR3jJBOfkOXf3bErxVoxbuKwdoJN55M1i
github.com/anyproto/go-slip10 v1.0.0/go.mod h1:BCmIlM1KB8wX6K4/8pOvxPl9oVKfEvZ5vsmO5rkK6vg=
github.com/anyproto/go-slip21 v1.0.0 h1:CI7lUqTIwmPOEGVAj4jyNLoICvueh++0U2HoAi3m2ZY=
github.com/anyproto/go-slip21 v1.0.0/go.mod h1:gbIJt7HAdr5DuT4f2pFTKCBSUWYsm/fysHBNqgsuxT0=
github.com/anyproto/go-sqlite v0.0.0-20250226111550-9b81a8e3cff4 h1:HzVjm45VOUVFUrxh2s0cRR4lqfCr/VAee6wNzPLcApI=
github.com/anyproto/go-sqlite v0.0.0-20250226111550-9b81a8e3cff4/go.mod h1:0w9F1DN9IZj9AcLS9YDKMboubCACkwYCGkzoy3eG5ik=
github.com/anyproto/html-to-markdown v0.0.0-20231025221133-830bf0a6f139 h1:Wp9z0Q2kAstznWUmTZyOb9UgpVmUgYt1LXRvK/cg10E=
github.com/anyproto/html-to-markdown v0.0.0-20231025221133-830bf0a6f139/go.mod h1:1zaDDQVWTRwNksmTUTkcVXqgNF28YHiEUIm8FL9Z+II=
github.com/anyproto/lexid v0.0.4 h1:2ztI0y5pNdtojd3vChw/YV/P6IO9pB7PccYysImDxWI=
@ -450,8 +446,8 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk=
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw=
@ -545,7 +541,6 @@ github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
@ -1244,8 +1239,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -1460,8 +1455,8 @@ golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -1524,8 +1519,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -1722,3 +1717,5 @@ sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
storj.io/drpc v0.0.34 h1:q9zlQKfJ5A7x8NQNFk8x7eKUF78FMhmAbZLnFK+og7I=
storj.io/drpc v0.0.34/go.mod h1:Y9LZaa8esL1PW2IDMqJE7CFSNq7d5bQ3RI7mGPtmKMg=
zombiezen.com/go/sqlite v1.4.0 h1:N1s3RIljwtp4541Y8rM880qgGIgq3fTD2yks1xftnKU=
zombiezen.com/go/sqlite v1.4.0/go.mod h1:0w9F1DN9IZj9AcLS9YDKMboubCACkwYCGkzoy3eG5ik=

File diff suppressed because it is too large Load diff

View file

@ -1664,6 +1664,7 @@ message Rpc {
string spaceId = 4;
string objectTypeUniqueKey = 5;
bool withChat = 6;
bool createTypeWidgetIfMissing = 7; // experimental flag to auto-create type widget if missing
}
message Response {
@ -2841,6 +2842,7 @@ message Rpc {
bool noProgress = 11;
StateFilters linksStateFilters = 12;
bool includeBacklinks = 13;
bool includeSpace = 14;
}
message StateFilters {
repeated RelationsWhiteList relationsWhiteList = 1;
@ -3716,6 +3718,7 @@ message Rpc {
google.protobuf.Struct details = 7; // additional details for file object
anytype.model.ObjectOrigin origin = 8;
anytype.model.ImageKind imageKind = 9;
bool createTypeWidgetIfMissing = 10; // experimental flag to auto-create type widget if missing
}
message Response {

View file

@ -9,7 +9,7 @@ import (
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
)
const RelationChecksum = "374aa57652dfcc9708b3249a57abb31a0dd702a441552940f77f7434effb7e4c"
const RelationChecksum = "5409ec2592a6b2b4497cca69d4528c33828c8dae60f7cc5d2acb049b16270f34"
const (
RelationKeyTag domain.RelationKey = "tag"
RelationKeyCamera domain.RelationKey = "camera"
@ -107,6 +107,7 @@ const (
RelationKeyIconOption domain.RelationKey = "iconOption"
RelationKeySpaceAccessibility domain.RelationKey = "spaceAccessibility"
RelationKeySpaceAccessType domain.RelationKey = "spaceAccessType"
RelationKeySpaceUxType domain.RelationKey = "spaceUxType"
RelationKeySourceFilePath domain.RelationKey = "sourceFilePath"
RelationKeyFileSyncStatus domain.RelationKey = "fileSyncStatus"
RelationKeyFileBackupStatus domain.RelationKey = "fileBackupStatus"
@ -153,6 +154,7 @@ const (
RelationKeyRecommendedFileRelations domain.RelationKey = "recommendedFileRelations"
RelationKeyDefaultViewType domain.RelationKey = "defaultViewType"
RelationKeyDefaultTypeId domain.RelationKey = "defaultTypeId"
RelationKeyAutoWidgetTargets domain.RelationKey = "autoWidgetTargets"
)
var (
@ -276,6 +278,19 @@ var (
Revision: 1,
Scope: model.Relation_type,
},
RelationKeyAutoWidgetTargets: {
DataSource: model.Relation_details,
Description: "Automatically generated widget. Used to avoid creating widget if was removed by user",
Format: model.RelationFormat_object,
Hidden: true,
Id: "_brautoWidgetTargets",
Key: "autoWidgetTargets",
Name: "Auto Widget targets",
ReadOnly: false,
ReadOnlyRelation: true,
Scope: model.Relation_type,
},
RelationKeyBacklinks: {
DataSource: model.Relation_local,
@ -1846,6 +1861,20 @@ var (
ReadOnlyRelation: true,
Scope: model.Relation_type,
},
RelationKeySpaceUxType: {
DataSource: model.Relation_details,
Description: "Space UX type, see enum model.SpaceUxType",
Format: model.RelationFormat_number,
Hidden: true,
Id: "_brspaceUxType",
Key: "spaceUxType",
MaxCount: 1,
Name: "Space UX type",
ReadOnly: false,
ReadOnlyRelation: true,
Scope: model.Relation_type,
},
RelationKeyStarred: {
DataSource: model.Relation_details,

View file

@ -998,6 +998,16 @@
"readonly": false,
"source": "derived"
},
{
"description": "Space UX type, see enum model.SpaceUxType",
"format": "number",
"hidden": true,
"key": "spaceUxType",
"maxCount": 1,
"name": "Space UX type",
"readonly": false,
"source": "details"
},
{
"description": "File path or url with original object",
"format": "longtext",
@ -1468,5 +1478,15 @@
"name": "Default type id",
"readonly": false,
"source": "details"
},
{
"description": "Automatically generated widget. Used to avoid creating widget if was removed by user",
"format": "object",
"hidden": true,
"key": "autoWidgetTargets",
"maxCount": 0,
"name": "Auto Widget targets",
"readonly": false,
"source": "details"
}
]

View file

@ -202,6 +202,7 @@ func (s *dsObjectStore) setDefaultConfig() {
}
s.anyStoreConfig.SQLiteConnectionOptions = maps.Clone(s.anyStoreConfig.SQLiteConnectionOptions)
s.anyStoreConfig.SQLiteConnectionOptions["synchronous"] = "off"
s.anyStoreConfig.SQLiteGlobalPageCachePreallocateSizeBytes = 1 << 26
}
func ensureDirExists(dir string) error {

View file

@ -396,6 +396,34 @@ func (SpaceAccessType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_98a910b73321e591, []int{6}
}
type SpaceUxType int32
const (
SpaceUxType_Chat SpaceUxType = 0
SpaceUxType_Data SpaceUxType = 1
SpaceUxType_Stream SpaceUxType = 2
)
var SpaceUxType_name = map[int32]string{
0: "Chat",
1: "Data",
2: "Stream",
}
var SpaceUxType_value = map[string]int32{
"Chat": 0,
"Data": 1,
"Stream": 2,
}
func (x SpaceUxType) String() string {
return proto.EnumName(SpaceUxType_name, int32(x))
}
func (SpaceUxType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_98a910b73321e591, []int{7}
}
type ImageKind int32
const (
@ -424,7 +452,7 @@ func (x ImageKind) String() string {
}
func (ImageKind) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_98a910b73321e591, []int{7}
return fileDescriptor_98a910b73321e591, []int{8}
}
type FileIndexingStatus int32
@ -452,7 +480,7 @@ func (x FileIndexingStatus) String() string {
}
func (FileIndexingStatus) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_98a910b73321e591, []int{8}
return fileDescriptor_98a910b73321e591, []int{9}
}
type SpaceShareableStatus int32
@ -480,7 +508,7 @@ func (x SpaceShareableStatus) String() string {
}
func (SpaceShareableStatus) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_98a910b73321e591, []int{9}
return fileDescriptor_98a910b73321e591, []int{10}
}
type NameserviceNameType int32
@ -503,7 +531,7 @@ func (x NameserviceNameType) String() string {
}
func (NameserviceNameType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_98a910b73321e591, []int{10}
return fileDescriptor_98a910b73321e591, []int{11}
}
type DeviceNetworkType int32
@ -531,7 +559,7 @@ func (x DeviceNetworkType) String() string {
}
func (DeviceNetworkType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_98a910b73321e591, []int{11}
return fileDescriptor_98a910b73321e591, []int{12}
}
type BlockPosition int32
@ -9582,6 +9610,7 @@ func init() {
proto.RegisterEnum("anytype.model.ParticipantPermissions", ParticipantPermissions_name, ParticipantPermissions_value)
proto.RegisterEnum("anytype.model.ParticipantStatus", ParticipantStatus_name, ParticipantStatus_value)
proto.RegisterEnum("anytype.model.SpaceAccessType", SpaceAccessType_name, SpaceAccessType_value)
proto.RegisterEnum("anytype.model.SpaceUxType", SpaceUxType_name, SpaceUxType_value)
proto.RegisterEnum("anytype.model.ImageKind", ImageKind_name, ImageKind_value)
proto.RegisterEnum("anytype.model.FileIndexingStatus", FileIndexingStatus_name, FileIndexingStatus_value)
proto.RegisterEnum("anytype.model.SpaceShareableStatus", SpaceShareableStatus_name, SpaceShareableStatus_value)
@ -9742,7 +9771,7 @@ func init() {
}
var fileDescriptor_98a910b73321e591 = []byte{
// 9132 bytes of a gzipped FileDescriptorProto
// 9155 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0xbd, 0x5d, 0x6c, 0x23, 0xd9,
0x95, 0x18, 0x2c, 0xfe, 0x93, 0x87, 0xa2, 0x74, 0x75, 0xfb, 0x8f, 0xa6, 0xdb, 0xfd, 0xb5, 0xe9,
0xf1, 0x4c, 0xbb, 0x3d, 0x56, 0xcf, 0xf4, 0xcc, 0x78, 0xc6, 0x63, 0xcf, 0xd8, 0x14, 0x45, 0xb5,
@ -10300,20 +10329,22 @@ var fileDescriptor_98a910b73321e591 = []byte{
0x63, 0xd0, 0xd9, 0x0a, 0x4b, 0x7d, 0x8f, 0x21, 0xea, 0x66, 0x56, 0x7d, 0x98, 0xca, 0xed, 0x0b,
0x47, 0x58, 0x2c, 0x77, 0xff, 0x3d, 0x58, 0xd7, 0x43, 0xed, 0x8b, 0x20, 0x08, 0x6f, 0xff, 0x1c,
0xfa, 0xf6, 0x99, 0xfa, 0xe6, 0xc3, 0x2a, 0x14, 0x0f, 0x85, 0x1f, 0x78, 0x2e, 0x7d, 0xef, 0x02,
0x20, 0xdf, 0x1d, 0x9a, 0x3e, 0xbe, 0xe3, 0x7e, 0x13, 0x4a, 0x74, 0x1b, 0xe8, 0xb1, 0xed, 0x5a,
0x38, 0x92, 0x2d, 0x5d, 0xb9, 0x4e, 0x1f, 0x16, 0x3a, 0xa3, 0xf1, 0x15, 0xd5, 0x27, 0x58, 0x59,
0x9a, 0xdf, 0x04, 0xde, 0x98, 0x48, 0x6f, 0x64, 0xd2, 0x2d, 0x56, 0xe7, 0x42, 0x7d, 0xae, 0x37,
0x73, 0xff, 0xdb, 0xc0, 0x55, 0xd0, 0xcc, 0x12, 0xe7, 0xb6, 0x3b, 0x88, 0x2e, 0xc8, 0x03, 0x7d,
0xed, 0xc2, 0x12, 0xe7, 0xe1, 0x55, 0xae, 0xb0, 0x11, 0x7e, 0x73, 0x63, 0xc7, 0x9b, 0xb8, 0xd8,
0x8b, 0xa7, 0x70, 0x5d, 0xc9, 0x0c, 0x76, 0x8b, 0xae, 0x48, 0x5e, 0xea, 0xc9, 0xab, 0xab, 0x5c,
0x72, 0x12, 0x44, 0xb8, 0x2c, 0x85, 0x1d, 0x8b, 0xbc, 0xe0, 0x18, 0x9e, 0xbe, 0x5f, 0x87, 0x6b,
0x0b, 0x42, 0x11, 0xa4, 0xa5, 0x95, 0x43, 0xc6, 0x56, 0xee, 0x7f, 0x08, 0x1b, 0x4a, 0xaf, 0x1c,
0xa8, 0x4b, 0x6c, 0xe1, 0x11, 0xf9, 0xac, 0xbd, 0xd3, 0x56, 0x53, 0xd7, 0x6c, 0xed, 0xed, 0x3d,
0xd9, 0x6b, 0x18, 0x2c, 0x45, 0x0b, 0xdc, 0xe9, 0x1d, 0x35, 0x3b, 0x07, 0x07, 0xad, 0x66, 0xaf,
0xb5, 0xcd, 0xd2, 0x5b, 0xf7, 0xff, 0xcd, 0xa7, 0x77, 0x52, 0x3f, 0xff, 0xf4, 0x4e, 0xea, 0x3f,
0x7f, 0x7a, 0x27, 0xf5, 0xe3, 0x5f, 0xdc, 0x59, 0xf9, 0xf9, 0x2f, 0xee, 0xac, 0xfc, 0x87, 0x5f,
0xdc, 0x59, 0xf9, 0x84, 0xcd, 0xfe, 0x4f, 0x99, 0xe3, 0x3c, 0xf9, 0x00, 0x6f, 0xfd, 0x9f, 0x00,
0x00, 0x00, 0xff, 0xff, 0xbe, 0xb7, 0xb3, 0xbf, 0x6e, 0x66, 0x00, 0x00,
0x20, 0xdf, 0x1d, 0x9a, 0x3e, 0xbe, 0xe3, 0xfe, 0xd7, 0xf4, 0x24, 0x3d, 0x39, 0x0f, 0x8f, 0x06,
0xdc, 0x3f, 0xfa, 0x73, 0x2f, 0xa6, 0x34, 0x35, 0xba, 0xf4, 0x85, 0x39, 0x62, 0xe9, 0xfb, 0x4d,
0x28, 0xd1, 0xe5, 0xa1, 0xc7, 0xb6, 0x6b, 0xe1, 0xc0, 0xb7, 0x74, 0xa1, 0x3b, 0x7d, 0x87, 0xe8,
0x8c, 0xa6, 0xa3, 0xa8, 0xbe, 0xd8, 0xca, 0xd2, 0xfc, 0x26, 0xf0, 0xc6, 0x44, 0x7a, 0x23, 0x93,
0x2e, 0xbd, 0x3a, 0x17, 0xea, 0xeb, 0xbe, 0x99, 0xfb, 0xdf, 0x06, 0xae, 0x62, 0x6c, 0x96, 0x38,
0xb7, 0xdd, 0x41, 0x74, 0x9f, 0x1e, 0xe8, 0xe3, 0x18, 0x96, 0x38, 0x0f, 0x6f, 0x7e, 0x85, 0x8d,
0xf0, 0x13, 0x1d, 0x3b, 0xde, 0xc4, 0xc5, 0x4e, 0x3f, 0x85, 0xeb, 0x4a, 0xc4, 0x70, 0x14, 0x74,
0xa3, 0xf2, 0x52, 0xc7, 0x5f, 0xdd, 0xfc, 0x92, 0x93, 0x20, 0xc2, 0x65, 0x29, 0xec, 0x58, 0xe4,
0x34, 0xc7, 0xf0, 0xf4, 0xfd, 0x3a, 0x5c, 0x5b, 0x10, 0xb9, 0x20, 0xa5, 0xae, 0xfc, 0x37, 0xb6,
0x72, 0xff, 0x43, 0xd8, 0x50, 0x6a, 0xe8, 0x40, 0xdd, 0x79, 0x0b, 0xa7, 0xed, 0x59, 0x7b, 0xa7,
0xad, 0x66, 0xba, 0xd9, 0xda, 0xdb, 0x7b, 0xb2, 0xd7, 0x30, 0x58, 0x8a, 0xe4, 0xa1, 0xd3, 0x3b,
0x6a, 0x76, 0x0e, 0x0e, 0x5a, 0xcd, 0x5e, 0x6b, 0x9b, 0xa5, 0xb7, 0xee, 0xff, 0x9b, 0x4f, 0xef,
0xa4, 0x7e, 0xfe, 0xe9, 0x9d, 0xd4, 0x7f, 0xfe, 0xf4, 0x4e, 0xea, 0xc7, 0xbf, 0xb8, 0xb3, 0xf2,
0xf3, 0x5f, 0xdc, 0x59, 0xf9, 0x0f, 0xbf, 0xb8, 0xb3, 0xf2, 0x09, 0x9b, 0xfd, 0x17, 0x34, 0xc7,
0x79, 0x72, 0x19, 0xde, 0xfa, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x29, 0x55, 0xe8, 0xb4, 0x9d,
0x66, 0x00, 0x00,
}
func (m *SmartBlockSnapshotBase) Marshal() (dAtA []byte, err error) {

View file

@ -336,7 +336,7 @@ message Block {
}
message Dataview {
repeated string source = 1;
repeated string source = 1; // can be set for detached(without TargetObjectId) inline sets
repeated View views = 2;
string activeView = 3; // do not generate changes for this field
// deprecated
@ -344,7 +344,7 @@ message Block {
repeated GroupOrder groupOrders = 12;
repeated ObjectOrder objectOrders = 13;
repeated anytype.model.RelationLink relationLinks = 5;
string TargetObjectId = 6;
string TargetObjectId = 6; // empty for original set/collection objects and for detached inline sets
bool isCollection = 14;
message View {
@ -1019,6 +1019,12 @@ enum SpaceAccessType {
Shared = 2;
}
enum SpaceUxType {
Chat = 0; // chat-first UX
Data = 1; // objects-first UX
Stream = 2; // stream UX (chat with limited amount of owners)
}
message Metadata {
oneof payload {
Payload.IdentityPayload identity = 1;

View file

@ -138,7 +138,8 @@ func (s *storageService) anyStoreConfig() *anystore.Config {
}
opts["synchronous"] = "off"
return &anystore.Config{
ReadConnections: 4,
SQLiteConnectionOptions: opts,
ReadConnections: 4,
SQLiteConnectionOptions: opts,
SQLiteGlobalPageCachePreallocateSizeBytes: 1 << 26,
}
}

View file

@ -21,7 +21,7 @@ func TestFiles(t *testing.T) {
t.Run("upload image", func(t *testing.T) {
blockService := getService[*block.Service](app)
objectId, details, err := blockService.UploadFile(ctx, app.personalSpaceId(), block.FileUploadRequest{
objectId, _, details, err := blockService.UploadFile(ctx, app.personalSpaceId(), block.FileUploadRequest{
RpcFileUploadRequest: pb.RpcFileUploadRequest{
LocalPath: "./testdata/test_image.png",
},
@ -43,7 +43,7 @@ func TestFiles(t *testing.T) {
t.Run("upload file", func(t *testing.T) {
blockService := getService[*block.Service](app)
objectId, details, err := blockService.UploadFile(ctx, app.personalSpaceId(), block.FileUploadRequest{
objectId, _, details, err := blockService.UploadFile(ctx, app.personalSpaceId(), block.FileUploadRequest{
RpcFileUploadRequest: pb.RpcFileUploadRequest{
LocalPath: "./files_test.go", // Upload itself :)
},

View file

@ -10,6 +10,7 @@ import (
"net/http"
"os"
"path/filepath"
"slices"
"strconv"
"sync"
"time"
@ -19,6 +20,7 @@ import (
"github.com/anyproto/anytype-heart/core/block/cache"
"github.com/anyproto/anytype-heart/core/block/detailservice"
"github.com/anyproto/anytype-heart/core/block/editor"
"github.com/anyproto/anytype-heart/core/block/editor/state"
"github.com/anyproto/anytype-heart/core/block/editor/widget"
importer "github.com/anyproto/anytype-heart/core/block/import"
@ -77,19 +79,6 @@ var (
pb.RpcObjectImportUseCaseRequest_GET_STARTED: getStartedZip,
pb.RpcObjectImportUseCaseRequest_EMPTY: emptyZip,
}
// TODO: GO-2009 Now we need to create widgets by hands, widget import is not implemented yet
widgetParams = map[pb.RpcObjectImportUseCaseRequestUseCase][]widgetParameters{
pb.RpcObjectImportUseCaseRequest_EMPTY: {
{model.BlockContentWidget_Link, "bafyreic75ulgm2yz426hjwdjkzqw3kafniknki7qkhufqgrspmxzdppixa", "", true},
},
pb.RpcObjectImportUseCaseRequest_GET_STARTED: {
{model.BlockContentWidget_Link, "bafyreiccjf5vbijsmr55ypsnnzltmcvl4n63g73twwxqnfkn5usoq2iqyi", "", true},
{model.BlockContentWidget_View, "bafyreifjgm3iy4o6o4zyf33ld3dnweo2grhvakvr7psn5twjge3xo3627m", "66f6775526909528d002c932", true},
{model.BlockContentWidget_View, "bafyreihrzztw2xcmxxz5uz5xodncby23xdacalcek2dtxxu77yn6wvzsq4", "6182a74fcae0300221f9f207", true},
{model.BlockContentWidget_CompactList, widget.DefaultWidgetRecentOpen, "", false},
},
}
)
type BuiltinObjects interface {
@ -374,39 +363,40 @@ func (b *builtinObjects) createWidgets(ctx session.Context, spaceId string, useC
}
widgetObjectID := spc.DerivedIDs().Widgets
typeId, err := spc.GetTypeIdByKey(context.Background(), bundle.TypeKeyPage)
if err != nil {
log.Errorf("failed to get type id: %w", err)
return
}
// todo: rewrite to use CreateTypeWidgetIfMissing in block.Service
if err = cache.DoStateCtx(b.objectGetter, ctx, widgetObjectID, func(s *state.State, w widget.Widget) error {
for _, param := range widgetParams[useCase] {
objectID := param.objectID
if param.isObjectIDChanged {
objectID, err = b.getNewObjectID(spc.Id(), objectID)
if err != nil {
log.Errorf("Skipping creation of widget block as failed to get new object id using old one '%s': %v", objectID, err)
continue
}
}
request := &pb.RpcBlockCreateWidgetRequest{
ContextId: widgetObjectID,
Position: model.Block_Bottom,
WidgetLayout: param.layout,
Block: &model.Block{
Content: &model.BlockContentOfLink{
Link: &model.BlockContentLink{
TargetBlockId: objectID,
Style: model.BlockContentLink_Page,
IconSize: model.BlockContentLink_SizeNone,
CardStyle: model.BlockContentLink_Inline,
Description: model.BlockContentLink_None,
},
targets := s.Details().Get(bundle.RelationKeyAutoWidgetTargets).StringList()
if slices.Contains(targets, typeId) {
return nil
}
targets = append(targets, typeId)
s.Details().Set(bundle.RelationKeyAutoWidgetTargets, domain.StringList(targets))
request := &pb.RpcBlockCreateWidgetRequest{
ContextId: widgetObjectID,
Position: model.Block_Bottom,
WidgetLayout: model.BlockContentWidget_View,
ViewId: editor.ObjectTypeAllViewId,
Block: &model.Block{
Content: &model.BlockContentOfLink{
Link: &model.BlockContentLink{
TargetBlockId: typeId,
Style: model.BlockContentLink_Page,
IconSize: model.BlockContentLink_SizeNone,
CardStyle: model.BlockContentLink_Inline,
Description: model.BlockContentLink_None,
},
},
}
if param.viewID != "" {
request.ViewId = param.viewID
}
if _, err = w.CreateBlock(s, request); err != nil {
log.Errorf("Failed to make Widget blocks: %v", err)
}
},
}
if _, createErr := w.CreateBlock(s, request); createErr != nil {
return fmt.Errorf("failed to make Widget block: %v", createErr)
}
return nil
}); err != nil {