Skip to content

Commit f582f26

Browse files
authored
Merge pull request #1756 from anyproto/go-4348-create-widget-on-favoriting
GO-4348 Create favorite widget on favoriting
2 parents 0d37216 + cc9c88e commit f582f26

File tree

7 files changed

+124
-44
lines changed

7 files changed

+124
-44
lines changed

core/block/delete.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ func (s *Service) OnDelete(id domain.FullID, workspaceRemove func() error) error
132132
b.ObjectCloseAllSessions()
133133
st := b.NewState()
134134
isFavorite := pbtypes.GetBool(st.LocalDetails(), bundle.RelationKeyIsFavorite.String())
135-
if err := s.detailsService.SetIsFavorite(id.ObjectID, isFavorite); err != nil {
135+
if err := s.detailsService.SetIsFavorite(id.ObjectID, isFavorite, false); err != nil {
136136
log.With("objectId", id).Errorf("failed to favorite object: %v", err)
137137
}
138138
b.SetIsDeleted()

core/block/detailservice/mock_detailservice/mock_Service.go

+11-10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/block/detailservice/service.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ type Service interface {
4646
SetSpaceInfo(spaceId string, details *types.Struct) error
4747
SetWorkspaceDashboardId(ctx session.Context, workspaceId string, id string) (setId string, err error)
4848

49-
SetIsFavorite(objectId string, isFavorite bool) error
49+
SetIsFavorite(objectId string, isFavorite, createWidget bool) error
5050
SetIsArchived(objectId string, isArchived bool) error
5151
SetListIsFavorite(objectIds []string, isFavorite bool) error
5252
SetListIsArchived(objectIds []string, isArchived bool) error

core/block/detailservice/service_test.go

+51-26
Original file line numberDiff line numberDiff line change
@@ -325,82 +325,107 @@ func TestService_SetListIsFavorite(t *testing.T) {
325325
{bundle.RelationKeyId: pbtypes.String("obj2"), bundle.RelationKeySpaceId: pbtypes.String(spaceId)},
326326
{bundle.RelationKeyId: pbtypes.String("obj3"), bundle.RelationKeySpaceId: pbtypes.String(spaceId)},
327327
}
328-
homeId = "home"
328+
homeId = "home"
329+
widgetId = "widget"
329330
)
330331

331332
t.Run("no error on favoriting", func(t *testing.T) {
332333
// given
333334
fx := newFixture(t)
334-
sb := smarttest.New(homeId)
335-
sb.AddBlock(simple.New(&model.Block{Id: homeId, ChildrenIds: []string{}}))
335+
home := smarttest.New(homeId)
336+
home.AddBlock(simple.New(&model.Block{Id: homeId, ChildrenIds: []string{}}))
337+
widget := smarttest.New(widgetId)
338+
widget.AddBlock(simple.New(&model.Block{Id: widgetId, ChildrenIds: []string{}}))
336339
fx.store.AddObjects(t, spaceId, objects)
337-
fx.space.EXPECT().DerivedIDs().Return(threads.DerivedSmartblockIds{Home: homeId})
340+
fx.space.EXPECT().DerivedIDs().Return(threads.DerivedSmartblockIds{Home: homeId, Widgets: widgetId})
338341
fx.getter.EXPECT().GetObject(mock.Anything, mock.Anything).RunAndReturn(func(_ context.Context, objectId string) (smartblock.SmartBlock, error) {
339-
require.Equal(t, homeId, objectId)
340-
return editor.NewDashboard(sb, fx.store.SpaceIndex(spaceId), nil), nil
342+
switch objectId {
343+
case homeId:
344+
return editor.NewDashboard(home, fx.store.SpaceIndex(spaceId), nil), nil
345+
case widgetId:
346+
return editor.NewWidgetObject(widget, fx.store.SpaceIndex(spaceId), nil), nil
347+
}
348+
return nil, fmt.Errorf("failed to get object")
341349
})
342350

343351
// when
344352
err := fx.SetListIsFavorite([]string{"obj1", "obj2", "obj3"}, true)
345353

346354
// then
347355
assert.NoError(t, err)
348-
assert.Len(t, sb.Blocks(), 4)
356+
assert.Len(t, home.Blocks(), 4)
357+
assert.Len(t, widget.Blocks(), 3)
349358
})
350359

351360
t.Run("no error on unfavoriting", func(t *testing.T) {
352361
// given
353362
fx := newFixture(t)
354-
sb := smarttest.New(homeId)
355-
sb.AddBlock(simple.New(&model.Block{Id: homeId, ChildrenIds: []string{"obj1", "obj2", "obj3"}}))
356-
sb.AddBlock(simple.New(&model.Block{Id: "obj1", Content: &model.BlockContentOfLink{Link: &model.BlockContentLink{TargetBlockId: "obj1"}}}))
357-
sb.AddBlock(simple.New(&model.Block{Id: "obj2", Content: &model.BlockContentOfLink{Link: &model.BlockContentLink{TargetBlockId: "obj2"}}}))
358-
sb.AddBlock(simple.New(&model.Block{Id: "obj3", Content: &model.BlockContentOfLink{Link: &model.BlockContentLink{TargetBlockId: "obj3"}}}))
363+
home := smarttest.New(homeId)
364+
home.AddBlock(simple.New(&model.Block{Id: homeId, ChildrenIds: []string{"obj1", "obj2", "obj3"}}))
365+
home.AddBlock(simple.New(&model.Block{Id: "obj1", Content: &model.BlockContentOfLink{Link: &model.BlockContentLink{TargetBlockId: "obj1"}}}))
366+
home.AddBlock(simple.New(&model.Block{Id: "obj2", Content: &model.BlockContentOfLink{Link: &model.BlockContentLink{TargetBlockId: "obj2"}}}))
367+
home.AddBlock(simple.New(&model.Block{Id: "obj3", Content: &model.BlockContentOfLink{Link: &model.BlockContentLink{TargetBlockId: "obj3"}}}))
368+
widget := smarttest.New(widgetId)
369+
widget.AddBlock(simple.New(&model.Block{Id: widgetId, ChildrenIds: []string{}}))
359370
fx.store.AddObjects(t, spaceId, objects)
360-
fx.space.EXPECT().DerivedIDs().Return(threads.DerivedSmartblockIds{Home: homeId})
371+
fx.space.EXPECT().DerivedIDs().Return(threads.DerivedSmartblockIds{Home: homeId, Widgets: widgetId})
361372
fx.getter.EXPECT().GetObject(mock.Anything, mock.Anything).RunAndReturn(func(_ context.Context, objectId string) (smartblock.SmartBlock, error) {
362-
require.Equal(t, homeId, objectId)
363-
return editor.NewDashboard(sb, fx.store.SpaceIndex(spaceId), nil), nil
373+
switch objectId {
374+
case homeId:
375+
return editor.NewDashboard(home, fx.store.SpaceIndex(spaceId), nil), nil
376+
case widgetId:
377+
return editor.NewWidgetObject(widget, fx.store.SpaceIndex(spaceId), nil), nil
378+
}
379+
return nil, fmt.Errorf("failed to get object")
364380
})
365381

366382
// when
367383
err := fx.SetListIsFavorite([]string{"obj3", "obj1"}, false)
368384

369385
// then
370386
assert.NoError(t, err)
371-
assert.Len(t, sb.Blocks(), 2)
387+
assert.Len(t, home.Blocks(), 2)
388+
assert.Len(t, widget.Blocks(), 1)
372389
})
373390

374391
t.Run("some updates failed", func(t *testing.T) {
375392
// given
376393
fx := newFixture(t)
377-
sb := smarttest.New(homeId)
378-
sb.AddBlock(simple.New(&model.Block{Id: homeId, ChildrenIds: []string{}}))
394+
home := smarttest.New(homeId)
395+
home.AddBlock(simple.New(&model.Block{Id: homeId, ChildrenIds: []string{}}))
396+
widget := smarttest.New(widgetId)
397+
widget.AddBlock(simple.New(&model.Block{Id: widgetId, ChildrenIds: []string{}}))
379398
fx.store.AddObjects(t, spaceId, objects)
380-
fx.space.EXPECT().DerivedIDs().Return(threads.DerivedSmartblockIds{Home: homeId})
399+
fx.space.EXPECT().DerivedIDs().Return(threads.DerivedSmartblockIds{Home: homeId, Widgets: widgetId})
381400
flag := false
382401
fx.getter.EXPECT().GetObject(mock.Anything, mock.Anything).RunAndReturn(func(_ context.Context, objectId string) (smartblock.SmartBlock, error) {
383-
require.Equal(t, homeId, objectId)
384-
if flag {
385-
return nil, fmt.Errorf("unexpected error")
402+
switch objectId {
403+
case homeId:
404+
if flag {
405+
return nil, fmt.Errorf("unexpected error")
406+
}
407+
flag = true
408+
return editor.NewDashboard(home, fx.store.SpaceIndex(spaceId), nil), nil
409+
case widgetId:
410+
return editor.NewWidgetObject(widget, fx.store.SpaceIndex(spaceId), nil), nil
386411
}
387-
flag = true
388-
return editor.NewDashboard(sb, fx.store.SpaceIndex(spaceId), nil), nil
412+
return nil, fmt.Errorf("failed to get object")
389413
})
390414

391415
// when
392416
err := fx.SetListIsFavorite([]string{"obj3", "obj1"}, true)
393417

394418
// then
395419
assert.NoError(t, err)
396-
assert.Len(t, sb.Blocks(), 2)
420+
assert.Len(t, home.Blocks(), 2)
421+
assert.Len(t, widget.Blocks(), 3)
397422
})
398423

399424
t.Run("all updates failed", func(t *testing.T) {
400425
// given
401426
fx := newFixture(t)
402427
fx.store.AddObjects(t, spaceId, objects)
403-
fx.space.EXPECT().DerivedIDs().Return(threads.DerivedSmartblockIds{Home: homeId})
428+
fx.space.EXPECT().DerivedIDs().Return(threads.DerivedSmartblockIds{Home: homeId, Widgets: widgetId})
404429
fx.getter.EXPECT().GetObject(mock.Anything, mock.Anything).RunAndReturn(func(_ context.Context, objectId string) (smartblock.SmartBlock, error) {
405430
require.Equal(t, homeId, objectId)
406431
return nil, fmt.Errorf("unexpected error")

core/block/detailservice/set_details.go

+58-4
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@ import (
1212
"github.com/anyproto/anytype-heart/core/block/editor"
1313
"github.com/anyproto/anytype-heart/core/block/editor/collection"
1414
"github.com/anyproto/anytype-heart/core/block/editor/smartblock"
15+
"github.com/anyproto/anytype-heart/core/block/editor/state"
16+
"github.com/anyproto/anytype-heart/core/block/editor/widget"
17+
"github.com/anyproto/anytype-heart/core/block/simple"
1518
"github.com/anyproto/anytype-heart/core/session"
19+
"github.com/anyproto/anytype-heart/pb"
1620
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
1721
coresb "github.com/anyproto/anytype-heart/pkg/lib/core/smartblock"
1822
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
@@ -57,7 +61,7 @@ func (s *service) SetWorkspaceDashboardId(ctx session.Context, workspaceId strin
5761
return id, err
5862
}
5963

60-
func (s *service) SetIsFavorite(objectId string, isFavorite bool) error {
64+
func (s *service) SetIsFavorite(objectId string, isFavorite, createWidget bool) error {
6165
spaceID, err := s.resolver.ResolveSpaceID(objectId)
6266
if err != nil {
6367
return fmt.Errorf("resolve spaceID: %w", err)
@@ -66,7 +70,18 @@ func (s *service) SetIsFavorite(objectId string, isFavorite bool) error {
6670
if err != nil {
6771
return fmt.Errorf("get space: %w", err)
6872
}
69-
return s.objectLinksCollectionModify(spc.DerivedIDs().Home, objectId, isFavorite)
73+
if err = s.objectLinksCollectionModify(spc.DerivedIDs().Home, objectId, isFavorite); err != nil {
74+
return err
75+
}
76+
77+
if createWidget && isFavorite {
78+
err = s.createFavoriteWidget(spc.DerivedIDs().Widgets)
79+
if err != nil {
80+
log.Error("failed to create favorite widget", zap.Error(err))
81+
}
82+
}
83+
84+
return nil
7085
}
7186

7287
func (s *service) SetIsArchived(objectId string, isArchived bool) error {
@@ -100,9 +115,9 @@ func (s *service) SetListIsFavorite(objectIds []string, isFavorite bool) error {
100115
return err
101116
}
102117

103-
for _, id := range ids {
118+
for i, id := range ids {
104119
// TODO Set list of ids at once
105-
err := s.SetIsFavorite(id, isFavorite)
120+
err := s.SetIsFavorite(id, isFavorite, i == 0)
106121
if err != nil {
107122
log.Error("failed to favorite object", zap.String("objectId", id), zap.Error(err))
108123
resultError = errors.Join(resultError, err)
@@ -229,3 +244,42 @@ func (s *service) modifyArchiveLinks(
229244
}
230245
return
231246
}
247+
248+
func (s *service) createFavoriteWidget(widgetObjectId string) error {
249+
return cache.DoState(s.objectGetter, widgetObjectId, func(st *state.State, w widget.Widget) (err error) {
250+
var favoriteBlockFound bool
251+
err = st.Iterate(func(b simple.Block) (isContinue bool) {
252+
link := b.Model().GetLink()
253+
if link == nil {
254+
return true
255+
}
256+
if link.TargetBlockId == widget.DefaultWidgetFavorite {
257+
favoriteBlockFound = true
258+
return false
259+
}
260+
return true
261+
})
262+
263+
if err != nil {
264+
return err
265+
}
266+
267+
if favoriteBlockFound {
268+
log.Debug("favorite widget block is already presented")
269+
return nil
270+
}
271+
272+
_, err = w.CreateBlock(st, &pb.RpcBlockCreateWidgetRequest{
273+
ContextId: widgetObjectId,
274+
ObjectLimit: 6,
275+
WidgetLayout: model.BlockContentWidget_List,
276+
Position: model.Block_Bottom,
277+
Block: &model.Block{
278+
Content: &model.BlockContentOfLink{Link: &model.BlockContentLink{
279+
TargetBlockId: widget.DefaultWidgetFavorite,
280+
}},
281+
},
282+
})
283+
return err
284+
})
285+
}

core/block/import/common/objectcreator/objectcreator.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ func (oc *ObjectCreator) resetState(newID string, st *state.State) *types.Struct
392392
func (oc *ObjectCreator) setFavorite(snapshot *model.SmartBlockSnapshotBase, newID string) {
393393
isFavorite := pbtypes.GetBool(snapshot.Details, bundle.RelationKeyIsFavorite.String())
394394
if isFavorite {
395-
err := oc.detailsService.SetIsFavorite(newID, true)
395+
err := oc.detailsService.SetIsFavorite(newID, true, false)
396396
if err != nil {
397397
log.With(zap.String("object id", newID)).Errorf("failed to set isFavorite when importing object: %s", err)
398398
}

core/details.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ func (mw *Middleware) ObjectSetIsFavorite(_ context.Context, req *pb.RpcObjectSe
115115
}
116116
return m
117117
}
118-
err := getService[detailservice.Service](mw).SetIsFavorite(req.ContextId, req.IsFavorite)
118+
err := getService[detailservice.Service](mw).SetIsFavorite(req.ContextId, req.IsFavorite, true)
119119
if err != nil {
120120
return response(pb.RpcObjectSetIsFavoriteResponseError_UNKNOWN_ERROR, err)
121121
}

0 commit comments

Comments
 (0)