1
0
Fork 0
mirror of https://github.com/anyproto/anytype-heart.git synced 2025-06-11 02:13:41 +09:00

Merge pull request #1561 from anytypeio/feat-GO-212-changes-for-kanban

changes for kanban
This commit is contained in:
pzavyalov 2022-09-28 16:23:24 +06:00 committed by GitHub
commit 8eaa291f94
Signed by: github
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 4987 additions and 3860 deletions

View file

@ -410,6 +410,8 @@ func (s *State) fillChanges(msgs []simple.EventMessage) {
updMsgs = append(updMsgs, msg.Msg)
case *pb.EventMessageValueOfBlockDataViewGroupOrderUpdate:
updMsgs = append(updMsgs, msg.Msg)
case *pb.EventMessageValueOfBlockDataViewObjectOrderUpdate:
updMsgs = append(updMsgs, msg.Msg)
default:
log.Errorf("unexpected event - can't convert to changes: %v", msg.Msg)
}

View file

@ -2,6 +2,7 @@ package state
import (
"fmt"
"github.com/anytypeio/go-anytype-middleware/util/pbtypes"
"github.com/anytypeio/go-anytype-middleware/core/block/simple/latex"
"github.com/anytypeio/go-anytype-middleware/core/block/simple/table"
@ -212,6 +213,26 @@ func (s *State) applyEvent(ev *pb.EventMessage) (err error) {
}); err != nil {
return
}
case *pb.EventMessageValueOfBlockDataViewObjectOrderUpdate:
if err = apply(o.BlockDataViewObjectOrderUpdate.Id, func(b simple.Block) error {
if f, ok := b.(dataview.Block); ok {
for _, order := range b.Model().GetDataview().ObjectOrders {
if order.ViewId == o.BlockDataViewObjectOrderUpdate.ViewId && order.GroupId == o.BlockDataViewObjectOrderUpdate.GroupId {
changes := o.BlockDataViewObjectOrderUpdate.GetSliceChanges()
changedIds := slice.ApplyChanges(order.ObjectIds, pbtypes.EventsToSliceChange(changes))
order.ObjectIds = changedIds
}
}
f.SetViewObjectOrder(b.Model().GetDataview().ObjectOrders)
return nil
}
return fmt.Errorf("not a dataview block")
}); err != nil {
return
}
}
return nil

View file

@ -112,6 +112,42 @@ func (d *Dataview) Diff(b simple.Block) (msgs []simple.EventMessage, err error)
}
}
for _, order2 := range dv.content.ObjectOrders {
var found bool
var changes []slice.Change
for _, order1 := range d.content.ObjectOrders {
if order1.ViewId == order2.ViewId && order1.GroupId == order2.GroupId {
found = true
changes = slice.Diff(order1.ObjectIds, order2.ObjectIds)
break
}
}
if !found {
msgs = append(msgs,
simple.EventMessage{
Msg: &pb.EventMessage{Value: &pb.EventMessageValueOfBlockDataViewObjectOrderUpdate{
&pb.EventBlockDataviewObjectOrderUpdate{
Id: dv.Id,
ViewId: order2.ViewId,
GroupId: order2.GroupId,
SliceChanges: []*pb.EventBlockDataviewSliceChange{{Op: pb.EventBlockDataview_SliceOperationAdd, Ids: order2.ObjectIds}},
}}}})
}
if len(changes) > 0 {
msgs = append(msgs,
simple.EventMessage{
Msg: &pb.EventMessage{Value: &pb.EventMessageValueOfBlockDataViewObjectOrderUpdate{
&pb.EventBlockDataviewObjectOrderUpdate{
Id: dv.Id,
ViewId: order2.ViewId,
GroupId: order2.GroupId,
SliceChanges: pbtypes.SliceChangeToEvents(changes),
}}}})
}
}
// @TODO: rewrite for optimised compare
for _, view2 := range dv.content.Views {
var found bool

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -68,6 +68,7 @@ message Event {
Block.Dataview.RelationDelete blockDataviewRelationDelete = 24;
Block.Dataview.RelationSet blockDataviewRelationSet = 23;
Block.Dataview.GroupOrderUpdate blockDataViewGroupOrderUpdate = 38;
Block.Dataview.ObjectOrderUpdate blockDataViewObjectOrderUpdate = 39;
User.Block.Join userBlockJoin = 31;
@ -740,6 +741,27 @@ message Event {
string id = 1; // dataview block's id
anytype.model.Block.Content.Dataview.GroupOrder groupOrder = 2;
}
message ObjectOrderUpdate {
string id = 1; // dataview block's id
string viewId = 2;
string groupId = 3;
repeated SliceChange sliceChanges = 4;
}
message SliceChange {
SliceOperation op = 1;
repeated string ids = 2;
string afterId = 3;
}
enum SliceOperation {
SliceOperationNone = 0; // not used
SliceOperationAdd = 1;
SliceOperationMove = 2;
SliceOperationRemove = 3;
SliceOperationReplace = 4;
}
}
}

View file

@ -1,6 +1,7 @@
package pbtypes
import (
"github.com/anytypeio/go-anytype-middleware/pb"
"sync"
"github.com/anytypeio/go-anytype-middleware/util/slice"
@ -202,3 +203,38 @@ func StructNotNilKeys(st *types.Struct) (keys []string) {
}
return
}
func EventsToSliceChange(changes []*pb.EventBlockDataviewSliceChange) []slice.Change {
sliceOpMap := map[pb.EventBlockDataviewSliceOperation]slice.DiffOperation{
pb.EventBlockDataview_SliceOperationNone: slice.OperationNone,
pb.EventBlockDataview_SliceOperationAdd: slice.OperationAdd,
pb.EventBlockDataview_SliceOperationMove: slice.OperationMove,
pb.EventBlockDataview_SliceOperationRemove: slice.OperationRemove,
pb.EventBlockDataview_SliceOperationReplace: slice.OperationReplace,
}
var res []slice.Change
for _, eventCh := range changes {
res = append(res, slice.Change{Op: sliceOpMap[eventCh.Op], Ids: eventCh.Ids, AfterId: eventCh.AfterId})
}
return res
}
func SliceChangeToEvents(changes []slice.Change) []*pb.EventBlockDataviewSliceChange {
eventsOpMap := map[slice.DiffOperation]pb.EventBlockDataviewSliceOperation {
slice.OperationNone: pb.EventBlockDataview_SliceOperationNone,
slice.OperationAdd: pb.EventBlockDataview_SliceOperationAdd,
slice.OperationMove: pb.EventBlockDataview_SliceOperationMove,
slice.OperationRemove: pb.EventBlockDataview_SliceOperationRemove,
slice.OperationReplace: pb.EventBlockDataview_SliceOperationReplace,
}
var res []*pb.EventBlockDataviewSliceChange
for _, sliceCh := range changes {
res = append(res, &pb.EventBlockDataviewSliceChange{Op: eventsOpMap[sliceCh.Op], Ids: sliceCh.Ids, AfterId: sliceCh.AfterId})
}
return res
}

134
util/slice/diff.go Normal file
View file

@ -0,0 +1,134 @@
package slice
import (
"github.com/mb0/diff"
)
type DiffOperation int
const (
OperationNone DiffOperation = iota
OperationAdd
OperationMove
OperationRemove
OperationReplace
)
type Change struct {
Op DiffOperation
Ids []string
AfterId string
}
type MixedInput struct {
A []string
B []string
}
func (m *MixedInput) Equal(a, b int) bool {
return m.A[a] == m.B[b]
}
func Diff(origin, changed []string) []Change {
m := &MixedInput{
origin,
changed,
}
var result []Change
changes := diff.Diff(len(m.A), len(m.B), m)
delMap := make(map[string]bool)
for _, c := range changes {
if c.Del > 0 {
for _, id := range m.A[c.A:c.A+c.Del] {
delMap[id] = true
}
}
}
for _, c := range changes {
if c.Ins > 0 {
inserts := m.B[c.B:c.B+c.Ins]
afterId := ""
if c.A > 0 {
afterId = m.A[c.A-1]
}
var oneCh Change
for _, id := range inserts {
if delMap[id] { // move
if oneCh.Op != OperationMove {
if len(oneCh.Ids) > 0 {
result = append(result, oneCh)
}
oneCh = Change{Op: OperationMove, AfterId: afterId}
}
oneCh.Ids = append(oneCh.Ids, id)
delete(delMap, id)
} else { // insert new
if oneCh.Op != OperationAdd {
if len(oneCh.Ids) > 0 {
result = append(result, oneCh)
}
oneCh = Change{Op: OperationAdd, AfterId: afterId}
}
oneCh.Ids = append(oneCh.Ids, id)
}
afterId = id
}
if len(oneCh.Ids) > 0 {
result = append(result, oneCh)
}
}
}
if len(delMap) > 0 { // remove
delIds := make([]string, 0, len(delMap))
for id := range delMap {
delIds = append(delIds, id)
}
result = append(result, Change{Op: OperationRemove, Ids: delIds})
}
return result
}
func ApplyChanges(origin []string, changes []Change) []string {
result := make([]string, len(origin))
copy(result, origin)
for _, ch := range changes {
switch ch.Op {
case OperationAdd:
pos := -1
if ch.AfterId != "" {
pos = FindPos(result, ch.AfterId)
if pos < 0 {
continue
}
}
result = Insert(result, pos+1, ch.Ids...)
case OperationMove:
withoutMoved := Filter(result, func(id string) bool {
return FindPos(ch.Ids, id) < 0
})
pos := -1
if ch.AfterId != "" {
pos = FindPos(withoutMoved, ch.AfterId)
if pos < 0 {
continue
}
}
result = Insert(withoutMoved, pos+1, ch.Ids...)
case OperationRemove:
result = Filter(result, func(id string) bool{
return FindPos(ch.Ids, id) < 0
})
case OperationReplace:
result = ch.Ids
}
}
return result
}

100
util/slice/diff_test.go Normal file
View file

@ -0,0 +1,100 @@
package slice
import (
"github.com/globalsign/mgo/bson"
"github.com/stretchr/testify/assert"
"math/rand"
"testing"
"time"
)
func Test_Diff(t *testing.T) {
origin := []string{"000", "001", "002", "003", "004", "005", "006", "007", "008", "009"}
changed := []string{"000", "008", "001", "002", "003", "005", "006", "007", "009", "004"}
chs := Diff(origin, changed)
assert.Equal(t, chs, []Change{
{Op: OperationMove, Ids: []string{"008"}, AfterId: "000"},
{Op: OperationMove, Ids: []string{"004"}, AfterId: "009"}},
)
}
func Test_ChangesApply(t *testing.T) {
origin := []string{"000", "001", "002", "003", "004", "005", "006", "007", "008", "009"}
changed := []string{"000", "008", "001", "002", "003", "005", "006", "007", "009", "004", "new"}
chs := Diff(origin, changed)
res := ApplyChanges(origin, chs)
assert.Equal(t, changed, res)
}
func Test_SameLength(t *testing.T) {
for i := 0; i < 10000; i++ {
l := randNum(5, 200)
origin := getRandArray(l)
changed := make([]string, len(origin))
copy(changed, origin)
rand.Shuffle(len(changed),
func(i, j int) { changed[i], changed[j] = changed[j], changed[i] })
chs := Diff(origin, changed)
res := ApplyChanges(origin, chs)
assert.Equal(t, res, changed)
}
}
func Test_DifferentLength(t *testing.T) {
for i := 0; i < 10000; i++ {
l := randNum(5, 200)
origin := getRandArray(l)
changed := make([]string, len(origin))
copy(changed, origin)
rand.Shuffle(len(changed),
func(i, j int) { changed[i], changed[j] = changed[j], changed[i] })
delCnt := randNum(0, 10)
for i := 0; i < delCnt; i++ {
l := len(changed) - 1
if l <= 0 {
continue
}
delIdx := randNum(0, l)
changed = Remove(changed, changed[delIdx])
}
insCnt := randNum(0, 10)
for i := 0; i < insCnt; i++ {
l := len(changed) - 1
if l <= 0 {
continue
}
insIdx := randNum(0, l)
changed = Insert(changed, insIdx, []string{bson.NewObjectId().Hex()}...)
}
chs := Diff(origin, changed)
res := ApplyChanges(origin, chs)
assert.Equal(t, res, changed)
}
}
func randNum(min, max int) int{
if max <= min {
return max
}
rand.Seed(time.Now().UnixNano())
return rand.Intn(max - min) + min
}
func getRandArray(len int) []string {
res := make([]string, len)
for i := 0; i < len; i++ {
res[i] = bson.NewObjectId().Hex()
}
return res
}