timelines working pretty alright!
This commit is contained in:
parent
d9d9a7a626
commit
a25e53af4e
@ -30,7 +30,7 @@ import (
|
||||
// WebfingerGETRequest handles requests to, for example, https://example.org/.well-known/webfinger?resource=acct:some_user@example.org
|
||||
func (m *Module) WebfingerGETRequest(c *gin.Context) {
|
||||
l := m.log.WithFields(logrus.Fields{
|
||||
"func": "WebfingerGETRequest",
|
||||
"func": "WebfingerGETRequest",
|
||||
"user-agent": c.Request.UserAgent(),
|
||||
})
|
||||
|
||||
|
@ -1165,33 +1165,19 @@ func (ps *postgresService) GetStatusesWhereFollowing(accountID string, maxID str
|
||||
|
||||
q = q.ColumnExpr("status.*").
|
||||
Join("JOIN follows AS f ON f.target_account_id = status.account_id").
|
||||
Where("f.account_id = ?", accountID)
|
||||
Where("f.account_id = ?", accountID).
|
||||
Order("status.id DESC")
|
||||
|
||||
if maxID != "" {
|
||||
s := >smodel.Status{}
|
||||
if err := ps.conn.Model(s).Where("id = ?", maxID).Select(); err == nil {
|
||||
q = q.Where("status.created_at < ?", s.CreatedAt)
|
||||
}
|
||||
q = q.Where("status.id < ?", maxID)
|
||||
}
|
||||
|
||||
if sinceID != "" {
|
||||
s := >smodel.Status{}
|
||||
if err := ps.conn.Model(s).Where("id = ?", sinceID).Select(); err == nil {
|
||||
q = q.Where("status.created_at > ?", s.CreatedAt)
|
||||
}
|
||||
q = q.Where("status.id > ?", sinceID)
|
||||
}
|
||||
|
||||
if minID != "" {
|
||||
s := >smodel.Status{}
|
||||
if err := ps.conn.Model(s).Where("id = ?", minID).Select(); err == nil {
|
||||
q = q.Where("status.created_at > ?", s.CreatedAt)
|
||||
}
|
||||
}
|
||||
|
||||
if minID != "" {
|
||||
q = q.Order("status.created_at")
|
||||
} else {
|
||||
q = q.Order("status.created_at DESC")
|
||||
q = q.Where("status.id > ?", minID)
|
||||
}
|
||||
|
||||
if local {
|
||||
@ -1204,9 +1190,14 @@ func (ps *postgresService) GetStatusesWhereFollowing(accountID string, maxID str
|
||||
|
||||
err := q.Select()
|
||||
if err != nil {
|
||||
if err != pg.ErrNoRows {
|
||||
return nil, err
|
||||
if err == pg.ErrNoRows {
|
||||
return nil, db.ErrNoEntries{}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(statuses) == 0 {
|
||||
return nil, db.ErrNoEntries{}
|
||||
}
|
||||
|
||||
return statuses, nil
|
||||
@ -1219,42 +1210,34 @@ func (ps *postgresService) GetPublicTimelineForAccount(accountID string, maxID s
|
||||
Where("visibility = ?", gtsmodel.VisibilityPublic).
|
||||
Where("? IS NULL", pg.Ident("in_reply_to_id")).
|
||||
Where("? IS NULL", pg.Ident("boost_of_id")).
|
||||
Limit(limit).
|
||||
Order("created_at DESC")
|
||||
Order("status.id DESC")
|
||||
|
||||
if maxID != "" {
|
||||
s := >smodel.Status{}
|
||||
if err := ps.conn.Model(s).Where("id = ?", maxID).Select(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
q = q.Where("created_at < ?", s.CreatedAt)
|
||||
}
|
||||
|
||||
if minID != "" {
|
||||
s := >smodel.Status{}
|
||||
if err := ps.conn.Model(s).Where("id = ?", minID).Select(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
q = q.Where("created_at > ?", s.CreatedAt)
|
||||
q = q.Where("status.id < ?", maxID)
|
||||
}
|
||||
|
||||
if sinceID != "" {
|
||||
s := >smodel.Status{}
|
||||
if err := ps.conn.Model(s).Where("id = ?", sinceID).Select(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
q = q.Where("created_at > ?", s.CreatedAt)
|
||||
q = q.Where("status.id > ?", sinceID)
|
||||
}
|
||||
|
||||
if minID != "" {
|
||||
q = q.Where("status.id > ?", minID)
|
||||
}
|
||||
|
||||
if local {
|
||||
q = q.Where("local = ?", local)
|
||||
q = q.Where("status.local = ?", local)
|
||||
}
|
||||
|
||||
if limit > 0 {
|
||||
q = q.Limit(limit)
|
||||
}
|
||||
|
||||
err := q.Select()
|
||||
if err != nil {
|
||||
if err != pg.ErrNoRows {
|
||||
return nil, err
|
||||
if err == pg.ErrNoRows {
|
||||
return nil, db.ErrNoEntries{}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return statuses, nil
|
||||
@ -1266,19 +1249,11 @@ func (ps *postgresService) GetNotificationsForAccount(accountID string, limit in
|
||||
q := ps.conn.Model(¬ifications).Where("target_account_id = ?", accountID)
|
||||
|
||||
if maxID != "" {
|
||||
n := >smodel.Notification{}
|
||||
if err := ps.conn.Model(n).Where("id = ?", maxID).Select(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
q = q.Where("created_at < ?", n.CreatedAt)
|
||||
q = q.Where("id < ?", maxID)
|
||||
}
|
||||
|
||||
if sinceID != "" {
|
||||
n := >smodel.Notification{}
|
||||
if err := ps.conn.Model(n).Where("id = ?", sinceID).Select(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
q = q.Where("created_at > ?", n.CreatedAt)
|
||||
q = q.Where("id > ?", sinceID)
|
||||
}
|
||||
|
||||
if limit != 0 {
|
||||
|
@ -95,7 +95,7 @@ func (f *federatingDB) Accept(ctx context.Context, accept vocab.ActivityStreamsA
|
||||
continue
|
||||
}
|
||||
switch iter.GetType().GetTypeName() {
|
||||
// we have the whole object so we can figure out what we're accepting
|
||||
// we have the whole object so we can figure out what we're accepting
|
||||
case string(gtsmodel.ActivityStreamsFollow):
|
||||
// ACCEPT FOLLOW
|
||||
asFollow, ok := iter.GetType().(vocab.ActivityStreamsFollow)
|
||||
|
@ -21,9 +21,7 @@ package processing
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
@ -38,80 +36,14 @@ func (p *processor) HomeTimelineGet(authed *oauth.Auth, maxID string, sinceID st
|
||||
Statuses: []*apimodel.Status{},
|
||||
}
|
||||
|
||||
apiStatuses := []*apimodel.Status{}
|
||||
|
||||
maxIDMarker := maxID
|
||||
sinceIDMarker := sinceID
|
||||
minIDMarker := minID
|
||||
|
||||
gtsStatuses, err := p.db.GetStatusesWhereFollowing(authed.Account.ID, maxIDMarker, sinceIDMarker, minIDMarker, limit, local)
|
||||
apiStatuses, err := p.timelineManager.HomeTimeline(authed.Account.ID, maxID, sinceID, minID, limit, local)
|
||||
if err != nil {
|
||||
if _, ok := err.(db.ErrNoEntries); !ok {
|
||||
return nil, gtserror.NewErrorInternalError(fmt.Errorf("HomeTimelineGet: error getting statuses from db: %s", err))
|
||||
}
|
||||
return nil, gtserror.NewErrorInternalError(err)
|
||||
}
|
||||
|
||||
for _, gtsStatus := range gtsStatuses {
|
||||
// pull relevant accounts from the status -- we need this both for checking visibility and for serializing
|
||||
relevantAccounts, err := p.db.PullRelevantAccountsFromStatus(gtsStatus)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
visible, err := p.db.StatusVisible(gtsStatus, authed.Account, relevantAccounts)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if visible {
|
||||
// check if this is a boost...
|
||||
var reblogOfStatus *gtsmodel.Status
|
||||
if gtsStatus.BoostOfID != "" {
|
||||
s := >smodel.Status{}
|
||||
if err := p.db.GetByID(s.BoostOfID, s); err != nil {
|
||||
continue
|
||||
}
|
||||
reblogOfStatus = s
|
||||
}
|
||||
|
||||
// serialize the status (or, at least, convert it to a form that's ready to be serialized)
|
||||
apiStatus, err := p.tc.StatusToMasto(gtsStatus, relevantAccounts.StatusAuthor, authed.Account, relevantAccounts.BoostedAccount, relevantAccounts.ReplyToAccount, reblogOfStatus)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
apiStatuses = append(apiStatuses, apiStatus)
|
||||
sort.Slice(apiStatuses, func(i int, j int) bool {
|
||||
is, err := time.Parse(time.RFC3339, apiStatuses[i].CreatedAt)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
js, err := time.Parse(time.RFC3339, apiStatuses[j].CreatedAt)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return is.After(js)
|
||||
})
|
||||
|
||||
if len(apiStatuses) == limit {
|
||||
// we have enough
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(apiStatuses) != 0 {
|
||||
if maxIDMarker != "" {
|
||||
maxIDMarker = apiStatuses[len(apiStatuses)-1].ID
|
||||
}
|
||||
if minIDMarker != "" {
|
||||
minIDMarker = apiStatuses[0].ID
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resp.Statuses = apiStatuses
|
||||
|
||||
if len(resp.Statuses) != 0 {
|
||||
// prepare the next and previous links
|
||||
if len(apiStatuses) != 0 {
|
||||
nextLink := &url.URL{
|
||||
Scheme: p.config.Protocol,
|
||||
Host: p.config.Host,
|
||||
@ -259,7 +191,9 @@ func (p *processor) initTimelineFor(account *gtsmodel.Account, wg *sync.WaitGrou
|
||||
|
||||
statuses, err := p.db.GetStatusesWhereFollowing(account.ID, "", "", "", desiredIndexLength, false)
|
||||
if err != nil {
|
||||
l.Error(fmt.Errorf("initTimelineFor: error getting statuses: %s", err))
|
||||
if _, ok := err.(db.ErrNoEntries); !ok {
|
||||
l.Error(fmt.Errorf("initTimelineFor: error getting statuses: %s", err))
|
||||
}
|
||||
return
|
||||
}
|
||||
p.indexAndIngest(statuses, account, desiredIndexLength)
|
||||
|
@ -8,11 +8,53 @@ import (
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
)
|
||||
|
||||
func (t *timeline) Get(amount int, maxID string, sinceID string, minID string) ([]*apimodel.Status, error) {
|
||||
var statuses []*apimodel.Status
|
||||
var err error
|
||||
|
||||
// no params are defined to just fetch from the top
|
||||
if maxID == "" && sinceID == "" && minID == "" {
|
||||
statuses, err = t.GetXFromTop(amount)
|
||||
// aysnchronously prepare the next predicted query so it's ready when the user asks for it
|
||||
if len(statuses) != 0 {
|
||||
nextMaxID := statuses[len(statuses)-1].ID
|
||||
go t.prepareNextQuery(amount, nextMaxID, "", "")
|
||||
}
|
||||
}
|
||||
|
||||
// maxID is defined but sinceID isn't so take from behind
|
||||
if maxID != "" && sinceID == "" {
|
||||
statuses, err = t.GetXBehindID(amount, maxID)
|
||||
// aysnchronously prepare the next predicted query so it's ready when the user asks for it
|
||||
if len(statuses) != 0 {
|
||||
nextMaxID := statuses[len(statuses)-1].ID
|
||||
go t.prepareNextQuery(amount, nextMaxID, "", "")
|
||||
}
|
||||
}
|
||||
|
||||
// maxID is defined and sinceID || minID are as well, so take a slice between them
|
||||
if maxID != "" && sinceID != "" {
|
||||
statuses, err = t.GetXBetweenID(amount, maxID, minID)
|
||||
}
|
||||
if maxID != "" && minID != "" {
|
||||
statuses, err = t.GetXBetweenID(amount, maxID, minID)
|
||||
}
|
||||
|
||||
// maxID isn't defined, but sinceID || minID are, so take x before
|
||||
if maxID == "" && sinceID != "" {
|
||||
statuses, err = t.GetXBeforeID(amount, sinceID, true)
|
||||
}
|
||||
if maxID == "" && minID != "" {
|
||||
statuses, err = t.GetXBeforeID(amount, minID, true)
|
||||
}
|
||||
|
||||
return statuses, err
|
||||
}
|
||||
|
||||
func (t *timeline) GetXFromTop(amount int) ([]*apimodel.Status, error) {
|
||||
// make a slice of statuses with the length we need to return
|
||||
statuses := make([]*apimodel.Status, 0, amount)
|
||||
|
||||
// if there are no prepared posts, just return the empty slice
|
||||
if t.preparedPosts.data == nil {
|
||||
t.preparedPosts.data = &list.List{}
|
||||
}
|
||||
@ -45,7 +87,6 @@ func (t *timeline) GetXBehindID(amount int, behindID string) ([]*apimodel.Status
|
||||
// make a slice of statuses with the length we need to return
|
||||
statuses := make([]*apimodel.Status, 0, amount)
|
||||
|
||||
// if there are no prepared posts, just return the empty slice
|
||||
if t.preparedPosts.data == nil {
|
||||
t.preparedPosts.data = &list.List{}
|
||||
}
|
||||
@ -53,6 +94,7 @@ func (t *timeline) GetXBehindID(amount int, behindID string) ([]*apimodel.Status
|
||||
// iterate through the modified list until we hit the mark we're looking for
|
||||
var position int
|
||||
var behindIDMark *list.Element
|
||||
|
||||
findMarkLoop:
|
||||
for e := t.preparedPosts.data.Front(); e != nil; e = e.Next() {
|
||||
position = position + 1
|
||||
@ -62,7 +104,6 @@ findMarkLoop:
|
||||
}
|
||||
|
||||
if entry.statusID == behindID {
|
||||
fmt.Printf("\n\n\n GETXBEHINDID: FOUND BEHINDID %s WITH POSITION %d AND CREATEDAT %s \n\n\n", behindID, position, entry.createdAt.String())
|
||||
behindIDMark = e
|
||||
break findMarkLoop
|
||||
}
|
||||
@ -70,22 +111,29 @@ findMarkLoop:
|
||||
|
||||
// we didn't find it, so we need to make sure it's indexed and prepared and then try again
|
||||
if behindIDMark == nil {
|
||||
if err := t.IndexBehind(behindID, true, amount); err != nil {
|
||||
if err := t.IndexBehind(behindID, amount); err != nil {
|
||||
return nil, fmt.Errorf("GetXBehindID: error indexing behind and including ID %s", behindID)
|
||||
}
|
||||
if err := t.PrepareBehind(behindID, true, amount); err != nil {
|
||||
if err := t.PrepareBehind(behindID, amount); err != nil {
|
||||
return nil, fmt.Errorf("GetXBehindID: error preparing behind and including ID %s", behindID)
|
||||
}
|
||||
oldestID, err := t.OldestPreparedPostID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if oldestID == "" || oldestID == behindID {
|
||||
// there is no oldest prepared post, or the oldest prepared post is still the post we're looking for entries after
|
||||
// this means we should just return the empty statuses slice since we don't have any more posts to offer
|
||||
return statuses, nil
|
||||
}
|
||||
return t.GetXBehindID(amount, behindID)
|
||||
}
|
||||
|
||||
// make sure we have enough posts prepared behind it to return what we're being asked for
|
||||
if t.preparedPosts.data.Len() < amount+position {
|
||||
fmt.Printf("\n\n\n GETXBEHINDID: PREPARED POSTS LENGTH %d WAS LESS THAN AMOUNT %d PLUS POSITION %d", t.preparedPosts.data.Len(), amount, position)
|
||||
if err := t.PrepareBehind(behindID, false, amount); err != nil {
|
||||
if err := t.PrepareBehind(behindID, amount); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fmt.Printf("\n\n\n GETXBEHINDID: PREPARED POSTS LENGTH IS NOW %d", t.preparedPosts.data.Len())
|
||||
}
|
||||
|
||||
// start serving from the entry right after the mark
|
||||
@ -97,7 +145,6 @@ serveloop:
|
||||
return nil, errors.New("GetXBehindID: could not parse e as a preparedPostsEntry")
|
||||
}
|
||||
|
||||
fmt.Printf("\n\n\n GETXBEHINDID: SERVING STATUS ID %s WITH CREATEDAT %s \n\n\n", entry.statusID, entry.createdAt.String())
|
||||
// serve up to the amount requested
|
||||
statuses = append(statuses, entry.prepared)
|
||||
served = served + 1
|
||||
@ -165,8 +212,7 @@ findMarkLoop:
|
||||
break serveloopFromTop
|
||||
}
|
||||
}
|
||||
|
||||
} else if startFromTop {
|
||||
} else if !startFromTop {
|
||||
// start serving from the entry right before the mark
|
||||
serveloopFromBottom:
|
||||
for e := beforeIDMark.Prev(); e != nil; e = e.Prev() {
|
||||
@ -191,7 +237,6 @@ func (t *timeline) GetXBetweenID(amount int, behindID string, beforeID string) (
|
||||
// make a slice of statuses with the length we need to return
|
||||
statuses := make([]*apimodel.Status, 0, amount)
|
||||
|
||||
// if there are no prepared posts, just return the empty slice
|
||||
if t.preparedPosts.data == nil {
|
||||
t.preparedPosts.data = &list.List{}
|
||||
}
|
||||
@ -220,7 +265,7 @@ findMarkLoop:
|
||||
|
||||
// make sure we have enough posts prepared behind it to return what we're being asked for
|
||||
if t.preparedPosts.data.Len() < amount+position {
|
||||
if err := t.PrepareBehind(behindID, false, amount); err != nil {
|
||||
if err := t.PrepareBehind(behindID, amount); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,12 @@
|
||||
package timeline
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
)
|
||||
|
||||
func (t *timeline) IndexBefore(statusID string, include bool, amount int) error {
|
||||
@ -44,41 +48,43 @@ func (t *timeline) IndexBefore(statusID string, include bool, amount int) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *timeline) IndexBehind(statusID string, include bool, amount int) error {
|
||||
// filtered := []*gtsmodel.Status{}
|
||||
// offsetStatus := statusID
|
||||
func (t *timeline) IndexBehind(statusID string, amount int) error {
|
||||
filtered := []*gtsmodel.Status{}
|
||||
offsetStatus := statusID
|
||||
|
||||
// grabloop:
|
||||
// for len(filtered) < amount {
|
||||
// statuses, err := t.db.GetStatusesWhereFollowing(t.accountID, amount, offsetStatus, include, false)
|
||||
// if err != nil {
|
||||
// if _, ok := err.(db.ErrNoEntries); !ok {
|
||||
// return fmt.Errorf("IndexBehindAndIncluding: 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
|
||||
// }
|
||||
fmt.Println("\n\n\nENTERING GRABLOOP\n\n\n")
|
||||
grabloop:
|
||||
for len(filtered) < amount {
|
||||
statuses, err := t.db.GetStatusesWhereFollowing(t.accountID, offsetStatus, "", "", amount, false)
|
||||
if err != nil {
|
||||
if _, ok := err.(db.ErrNoEntries); ok {
|
||||
break grabloop // we just don't have enough statuses left in the db so index what we've got and then bail
|
||||
}
|
||||
return fmt.Errorf("IndexBehindAndIncluding: error getting statuses from db: %s", err)
|
||||
}
|
||||
|
||||
// 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 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
|
||||
}
|
||||
}
|
||||
fmt.Println("\n\n\nLEAVING GRABLOOP\n\n\n")
|
||||
|
||||
// for _, s := range filtered {
|
||||
// if err := t.IndexOne(s.CreatedAt, s.ID); err != nil {
|
||||
// return fmt.Errorf("IndexBehindAndIncluding: error indexing status with id %s: %s", s.ID, err)
|
||||
// }
|
||||
// }
|
||||
for _, s := range filtered {
|
||||
if err := t.IndexOne(s.CreatedAt, s.ID); err != nil {
|
||||
return fmt.Errorf("IndexBehindAndIncluding: error indexing status with id %s: %s", s.ID, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -92,8 +98,7 @@ func (t *timeline) IndexOne(statusCreatedAt time.Time, statusID string) error {
|
||||
defer t.Unlock()
|
||||
|
||||
postIndexEntry := &postIndexEntry{
|
||||
createdAt: statusCreatedAt,
|
||||
statusID: statusID,
|
||||
statusID: statusID,
|
||||
}
|
||||
|
||||
return t.postIndex.insertIndexed(postIndexEntry)
|
||||
@ -104,8 +109,7 @@ func (t *timeline) IndexAndPrepareOne(statusCreatedAt time.Time, statusID string
|
||||
defer t.Unlock()
|
||||
|
||||
postIndexEntry := &postIndexEntry{
|
||||
createdAt: statusCreatedAt,
|
||||
statusID: statusID,
|
||||
statusID: statusID,
|
||||
}
|
||||
|
||||
if err := t.postIndex.insertIndexed(postIndexEntry); err != nil {
|
||||
@ -118,3 +122,24 @@ func (t *timeline) IndexAndPrepareOne(statusCreatedAt time.Time, statusID string
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *timeline) OldestIndexedPostID() (string, error) {
|
||||
var id string
|
||||
if t.postIndex == nil || t.postIndex.data == nil {
|
||||
// return an empty string if postindex hasn't been initialized yet
|
||||
return id, nil
|
||||
}
|
||||
|
||||
e := t.postIndex.data.Back()
|
||||
|
||||
if e == nil {
|
||||
// return an empty string if there's no back entry (ie., the index list hasn't been initialized yet)
|
||||
return id, nil
|
||||
}
|
||||
|
||||
entry, ok := e.Value.(*postIndexEntry)
|
||||
if !ok {
|
||||
return id, errors.New("OldestIndexedPostID: could not parse e as a postIndexEntry")
|
||||
}
|
||||
return entry.statusID, nil
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ type Manager interface {
|
||||
IngestAndPrepare(status *gtsmodel.Status, timelineAccountID string) error
|
||||
// HomeTimeline returns limit n amount of entries from the home timeline of the given account ID, in descending chronological order.
|
||||
// If maxID is provided, it will return entries from that maxID onwards, inclusive.
|
||||
HomeTimeline(timelineAccountID string, maxID string, sinceID string, minID string, limit int, local bool) ([]*apimodel.Status, error)
|
||||
HomeTimeline(accountID string, maxID string, sinceID string, minID string, limit int, local bool) ([]*apimodel.Status, error)
|
||||
// GetIndexedLength returns the amount of posts/statuses that have been *indexed* for the given account ID.
|
||||
GetIndexedLength(timelineAccountID string) int
|
||||
// GetDesiredIndexLength returns the amount of posts that we, ideally, index for each user.
|
||||
@ -143,18 +143,7 @@ func (m *manager) HomeTimeline(timelineAccountID string, maxID string, sinceID s
|
||||
|
||||
t := m.getOrCreateTimeline(timelineAccountID)
|
||||
|
||||
var err error
|
||||
var statuses []*apimodel.Status
|
||||
if maxID != "" && sinceID != "" {
|
||||
statuses, err = t.GetXBetweenID(limit, maxID, sinceID)
|
||||
} else if maxID != "" {
|
||||
statuses, err = t.GetXBehindID(limit, maxID)
|
||||
} else if sinceID != "" {
|
||||
statuses, err = t.GetXBeforeID(limit, sinceID, true)
|
||||
} else {
|
||||
statuses, err = t.GetXFromTop(limit)
|
||||
}
|
||||
|
||||
statuses, err := t.Get(limit, maxID, sinceID, minID)
|
||||
if err != nil {
|
||||
l.Errorf("error getting statuses: %s", err)
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package timeline
|
||||
import (
|
||||
"container/list"
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
type postIndex struct {
|
||||
@ -11,8 +10,7 @@ type postIndex struct {
|
||||
}
|
||||
|
||||
type postIndexEntry struct {
|
||||
createdAt time.Time
|
||||
statusID string
|
||||
statusID string
|
||||
}
|
||||
|
||||
func (p *postIndex) insertIndexed(i *postIndexEntry) error {
|
||||
@ -37,7 +35,7 @@ func (p *postIndex) insertIndexed(i *postIndexEntry) error {
|
||||
|
||||
// if the post to index is newer than e, insert it before e in the list
|
||||
if insertMark == nil {
|
||||
if i.createdAt.After(entry.createdAt) {
|
||||
if i.statusID > entry.statusID {
|
||||
insertMark = e
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,26 @@ import (
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
)
|
||||
|
||||
func (t *timeline) PrepareBehind(statusID string, include bool, amount int) error {
|
||||
func (t *timeline) prepareNextQuery(amount int, maxID string, sinceID string, minID string) error {
|
||||
var err error
|
||||
|
||||
// maxID is defined but sinceID isn't so take from behind
|
||||
if maxID != "" && sinceID == "" {
|
||||
err = t.PrepareBehind(maxID, amount)
|
||||
}
|
||||
|
||||
// maxID isn't defined, but sinceID || minID are, so take x before
|
||||
if maxID == "" && sinceID != "" {
|
||||
err = t.PrepareBefore(sinceID, false, amount)
|
||||
}
|
||||
if maxID == "" && minID != "" {
|
||||
err = t.PrepareBefore(minID, false, amount)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (t *timeline) PrepareBehind(statusID string, amount int) error {
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
|
||||
@ -25,9 +44,6 @@ prepareloop:
|
||||
// we haven't hit the position we need to prepare from yet
|
||||
if entry.statusID == statusID {
|
||||
preparing = true
|
||||
if !include {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -171,10 +187,29 @@ func (t *timeline) prepare(statusID string) error {
|
||||
|
||||
// shove it in prepared posts as a prepared posts entry
|
||||
preparedPostsEntry := &preparedPostsEntry{
|
||||
createdAt: gtsStatus.CreatedAt,
|
||||
statusID: statusID,
|
||||
prepared: apiModelStatus,
|
||||
statusID: statusID,
|
||||
prepared: apiModelStatus,
|
||||
}
|
||||
|
||||
return t.preparedPosts.insertPrepared(preparedPostsEntry)
|
||||
}
|
||||
|
||||
func (t *timeline) OldestPreparedPostID() (string, error) {
|
||||
var id string
|
||||
if t.preparedPosts == nil || t.preparedPosts.data == nil {
|
||||
// return an empty string if prepared posts hasn't been initialized yet
|
||||
return id, nil
|
||||
}
|
||||
|
||||
e := t.preparedPosts.data.Back()
|
||||
if e == nil {
|
||||
// return an empty string if there's no back entry (ie., the index list hasn't been initialized yet)
|
||||
return id, nil
|
||||
}
|
||||
|
||||
entry, ok := e.Value.(*preparedPostsEntry)
|
||||
if !ok {
|
||||
return id, errors.New("OldestPreparedPostID: could not parse e as a preparedPostsEntry")
|
||||
}
|
||||
return entry.statusID, nil
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package timeline
|
||||
import (
|
||||
"container/list"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
)
|
||||
@ -13,9 +12,8 @@ type preparedPosts struct {
|
||||
}
|
||||
|
||||
type preparedPostsEntry struct {
|
||||
createdAt time.Time
|
||||
statusID string
|
||||
prepared *apimodel.Status
|
||||
statusID string
|
||||
prepared *apimodel.Status
|
||||
}
|
||||
|
||||
func (p *preparedPosts) insertPrepared(i *preparedPostsEntry) error {
|
||||
@ -40,7 +38,7 @@ func (p *preparedPosts) insertPrepared(i *preparedPostsEntry) error {
|
||||
|
||||
// if the post to index is newer than e, insert it before e in the list
|
||||
if insertMark == nil {
|
||||
if i.createdAt.After(entry.createdAt) {
|
||||
if i.statusID > entry.statusID {
|
||||
insertMark = e
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,6 @@
|
||||
package timeline
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@ -39,6 +38,7 @@ type Timeline interface {
|
||||
RETRIEVAL FUNCTIONS
|
||||
*/
|
||||
|
||||
Get(amount int, maxID string, sinceID string, minID string) ([]*apimodel.Status, error)
|
||||
// GetXFromTop returns x amount of posts from the top of the timeline, from newest to oldest.
|
||||
GetXFromTop(amount int) ([]*apimodel.Status, error)
|
||||
// GetXBehindID returns x amount of posts from the given id onwards, from newest to oldest.
|
||||
@ -63,12 +63,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
|
||||
// Remove removes a status from both the index and prepared posts.
|
||||
//
|
||||
// If a status has multiple entries in a timeline, they will all be removed.
|
||||
//
|
||||
// The returned int indicates the amount of entries that were removed.
|
||||
Remove(statusID string) (int, 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.
|
||||
OldestIndexedPostID() (string, error)
|
||||
@ -81,10 +76,13 @@ type Timeline interface {
|
||||
PrepareFromTop(amount int) error
|
||||
// PrepareBehind instructs the timeline to prepare the next amount of entries for serialization, from position onwards.
|
||||
// If include is true, then the given status ID will also be prepared, otherwise only entries behind it will be prepared.
|
||||
PrepareBehind(statusID string, include bool, amount int) error
|
||||
PrepareBehind(statusID string, amount int) error
|
||||
// IndexOne puts a status into the timeline at the appropriate place according to its 'createdAt' property,
|
||||
// and then immediately prepares it.
|
||||
IndexAndPrepareOne(statusCreatedAt time.Time, statusID string) error
|
||||
// OldestPreparedPostID returns the id of the rearmost (ie., the oldest) prepared 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.
|
||||
OldestPreparedPostID() (string, error)
|
||||
|
||||
/*
|
||||
INFO FUNCTIONS
|
||||
@ -99,6 +97,12 @@ type Timeline interface {
|
||||
|
||||
// Reset instructs the timeline to reset to its base state -- cache only the minimum amount of posts.
|
||||
Reset() error
|
||||
// Remove removes a status from both the index and prepared posts.
|
||||
//
|
||||
// If a status has multiple entries in a timeline, they will all be removed.
|
||||
//
|
||||
// The returned int indicates the amount of entries that were removed.
|
||||
Remove(statusID string) (int, error)
|
||||
}
|
||||
|
||||
// timeline fulfils the Timeline interface
|
||||
@ -134,24 +138,3 @@ func (t *timeline) PostIndexLength() int {
|
||||
|
||||
return t.postIndex.data.Len()
|
||||
}
|
||||
|
||||
func (t *timeline) OldestIndexedPostID() (string, error) {
|
||||
var id string
|
||||
if t.postIndex == nil || t.postIndex.data == nil {
|
||||
// return an empty string if postindex hasn't been initialized yet
|
||||
return id, nil
|
||||
}
|
||||
|
||||
e := t.postIndex.data.Back()
|
||||
|
||||
if e == nil {
|
||||
// return an empty string if there's no back entry (ie., the index list hasn't been initialized yet)
|
||||
return id, nil
|
||||
}
|
||||
|
||||
entry, ok := e.Value.(*postIndexEntry)
|
||||
if !ok {
|
||||
return id, errors.New("OldestIndexedPostID: could not parse e as a postIndexEntry")
|
||||
}
|
||||
return entry.statusID, nil
|
||||
}
|
||||
|
@ -125,7 +125,6 @@ func (c *converter) ASRepresentationToAccount(accountable Accountable, update bo
|
||||
acct.URL = uri.String()
|
||||
}
|
||||
|
||||
|
||||
// InboxURI
|
||||
if accountable.GetActivityStreamsInbox() != nil && accountable.GetActivityStreamsInbox().GetIRI() != nil {
|
||||
acct.InboxURI = accountable.GetActivityStreamsInbox().GetIRI().String()
|
||||
|
Loading…
Reference in New Issue
Block a user