1
0
Fork 0
mirror of https://github.com/anyproto/anytype-heart.git synced 2025-06-08 13:57:12 +09:00
anytype-heart/space/techspace/techspace.go

235 lines
6.1 KiB
Go

package techspace
import (
"context"
"errors"
"sync"
"github.com/anyproto/any-sync/app"
"github.com/anyproto/any-sync/app/logger"
"github.com/gogo/protobuf/types"
"go.uber.org/zap"
"github.com/anyproto/anytype-heart/core/block/editor"
editorsb "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/object/objectcache"
"github.com/anyproto/anytype-heart/core/block/object/payloadcreator"
"github.com/anyproto/anytype-heart/core/domain"
"github.com/anyproto/anytype-heart/pkg/lib/core/smartblock"
"github.com/anyproto/anytype-heart/space/spacecore"
"github.com/anyproto/anytype-heart/space/spaceinfo"
)
const CName = "client.space.techspace"
var log = logger.NewNamed(CName)
var (
ErrSpaceViewExists = errors.New("spaceView exists")
ErrSpaceViewNotExists = errors.New("spaceView not exists")
ErrNotASpaceView = errors.New("smartblock not a spaceView")
)
func New() TechSpace {
return &techSpace{}
}
type TechSpace interface {
TechSpaceId() string
SpaceViewCreate(ctx context.Context, spaceId string) (err error)
SpaceViewExists(ctx context.Context, spaceId string) (exists bool, err error)
SetInfo(ctx context.Context, info spaceinfo.SpaceInfo) (err error)
SpaceViewSetData(ctx context.Context, spaceId string, details *types.Struct) (err error)
SpaceViewId(id string) (string, error)
app.ComponentRunnable
}
type techSpace struct {
techCore *spacecore.AnySpace
spaceCoreService spacecore.SpaceCoreService
objectCache objectcache.Cache
mu sync.Mutex
ctx context.Context
ctxCancel context.CancelFunc
idsWakedUp chan struct{}
viewIds map[string]string
}
func (s *techSpace) Init(a *app.App) (err error) {
s.viewIds = make(map[string]string)
s.objectCache = app.MustComponent[objectcache.Cache](a)
s.spaceCoreService = app.MustComponent[spacecore.SpaceCoreService](a)
s.ctx, s.ctxCancel = context.WithCancel(context.Background())
return
}
func (s *techSpace) Name() (name string) {
return CName
}
func (s *techSpace) Run(ctx context.Context) (err error) {
if s.techCore, err = s.spaceCoreService.Derive(ctx, spacecore.TechSpaceType); err != nil {
return
}
s.idsWakedUp = make(chan struct{})
go func() {
defer close(s.idsWakedUp)
s.wakeUpViews()
}()
return
}
func (s *techSpace) wakeUpViews() {
for _, id := range s.techCore.StoredIds() {
select {
case <-s.ctx.Done():
return
default:
}
s.mu.Lock()
if _, err := s.objectCache.GetObject(s.ctx, domain.FullID{
ObjectID: id,
SpaceID: s.techCore.Id(),
}); err != nil {
log.Warn("wakeUp views: get object error", zap.String("objectId", id), zap.Error(err))
}
s.mu.Unlock()
}
s.techCore.TreeSyncer().StartSync()
return
}
func (s *techSpace) TechSpaceId() string {
return s.techCore.Id()
}
func (s *techSpace) SetInfo(ctx context.Context, info spaceinfo.SpaceInfo) (err error) {
s.mu.Lock()
defer s.mu.Unlock()
return s.doSpaceView(ctx, info.SpaceID, func(spaceView *editor.SpaceView) error {
return spaceView.SetSpaceInfo(info)
})
}
func (s *techSpace) SpaceViewCreate(ctx context.Context, spaceId string) (err error) {
s.mu.Lock()
defer s.mu.Unlock()
viewId, err := s.getViewId(ctx, spaceId)
if err != nil {
return err
}
_, err = s.objectCache.GetObject(ctx, domain.FullID{
ObjectID: viewId,
SpaceID: s.techCore.Id(),
})
if err != nil { // TODO: check specific error
return s.spaceViewCreate(ctx, spaceId)
}
return ErrSpaceViewExists
}
func (s *techSpace) SpaceViewExists(ctx context.Context, spaceId string) (exists bool, err error) {
s.mu.Lock()
defer s.mu.Unlock()
viewId, err := s.getViewId(ctx, spaceId)
if err != nil {
return
}
_, getErr := s.objectCache.GetObject(ctx, domain.FullID{
ObjectID: viewId,
SpaceID: s.techCore.Id(),
})
return getErr == nil, nil
}
func (s *techSpace) SpaceViewSetData(ctx context.Context, spaceId string, details *types.Struct) (err error) {
return s.doSpaceView(ctx, spaceId, func(spaceView *editor.SpaceView) error {
return spaceView.SetSpaceData(details)
})
}
func (s *techSpace) SpaceViewId(spaceId string) (string, error) {
s.mu.Lock()
defer s.mu.Unlock()
return s.getViewId(context.TODO(), spaceId)
}
func (s *techSpace) spaceViewCreate(ctx context.Context, spaceID string) (err error) {
uniqueKey, err := domain.NewUniqueKey(smartblock.SmartBlockTypeSpaceView, "")
if err != nil {
return
}
_, err = s.objectCache.DeriveTreeObject(ctx, s.techCore.Id(), objectcache.TreeDerivationParams{
Key: uniqueKey,
InitFunc: func(id string) *editorsb.InitContext {
return &editorsb.InitContext{Ctx: ctx, SpaceID: s.techCore.Id(), State: state.NewDoc(id, nil).(*state.State)}
},
TargetSpaceID: spaceID,
})
if err != nil {
return
}
return
}
func (s *techSpace) deriveSpaceViewID(ctx context.Context, spaceID string) (string, error) {
uniqueKey, err := domain.NewUniqueKey(smartblock.SmartBlockTypeSpaceView, "")
if err != nil {
return "", err
}
payload, err := s.objectCache.DeriveTreePayload(ctx, s.techCore.Id(), payloadcreator.PayloadDerivationParams{
Key: uniqueKey,
TargetSpaceID: spaceID,
})
if err != nil {
return "", err
}
return payload.RootRawChange.Id, nil
}
func (s *techSpace) doSpaceView(ctx context.Context, spaceID string, apply func(spaceView *editor.SpaceView) error) (err error) {
viewId, err := s.getViewId(ctx, spaceID)
if err != nil {
return
}
obj, err := s.objectCache.GetObject(ctx, domain.FullID{
ObjectID: viewId,
SpaceID: s.techCore.Id(),
})
if err != nil {
return ErrSpaceViewNotExists
}
spaceView, ok := obj.(*editor.SpaceView)
if !ok {
return ErrNotASpaceView
}
spaceView.Lock()
defer spaceView.Unlock()
return apply(spaceView)
}
func (s *techSpace) getViewId(ctx context.Context, spaceId string) (viewId string, err error) {
if viewId = s.viewIds[spaceId]; viewId != "" {
return
}
if viewId, err = s.deriveSpaceViewID(ctx, spaceId); err != nil {
return
}
s.viewIds[spaceId] = viewId
return
}
func (s *techSpace) Close(ctx context.Context) (err error) {
s.ctxCancel()
if s.idsWakedUp != nil {
<-s.idsWakedUp
}
return
}