Timeline improvements (#41)
Tidying up. Parent/child statuses now display correctly in status/id/context.
This commit is contained in:
@ -10,41 +10,6 @@ import (
|
||||
)
|
||||
|
||||
func (t *timeline) IndexBefore(statusID string, include bool, amount int) error {
|
||||
// filtered := []*gtsmodel.Status{}
|
||||
// offsetStatus := statusID
|
||||
|
||||
// grabloop:
|
||||
// for len(filtered) < amount {
|
||||
// statuses, err := t.db.GetStatusesWhereFollowing(t.accountID, amount, offsetStatus, include, true)
|
||||
// if err != nil {
|
||||
// if _, ok := err.(db.ErrNoEntries); !ok {
|
||||
// return fmt.Errorf("IndexBeforeAndIncluding: error getting statuses from db: %s", err)
|
||||
// }
|
||||
// break grabloop // we just don't have enough statuses left in the db so index what we've got and then bail
|
||||
// }
|
||||
|
||||
// for _, s := range statuses {
|
||||
// relevantAccounts, err := t.db.PullRelevantAccountsFromStatus(s)
|
||||
// if err != nil {
|
||||
// continue
|
||||
// }
|
||||
// visible, err := t.db.StatusVisible(s, t.account, relevantAccounts)
|
||||
// if err != nil {
|
||||
// continue
|
||||
// }
|
||||
// if visible {
|
||||
// filtered = append(filtered, s)
|
||||
// }
|
||||
// offsetStatus = s.ID
|
||||
// }
|
||||
// }
|
||||
|
||||
// for _, s := range filtered {
|
||||
// if err := t.IndexOne(s.CreatedAt, s.ID); err != nil {
|
||||
// return fmt.Errorf("IndexBeforeAndIncluding: error indexing status with id %s: %s", s.ID, err)
|
||||
// }
|
||||
// }
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -63,15 +28,11 @@ grabloop:
|
||||
}
|
||||
|
||||
for _, s := range statuses {
|
||||
relevantAccounts, err := t.db.PullRelevantAccountsFromStatus(s)
|
||||
timelineable, err := t.filter.StatusHometimelineable(s, t.account)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
visible, err := t.db.StatusVisible(s, t.account, relevantAccounts)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if visible {
|
||||
if timelineable {
|
||||
filtered = append(filtered, s)
|
||||
}
|
||||
offsetStatus = s.ID
|
||||
@ -79,7 +40,7 @@ grabloop:
|
||||
}
|
||||
|
||||
for _, s := range filtered {
|
||||
if err := t.IndexOne(s.CreatedAt, s.ID); err != nil {
|
||||
if err := t.IndexOne(s.CreatedAt, s.ID, s.BoostOfID); err != nil {
|
||||
return fmt.Errorf("IndexBehindAndIncluding: error indexing status with id %s: %s", s.ID, err)
|
||||
}
|
||||
}
|
||||
@ -91,12 +52,13 @@ func (t *timeline) IndexOneByID(statusID string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *timeline) IndexOne(statusCreatedAt time.Time, statusID string) error {
|
||||
func (t *timeline) IndexOne(statusCreatedAt time.Time, statusID string, boostOfID string) error {
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
|
||||
postIndexEntry := &postIndexEntry{
|
||||
statusID: statusID,
|
||||
statusID: statusID,
|
||||
boostOfID: boostOfID,
|
||||
}
|
||||
|
||||
return t.postIndex.insertIndexed(postIndexEntry)
|
||||
|
||||
@ -105,7 +105,7 @@ func (m *manager) Ingest(status *gtsmodel.Status, timelineAccountID string) erro
|
||||
t := m.getOrCreateTimeline(timelineAccountID)
|
||||
|
||||
l.Trace("ingesting status")
|
||||
return t.IndexOne(status.CreatedAt, status.ID)
|
||||
return t.IndexOne(status.CreatedAt, status.ID, status.BoostOfID)
|
||||
}
|
||||
|
||||
func (m *manager) IngestAndPrepare(status *gtsmodel.Status, timelineAccountID string) error {
|
||||
|
||||
@ -10,7 +10,8 @@ type postIndex struct {
|
||||
}
|
||||
|
||||
type postIndexEntry struct {
|
||||
statusID string
|
||||
statusID string
|
||||
boostOfID string
|
||||
}
|
||||
|
||||
func (p *postIndex) insertIndexed(i *postIndexEntry) error {
|
||||
@ -25,14 +26,26 @@ func (p *postIndex) insertIndexed(i *postIndexEntry) error {
|
||||
}
|
||||
|
||||
var insertMark *list.Element
|
||||
var position int
|
||||
// We need to iterate through the index to make sure we put this post in the appropriate place according to when it was created.
|
||||
// We also need to make sure we're not inserting a duplicate post -- this can happen sometimes and it's not nice UX (*shudder*).
|
||||
for e := p.data.Front(); e != nil; e = e.Next() {
|
||||
position = position + 1
|
||||
|
||||
entry, ok := e.Value.(*postIndexEntry)
|
||||
if !ok {
|
||||
return errors.New("index: could not parse e as a postIndexEntry")
|
||||
}
|
||||
|
||||
// don't insert this if it's a boost of a status we've seen recently
|
||||
if i.boostOfID != "" {
|
||||
if i.boostOfID == entry.boostOfID || i.boostOfID == entry.statusID {
|
||||
if position < boostReinsertionDepth {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if the post to index is newer than e, insert it before e in the list
|
||||
if insertMark == nil {
|
||||
if i.statusID > entry.statusID {
|
||||
|
||||
@ -163,24 +163,8 @@ func (t *timeline) prepare(statusID string) error {
|
||||
t.account = timelineOwnerAccount
|
||||
}
|
||||
|
||||
// to convert the status we need relevant accounts from it, so pull them out here
|
||||
relevantAccounts, err := t.db.PullRelevantAccountsFromStatus(gtsStatus)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// check if this is a boost...
|
||||
var reblogOfStatus *gtsmodel.Status
|
||||
if gtsStatus.BoostOfID != "" {
|
||||
s := >smodel.Status{}
|
||||
if err := t.db.GetByID(gtsStatus.BoostOfID, s); err != nil {
|
||||
return err
|
||||
}
|
||||
reblogOfStatus = s
|
||||
}
|
||||
|
||||
// serialize the status (or, at least, convert it to a form that's ready to be serialized)
|
||||
apiModelStatus, err := t.tc.StatusToMasto(gtsStatus, relevantAccounts.StatusAuthor, t.account, relevantAccounts.BoostedAccount, relevantAccounts.ReplyToAccount, reblogOfStatus)
|
||||
apiModelStatus, err := t.tc.StatusToMasto(gtsStatus, t.account)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -28,14 +28,32 @@ func (p *preparedPosts) insertPrepared(i *preparedPostsEntry) error {
|
||||
}
|
||||
|
||||
var insertMark *list.Element
|
||||
var position int
|
||||
// We need to iterate through the index to make sure we put this post in the appropriate place according to when it was created.
|
||||
// We also need to make sure we're not inserting a duplicate post -- this can happen sometimes and it's not nice UX (*shudder*).
|
||||
for e := p.data.Front(); e != nil; e = e.Next() {
|
||||
position = position + 1
|
||||
|
||||
entry, ok := e.Value.(*preparedPostsEntry)
|
||||
if !ok {
|
||||
return errors.New("index: could not parse e as a preparedPostsEntry")
|
||||
}
|
||||
|
||||
// don't insert this if it's a boost of a status we've seen recently
|
||||
if i.prepared.Reblog != nil {
|
||||
if entry.prepared.Reblog != nil && i.prepared.Reblog.ID == entry.prepared.Reblog.ID {
|
||||
if position < boostReinsertionDepth {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if i.prepared.Reblog.ID == entry.statusID {
|
||||
if position < boostReinsertionDepth {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if the post to index is newer than e, insert it before e in the list
|
||||
if insertMark == nil {
|
||||
if i.statusID > entry.statusID {
|
||||
|
||||
@ -27,8 +27,11 @@ import (
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
)
|
||||
|
||||
const boostReinsertionDepth = 50
|
||||
|
||||
// Timeline represents a timeline for one account, and contains indexed and prepared posts.
|
||||
type Timeline interface {
|
||||
/*
|
||||
@ -59,7 +62,7 @@ type Timeline interface {
|
||||
*/
|
||||
|
||||
// IndexOne puts a status into the timeline at the appropriate place according to its 'createdAt' property.
|
||||
IndexOne(statusCreatedAt time.Time, statusID string) error
|
||||
IndexOne(statusCreatedAt time.Time, statusID string, boostOfID string) error
|
||||
|
||||
// OldestIndexedPostID returns the id of the rearmost (ie., the oldest) indexed post, or an error if something goes wrong.
|
||||
// If nothing goes wrong but there's no oldest post, an empty string will be returned so make sure to check for this.
|
||||
@ -109,6 +112,7 @@ type timeline struct {
|
||||
accountID string
|
||||
account *gtsmodel.Account
|
||||
db db.DB
|
||||
filter visibility.Filter
|
||||
tc typeutils.TypeConverter
|
||||
log *logrus.Logger
|
||||
sync.Mutex
|
||||
@ -121,6 +125,7 @@ func NewTimeline(accountID string, db db.DB, typeConverter typeutils.TypeConvert
|
||||
preparedPosts: &preparedPosts{},
|
||||
accountID: accountID,
|
||||
db: db,
|
||||
filter: visibility.NewFilter(db, log),
|
||||
tc: typeConverter,
|
||||
log: log,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user