1
0
Fork 0
mirror of https://github.com/anyproto/anytype-heart.git synced 2025-06-11 18:20:33 +09:00

GO-4408 Unify date obj creation

This commit is contained in:
kirillston 2024-11-01 10:17:05 +01:00
parent 732b4024d1
commit 48342d4d66
No known key found for this signature in database
GPG key ID: 88218A7F1109754B
16 changed files with 316 additions and 234 deletions

View file

@ -17,8 +17,8 @@ import (
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
coresb "github.com/anyproto/anytype-heart/pkg/lib/core/smartblock"
"github.com/anyproto/anytype-heart/pkg/lib/database"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/addr"
"github.com/anyproto/anytype-heart/space/spacecore/typeprovider"
"github.com/anyproto/anytype-heart/util/date"
"github.com/anyproto/anytype-heart/util/pbtypes"
"github.com/anyproto/anytype-heart/util/slice"
)
@ -114,7 +114,7 @@ func generateFilter(value *types.Value) func(v *types.Value) bool {
return equalFilter
}
start, err := dateIDToDayStart(stringValue)
start, err := date.ParseDateId(stringValue)
if err != nil {
log.Error("failed to convert date id to day start", zap.Error(err))
return equalFilter
@ -132,10 +132,3 @@ func generateFilter(value *types.Value) func(v *types.Value) bool {
return equalFilter(v)
}
}
func dateIDToDayStart(id string) (time.Time, error) {
if !strings.HasPrefix(id, addr.DatePrefix) {
return time.Time{}, fmt.Errorf("invalid id: date prefix not found")
}
return time.Parse("2006-01-02", strings.TrimPrefix(id, addr.DatePrefix))
}

View file

@ -14,9 +14,9 @@ import (
"github.com/anyproto/anytype-heart/core/block/editor/state"
"github.com/anyproto/anytype-heart/core/domain"
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/addr"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/objectstore"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
"github.com/anyproto/anytype-heart/util/date"
"github.com/anyproto/anytype-heart/util/pbtypes"
)
@ -57,7 +57,7 @@ func TestService_ListRelationsWithValue(t *testing.T) {
{
bundle.RelationKeyId: pbtypes.String("obj2"),
bundle.RelationKeySpaceId: pbtypes.String(spaceId),
bundle.RelationKeyName: pbtypes.String(addr.TimeToID(now)),
bundle.RelationKeyName: pbtypes.String(date.TimeToDateId(now)),
bundle.RelationKeyCreatedDate: pbtypes.Int64(now.Add(-24*time.Hour - 5*time.Minute).Unix()),
bundle.RelationKeyAddedDate: pbtypes.Int64(now.Add(-24*time.Hour - 3*time.Minute).Unix()),
bundle.RelationKeyLastModifiedDate: pbtypes.Int64(now.Add(-1 * time.Minute).Unix()),
@ -84,13 +84,13 @@ func TestService_ListRelationsWithValue(t *testing.T) {
}{
{
"date object - today",
pbtypes.String(addr.TimeToID(now)),
pbtypes.String(date.TimeToDateId(now)),
[]string{bundle.RelationKeyAddedDate.String(), bundle.RelationKeyCreatedDate.String(), bundle.RelationKeyLastModifiedDate.String(), bundle.RelationKeyName.String()},
[]int64{1, 2, 3, 1},
},
{
"date object - yesterday",
pbtypes.String(addr.TimeToID(now.Add(-24 * time.Hour))),
pbtypes.String(date.TimeToDateId(now.Add(-24 * time.Hour))),
[]string{bundle.RelationKeyAddedDate.String(), bundle.RelationKeyCreatedDate.String()},
[]int64{1, 1},
},

View file

@ -2,13 +2,12 @@ package block
import (
"strings"
"time"
"github.com/globalsign/mgo/bson"
"github.com/anyproto/anytype-heart/core/block/import/notion/api"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/addr"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
dateUtil "github.com/anyproto/anytype-heart/util/date"
textUtil "github.com/anyproto/anytype-heart/util/text"
)
@ -240,7 +239,7 @@ func (t *TextObject) handleDateMention(rt api.RichText,
if rt.Mention.Date.End != "" {
textDate = rt.Mention.Date.End
}
date, err := time.Parse(DateMentionTimeFormat, textDate)
date, err := dateUtil.ParseDateId(textDate)
if err != nil {
return nil
}
@ -253,7 +252,7 @@ func (t *TextObject) handleDateMention(rt api.RichText,
To: int32(to),
},
Type: model.BlockContentTextMark_Mention,
Param: addr.TimeToID(date),
Param: dateUtil.TimeToDateId(date),
},
}
}

View file

@ -11,6 +11,7 @@ import (
"github.com/anyproto/anytype-heart/core/block/editor/lastused"
"github.com/anyproto/anytype-heart/core/block/editor/state"
"github.com/anyproto/anytype-heart/core/block/restriction"
"github.com/anyproto/anytype-heart/core/block/source"
"github.com/anyproto/anytype-heart/core/domain"
"github.com/anyproto/anytype-heart/pb"
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
@ -20,6 +21,7 @@ import (
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
"github.com/anyproto/anytype-heart/space"
"github.com/anyproto/anytype-heart/space/clientspace"
"github.com/anyproto/anytype-heart/util/date"
"github.com/anyproto/anytype-heart/util/internalflag"
"github.com/anyproto/anytype-heart/util/pbtypes"
)
@ -159,6 +161,8 @@ func (s *service) createObjectInSpace(
if pbtypes.GetString(details, bundle.RelationKeyTargetObjectType.String()) == "" {
return "", nil, fmt.Errorf("cannot create template without target object")
}
case bundle.TypeKeyDate:
return buildDateObject(space, details)
}
return s.createObjectFromTemplate(ctx, space, []domain.TypeKey{req.ObjectTypeKey}, details, req.TemplateId)
@ -177,3 +181,25 @@ func (s *service) createObjectFromTemplate(
}
return s.CreateSmartBlockFromStateInSpace(ctx, space, objectTypeKeys, createState)
}
// buildDateObject does not create real date object. It just builds date object details
func buildDateObject(space clientspace.Space, details *types.Struct) (string, *types.Struct, error) {
name := pbtypes.GetString(details, bundle.RelationKeyName.String())
id, err := date.DateNameToId(name)
if err != nil {
return "", nil, fmt.Errorf("failed to build date object, as its name is invalid: %w", err)
}
dateSource := source.NewDate(space, domain.FullID{
ObjectID: id,
SpaceID: space.Id(),
})
detailsGetter, ok := dateSource.(source.SourceIdEndodedDetails)
if !ok {
return "", nil, fmt.Errorf("date object does not implement DetailsFromId")
}
details, err = detailsGetter.DetailsFromId()
return id, details, err
}

View file

@ -3,6 +3,7 @@ package objectcreator
import (
"context"
"testing"
"time"
"github.com/gogo/protobuf/types"
"github.com/stretchr/testify/assert"
@ -15,6 +16,7 @@ import (
"github.com/anyproto/anytype-heart/space/clientspace"
"github.com/anyproto/anytype-heart/space/clientspace/mock_clientspace"
"github.com/anyproto/anytype-heart/space/mock_space"
"github.com/anyproto/anytype-heart/util/date"
"github.com/anyproto/anytype-heart/util/pbtypes"
)
@ -103,4 +105,45 @@ func TestService_CreateObject(t *testing.T) {
// then
assert.Error(t, err)
})
t.Run("date object creation", func(t *testing.T) {
// given
f := newFixture(t)
f.spaceService.EXPECT().Get(mock.Anything, mock.Anything).Return(f.spc, nil)
f.spc.EXPECT().Id().Return(spaceId)
ts := time.Now()
name := date.TimeToDateName(ts)
// when
id, details, err := f.service.CreateObject(context.Background(), spaceId, CreateObjectRequest{
ObjectTypeKey: bundle.TypeKeyDate,
Details: &types.Struct{Fields: map[string]*types.Value{
bundle.RelationKeyName.String(): pbtypes.String(name),
}},
})
// then
assert.NoError(t, err)
assert.Equal(t, date.TimeToDateId(ts), id)
assert.Equal(t, spaceId, pbtypes.GetString(details, bundle.RelationKeySpaceId.String()))
})
t.Run("date object creation - invalid name", func(t *testing.T) {
// given
f := newFixture(t)
f.spaceService.EXPECT().Get(mock.Anything, mock.Anything).Return(f.spc, nil)
ts := time.Now()
name := ts.Format(time.RFC3339)
// when
_, _, err := f.service.CreateObject(context.Background(), spaceId, CreateObjectRequest{
ObjectTypeKey: bundle.TypeKeyDate,
Details: &types.Struct{Fields: map[string]*types.Value{
bundle.RelationKeyName.String(): pbtypes.String(name),
}},
})
// then
assert.Error(t, err)
})
}

View file

@ -13,9 +13,9 @@ import (
"github.com/anyproto/anytype-heart/core/block/simple"
"github.com/anyproto/anytype-heart/core/domain"
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/addr"
"github.com/anyproto/anytype-heart/pkg/lib/logging"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
"github.com/anyproto/anytype-heart/util/date"
"github.com/anyproto/anytype-heart/util/pbtypes"
)
@ -152,7 +152,7 @@ func collectIdsFromDetail(rel *model.RelationLink, det *types.Struct, flags Flag
if relInt > 0 {
t := time.Unix(relInt, 0)
t = t.In(time.Local)
ids = append(ids, addr.TimeToID(t))
ids = append(ids, date.TimeToDateId(t))
}
return
}

View file

@ -3,7 +3,6 @@ package source
import (
"context"
"fmt"
"strings"
"time"
"github.com/gogo/protobuf/types"
@ -16,6 +15,7 @@ import (
"github.com/anyproto/anytype-heart/pkg/lib/core/smartblock"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/addr"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
dateutil "github.com/anyproto/anytype-heart/util/date"
"github.com/anyproto/anytype-heart/util/pbtypes"
)
@ -59,17 +59,14 @@ func (v *date) Type() smartblock.SmartBlockType {
return smartblock.SmartBlockTypeDate
}
func (v *date) getDetails(ctx context.Context) (*types.Struct, error) {
linksRelationId, err := v.space.GetRelationIdByKey(ctx, bundle.RelationKeyLinks)
func (v *date) getDetails(ctx context.Context, withType bool) (*types.Struct, error) {
t, err := dateutil.ParseDateId(v.id)
if err != nil {
return nil, fmt.Errorf("get links relation id: %w", err)
return nil, fmt.Errorf("failed to parse date id: %w", err)
}
dateTypeId, err := v.space.GetTypeIdByKey(ctx, bundle.TypeKeyDate)
if err != nil {
return nil, fmt.Errorf("get date type id: %w", err)
}
return &types.Struct{Fields: map[string]*types.Value{
bundle.RelationKeyName.String(): pbtypes.String(v.t.Format("02 Jan 2006")),
details := &types.Struct{Fields: map[string]*types.Value{
bundle.RelationKeyName.String(): pbtypes.String(dateutil.TimeToDateName(t)),
bundle.RelationKeyId.String(): pbtypes.String(v.id),
bundle.RelationKeyIsReadonly.String(): pbtypes.Bool(true),
bundle.RelationKeyIsArchived.String(): pbtypes.Bool(false),
@ -77,30 +74,28 @@ func (v *date) getDetails(ctx context.Context) (*types.Struct, error) {
bundle.RelationKeyLayout.String(): pbtypes.Float64(float64(model.ObjectType_date)),
bundle.RelationKeyIconEmoji.String(): pbtypes.String("📅"),
bundle.RelationKeySpaceId.String(): pbtypes.String(v.SpaceID()),
bundle.RelationKeySetOf.String(): pbtypes.StringList([]string{linksRelationId}),
bundle.RelationKeyType.String(): pbtypes.String(dateTypeId),
}}, nil
bundle.RelationKeyTimestamp.String(): pbtypes.Int64(t.Unix()),
}}
if withType {
if v.space == nil {
return nil, fmt.Errorf("get date type id: space is nil")
}
dateTypeId, err := v.space.GetTypeIdByKey(ctx, bundle.TypeKeyDate)
if err != nil {
return nil, fmt.Errorf("get date type id: %w", err)
}
details.Fields[bundle.RelationKeyType.String()] = pbtypes.String(dateTypeId)
}
return details, nil
}
// TODO Fix?
func (v *date) DetailsFromId() (*types.Struct, error) {
if err := v.parseId(); err != nil {
return nil, err
}
return &types.Struct{Fields: map[string]*types.Value{
bundle.RelationKeyName.String(): pbtypes.String(v.t.Format("02 Jan 2006")),
bundle.RelationKeyId.String(): pbtypes.String(v.id),
bundle.RelationKeyIsReadonly.String(): pbtypes.Bool(true),
bundle.RelationKeyIsArchived.String(): pbtypes.Bool(false),
bundle.RelationKeyIsHidden.String(): pbtypes.Bool(false),
bundle.RelationKeyLayout.String(): pbtypes.Float64(float64(model.ObjectType_date)),
bundle.RelationKeyIconEmoji.String(): pbtypes.String("📅"),
bundle.RelationKeySpaceId.String(): pbtypes.String(v.SpaceID()),
}}, nil
return v.getDetails(nil, false)
}
func (v *date) parseId() error {
t, err := time.Parse("2006-01-02", strings.TrimPrefix(v.id, addr.DatePrefix))
t, err := dateutil.ParseDateId(v.id)
if err != nil {
return err
}
@ -109,63 +104,15 @@ func (v *date) parseId() error {
}
func (v *date) ReadDoc(ctx context.Context, receiver ChangeReceiver, empty bool) (doc state.Doc, err error) {
if err = v.parseId(); err != nil {
return
}
s := state.NewDoc(v.id, nil).(*state.State)
d, err := v.getDetails(ctx)
d, err := v.getDetails(ctx, true)
if err != nil {
return
}
dataview := &model.BlockContentOfDataview{
Dataview: &model.BlockContentDataview{
RelationLinks: []*model.RelationLink{
{
Key: bundle.RelationKeyName.String(),
Format: model.RelationFormat_shorttext,
},
{
Key: bundle.RelationKeyLastModifiedDate.String(),
Format: model.RelationFormat_date,
},
},
Views: []*model.BlockContentDataviewView{
{
Id: "1",
Type: model.BlockContentDataviewView_Table,
Name: "Date backlinks",
Sorts: []*model.BlockContentDataviewSort{
{
RelationKey: bundle.RelationKeyLastModifiedDate.String(),
Type: model.BlockContentDataviewSort_Desc,
},
},
Filters: []*model.BlockContentDataviewFilter{
{
RelationKey: bundle.RelationKeyLinks.String(),
Condition: model.BlockContentDataviewFilter_In,
Value: pbtypes.String(v.id),
},
},
Relations: []*model.BlockContentDataviewRelation{
{
Key: bundle.RelationKeyName.String(),
IsVisible: true,
},
{
Key: bundle.RelationKeyLastModifiedDate.String(),
IsVisible: true,
},
},
},
},
},
}
s := state.NewDoc(v.id, nil).(*state.State)
template.InitTemplate(s,
template.WithTitle,
template.WithDefaultFeaturedRelations,
template.WithDataview(dataview, true),
template.WithAllBlocksEditsRestricted,
)
s.SetDetails(d)

146
core/date/suggest.go Normal file
View file

@ -0,0 +1,146 @@
package date
import (
"context"
"fmt"
"strconv"
"strings"
"time"
"github.com/anyproto/go-naturaldate/v2"
"github.com/araddon/dateparse"
"github.com/anyproto/anytype-heart/core/block/source"
"github.com/anyproto/anytype-heart/core/domain"
"github.com/anyproto/anytype-heart/pb"
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
"github.com/anyproto/anytype-heart/pkg/lib/database"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/objectstore"
"github.com/anyproto/anytype-heart/space"
dateutil "github.com/anyproto/anytype-heart/util/date"
)
func EnrichRecordsWithDateSuggestion(
ctx context.Context,
records []database.Record,
req *pb.RpcObjectSearchRequest,
store objectstore.ObjectStore,
spaceService space.Service,
) ([]database.Record, error) {
dt := suggestDateForSearch(time.Now(), req.FullText)
if dt.IsZero() {
return records, nil
}
id := dateutil.TimeToDateId(dt)
// Don't duplicate search suggestions
var found bool
for _, r := range records {
if r.Details == nil || r.Details.Fields == nil {
continue
}
if v, ok := r.Details.Fields[bundle.RelationKeyId.String()]; ok {
if v.GetStringValue() == id {
found = true
break
}
}
}
if found {
return records, nil
}
spc, err := spaceService.Get(ctx, req.SpaceId)
if err != nil {
return nil, fmt.Errorf("get space: %w", err)
}
rec, err := makeSuggestedDateRecord(spc, dt)
if err != nil {
return nil, fmt.Errorf("make date record: %w", err)
}
f, _ := database.MakeFilters(req.Filters, store.SpaceIndex(req.SpaceId)) //nolint:errcheck
if f.FilterObject(rec.Details) {
return append([]database.Record{rec}, records...), nil
}
return records, nil
}
func suggestDateForSearch(now time.Time, raw string) time.Time {
suggesters := []func() time.Time{
func() time.Time {
var exprType naturaldate.ExprType
t, exprType, err := naturaldate.Parse(raw, now)
if err != nil {
return time.Time{}
}
if exprType == naturaldate.ExprTypeInvalid {
return time.Time{}
}
// naturaldate parses numbers without qualifiers (m,s) as hours in 24 hours clock format. It leads to weird behavior
// when inputs like "123" represented as "current time + 123 hours"
if (exprType & naturaldate.ExprTypeClock24Hour) != 0 {
t = time.Time{}
}
return t
},
func() time.Time {
// Don't use plain numbers, because they will be represented as years
if _, err := strconv.Atoi(strings.TrimSpace(raw)); err == nil {
return time.Time{}
}
// todo: use system locale to get preferred date format
t, err := dateparse.ParseIn(raw, now.Location(), dateparse.PreferMonthFirst(false))
if err != nil {
return time.Time{}
}
return t
},
}
var t time.Time
for _, s := range suggesters {
if t = s(); !t.IsZero() {
break
}
}
if t.IsZero() {
return t
}
// Sanitize date
// Date without year
if t.Year() == 0 {
_, month, day := t.Date()
h, m, s := t.Clock()
t = time.Date(now.Year(), month, day, h, m, s, 0, t.Location())
}
return t
}
func makeSuggestedDateRecord(spc source.Space, t time.Time) (database.Record, error) {
id := dateutil.TimeToDateId(t)
dateSource := source.NewDate(spc, domain.FullID{
ObjectID: id,
SpaceID: spc.Id(),
})
v, ok := dateSource.(source.SourceIdEndodedDetails)
if !ok {
return database.Record{}, fmt.Errorf("source does not implement DetailsFromId")
}
details, err := v.DetailsFromId()
if err != nil {
return database.Record{}, err
}
return database.Record{
Details: details,
}, nil
}

View file

@ -1,4 +1,4 @@
package core
package date
import (
"testing"

View file

@ -4,12 +4,7 @@ import (
"context"
"errors"
"fmt"
"strconv"
"strings"
"time"
"github.com/anyproto/go-naturaldate/v2"
"github.com/araddon/dateparse"
"github.com/gogo/protobuf/types"
"github.com/hashicorp/go-multierror"
@ -17,12 +12,12 @@ import (
importer "github.com/anyproto/anytype-heart/core/block/import"
"github.com/anyproto/anytype-heart/core/block/import/common"
"github.com/anyproto/anytype-heart/core/block/object/objectgraph"
"github.com/anyproto/anytype-heart/core/date"
"github.com/anyproto/anytype-heart/core/domain/objectorigin"
"github.com/anyproto/anytype-heart/core/indexer"
"github.com/anyproto/anytype-heart/core/subscription"
"github.com/anyproto/anytype-heart/core/subscription/crossspacesub"
"github.com/anyproto/anytype-heart/pb"
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
"github.com/anyproto/anytype-heart/pkg/lib/database"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/ftsearch"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/objectstore"
@ -108,7 +103,7 @@ func (mw *Middleware) ObjectSearch(cctx context.Context, req *pb.RpcObjectSearch
// Add dates only to the first page of search results
if req.Offset == 0 {
records, err = mw.enrichWithDateSuggestion(cctx, records, req, ds)
records, err = date.EnrichRecordsWithDateSuggestion(cctx, records, req, ds, getService[space.Service](mw))
if err != nil {
return response(pb.RpcObjectSearchResponseError_UNKNOWN_ERROR, nil, err)
}
@ -174,127 +169,6 @@ func (mw *Middleware) ObjectSearchWithMeta(cctx context.Context, req *pb.RpcObje
return response(pb.RpcObjectSearchWithMetaResponseError_NULL, resultsModels, nil)
}
func (mw *Middleware) enrichWithDateSuggestion(ctx context.Context, records []database.Record, req *pb.RpcObjectSearchRequest, store objectstore.ObjectStore) ([]database.Record, error) {
dt := suggestDateForSearch(time.Now(), req.FullText)
if dt.IsZero() {
return records, nil
}
id := deriveDateId(dt)
// Don't duplicate search suggestions
var found bool
for _, r := range records {
if r.Details == nil || r.Details.Fields == nil {
continue
}
if v, ok := r.Details.Fields[bundle.RelationKeyId.String()]; ok {
if v.GetStringValue() == id {
found = true
break
}
}
}
if found {
return records, nil
}
var rec database.Record
rec, err := mw.makeSuggestedDateRecord(ctx, req.SpaceId, dt)
if err != nil {
return nil, fmt.Errorf("make date record: %w", err)
}
f, _ := database.MakeFilters(req.Filters, store.SpaceIndex(req.SpaceId)) //nolint:errcheck
if f.FilterObject(rec.Details) {
return append([]database.Record{rec}, records...), nil
}
return records, nil
}
func suggestDateForSearch(now time.Time, raw string) time.Time {
suggesters := []func() time.Time{
func() time.Time {
var exprType naturaldate.ExprType
t, exprType, err := naturaldate.Parse(raw, now)
if err != nil {
return time.Time{}
}
if exprType == naturaldate.ExprTypeInvalid {
return time.Time{}
}
// naturaldate parses numbers without qualifiers (m,s) as hours in 24 hours clock format. It leads to weird behavior
// when inputs like "123" represented as "current time + 123 hours"
if (exprType & naturaldate.ExprTypeClock24Hour) != 0 {
t = time.Time{}
}
return t
},
func() time.Time {
// Don't use plain numbers, because they will be represented as years
if _, err := strconv.Atoi(strings.TrimSpace(raw)); err == nil {
return time.Time{}
}
// todo: use system locale to get preferred date format
t, err := dateparse.ParseIn(raw, now.Location(), dateparse.PreferMonthFirst(false))
if err != nil {
return time.Time{}
}
return t
},
}
var t time.Time
for _, s := range suggesters {
if t = s(); !t.IsZero() {
break
}
}
if t.IsZero() {
return t
}
// Sanitize date
// Date without year
if t.Year() == 0 {
_, month, day := t.Date()
h, m, s := t.Clock()
t = time.Date(now.Year(), month, day, h, m, s, 0, t.Location())
}
return t
}
func deriveDateId(t time.Time) string {
return "_date_" + t.Format("2006-01-02")
}
func (mw *Middleware) makeSuggestedDateRecord(ctx context.Context, spaceID string, t time.Time) (database.Record, error) {
id := deriveDateId(t)
spc, err := getService[space.Service](mw).Get(ctx, spaceID)
if err != nil {
return database.Record{}, fmt.Errorf("get space: %w", err)
}
typeId, err := spc.GetTypeIdByKey(ctx, bundle.TypeKeyDate)
if err != nil {
return database.Record{}, fmt.Errorf("get date type id: %w", err)
}
d := &types.Struct{Fields: map[string]*types.Value{
bundle.RelationKeyId.String(): pbtypes.String(id),
bundle.RelationKeyName.String(): pbtypes.String(t.Format("02 Jan 2006")),
bundle.RelationKeyLayout.String(): pbtypes.Int64(int64(model.ObjectType_date)),
bundle.RelationKeyType.String(): pbtypes.String(typeId),
bundle.RelationKeyIconEmoji.String(): pbtypes.String("📅"),
bundle.RelationKeySpaceId.String(): pbtypes.String(spaceID),
}}
return database.Record{
Details: d,
}, nil
}
func (mw *Middleware) ObjectSearchSubscribe(cctx context.Context, req *pb.RpcObjectSearchSubscribeRequest) *pb.RpcObjectSearchSubscribeResponse {
errResponse := func(err error) *pb.RpcObjectSearchSubscribeResponse {
r := &pb.RpcObjectSearchSubscribeResponse{

View file

@ -9,7 +9,7 @@ import (
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
)
const RelationChecksum = "414402e104d3b92fa6045649ffa60f6988979f427cf91ef678c53257a0d83fc4"
const RelationChecksum = "d020435326985f2804482fd51f0a5fde92c007683e42a11ce26193c9b2876033"
const (
RelationKeyTag domain.RelationKey = "tag"
RelationKeyCamera domain.RelationKey = "camera"
@ -143,6 +143,7 @@ const (
RelationKeyHasChat domain.RelationKey = "hasChat"
RelationKeyChatId domain.RelationKey = "chatId"
RelationKeyMentions domain.RelationKey = "mentions"
RelationKeyTimestamp domain.RelationKey = "timestamp"
)
var (
@ -1847,6 +1848,19 @@ var (
ReadOnlyRelation: true,
Scope: model.Relation_type,
},
RelationKeyTimestamp: {
DataSource: model.Relation_derived,
Description: "Unix time representation of date object",
Format: model.RelationFormat_date,
Hidden: true,
Id: "_brtimestamp",
Key: "timestamp",
Name: "Timestamp",
ReadOnly: true,
ReadOnlyRelation: true,
Scope: model.Relation_type,
},
RelationKeyToBeDeletedDate: {
DataSource: model.Relation_account,

View file

@ -1350,5 +1350,15 @@
"name": "Mentions",
"readonly": true,
"source": "local"
},
{
"description": "Unix time representation of date object",
"format": "date",
"hidden": true,
"key": "timestamp",
"maxCount": 0,
"name": "Timestamp",
"readonly": true,
"source": "derived"
}
]

View file

@ -6,7 +6,7 @@ package bundle
import domain "github.com/anyproto/anytype-heart/core/domain"
const SystemRelationsChecksum = "dccec4608e8b4d207055a77d1e475598b6ed9ef177d0d796065ebc6b9e0466f7"
const SystemRelationsChecksum = "cebd4ab7522c2ca901215e20861e005c1ad1a6ca2a77d7c1205e9e51edd901db"
// SystemRelations contains relations that have some special biz logic depends on them in some objects
// in case EVERY object depend on the relation please add it to RequiredInternalRelations
@ -81,4 +81,5 @@ var SystemRelations = append(RequiredInternalRelations, []domain.RelationKey{
RelationKeyMentions,
RelationKeyChatId,
RelationKeyHasChat,
RelationKeyTimestamp,
}...)

View file

@ -89,5 +89,6 @@
"lastUsedDate",
"mentions",
"chatId",
"hasChat"
"hasChat",
"timestamp"
]

View file

@ -3,7 +3,6 @@ package addr
import (
"fmt"
"strings"
"time"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
)
@ -44,7 +43,3 @@ func ExtractVirtualSourceType(id string) (model.SmartBlockType, error) {
}
return 0, fmt.Errorf("sb type '%s' not found", sbTypeName)
}
func TimeToID(t time.Time) string {
return DatePrefix + t.Format("2006-01-02")
}

33
util/date/util.go Normal file
View file

@ -0,0 +1,33 @@
package date
import (
"strings"
"time"
"github.com/anyproto/anytype-heart/pkg/lib/localstore/addr"
)
const (
dateIdLayout = "2006-01-02"
dateNameLayout = "02 Jan 2006"
)
func TimeToDateId(t time.Time) string {
return addr.DatePrefix + t.Format(dateIdLayout)
}
func ParseDateId(id string) (time.Time, error) {
return time.Parse(dateIdLayout, strings.TrimPrefix(id, addr.DatePrefix))
}
func TimeToDateName(t time.Time) string {
return t.Format(dateNameLayout)
}
func DateNameToId(name string) (string, error) {
t, err := time.Parse(dateNameLayout, name)
if err != nil {
return "", err
}
return TimeToDateId(t), nil
}