timelines working pretty alright!
parent
d9d9a7a626
commit
a25e53af4e
internal
api/s2s/webfinger
db/pg
federation/federatingdb
processing
typeutils
|
@ -30,7 +30,7 @@ import (
|
||||||
// WebfingerGETRequest handles requests to, for example, https://example.org/.well-known/webfinger?resource=acct:some_user@example.org
|
// 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) {
|
func (m *Module) WebfingerGETRequest(c *gin.Context) {
|
||||||
l := m.log.WithFields(logrus.Fields{
|
l := m.log.WithFields(logrus.Fields{
|
||||||
"func": "WebfingerGETRequest",
|
"func": "WebfingerGETRequest",
|
||||||
"user-agent": c.Request.UserAgent(),
|
"user-agent": c.Request.UserAgent(),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1165,33 +1165,19 @@ func (ps *postgresService) GetStatusesWhereFollowing(accountID string, maxID str
|
||||||
|
|
||||||
q = q.ColumnExpr("status.*").
|
q = q.ColumnExpr("status.*").
|
||||||
Join("JOIN follows AS f ON f.target_account_id = status.account_id").
|
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 != "" {
|
if maxID != "" {
|
||||||
s := >smodel.Status{}
|
q = q.Where("status.id < ?", maxID)
|
||||||
if err := ps.conn.Model(s).Where("id = ?", maxID).Select(); err == nil {
|
|
||||||
q = q.Where("status.created_at < ?", s.CreatedAt)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if sinceID != "" {
|
if sinceID != "" {
|
||||||
s := >smodel.Status{}
|
q = q.Where("status.id > ?", sinceID)
|
||||||
if err := ps.conn.Model(s).Where("id = ?", sinceID).Select(); err == nil {
|
|
||||||
q = q.Where("status.created_at > ?", s.CreatedAt)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if minID != "" {
|
if minID != "" {
|
||||||
s := >smodel.Status{}
|
q = q.Where("status.id > ?", minID)
|
||||||
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")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if local {
|
if local {
|
||||||
|
@ -1204,9 +1190,14 @@ func (ps *postgresService) GetStatusesWhereFollowing(accountID string, maxID str
|
||||||
|
|
||||||
err := q.Select()
|
err := q.Select()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err != pg.ErrNoRows {
|
if err == pg.ErrNoRows {
|
||||||
return nil, err
|
return nil, db.ErrNoEntries{}
|
||||||
}
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(statuses) == 0 {
|
||||||
|
return nil, db.ErrNoEntries{}
|
||||||
}
|
}
|
||||||
|
|
||||||
return statuses, nil
|
return statuses, nil
|
||||||
|
@ -1219,42 +1210,34 @@ func (ps *postgresService) GetPublicTimelineForAccount(accountID string, maxID s
|
||||||
Where("visibility = ?", gtsmodel.VisibilityPublic).
|
Where("visibility = ?", gtsmodel.VisibilityPublic).
|
||||||
Where("? IS NULL", pg.Ident("in_reply_to_id")).
|
Where("? IS NULL", pg.Ident("in_reply_to_id")).
|
||||||
Where("? IS NULL", pg.Ident("boost_of_id")).
|
Where("? IS NULL", pg.Ident("boost_of_id")).
|
||||||
Limit(limit).
|
Order("status.id DESC")
|
||||||
Order("created_at DESC")
|
|
||||||
|
|
||||||
if maxID != "" {
|
if maxID != "" {
|
||||||
s := >smodel.Status{}
|
q = q.Where("status.id < ?", maxID)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if sinceID != "" {
|
if sinceID != "" {
|
||||||
s := >smodel.Status{}
|
q = q.Where("status.id > ?", sinceID)
|
||||||
if err := ps.conn.Model(s).Where("id = ?", sinceID).Select(); err != nil {
|
}
|
||||||
return nil, err
|
|
||||||
}
|
if minID != "" {
|
||||||
q = q.Where("created_at > ?", s.CreatedAt)
|
q = q.Where("status.id > ?", minID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if local {
|
if local {
|
||||||
q = q.Where("local = ?", local)
|
q = q.Where("status.local = ?", local)
|
||||||
|
}
|
||||||
|
|
||||||
|
if limit > 0 {
|
||||||
|
q = q.Limit(limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := q.Select()
|
err := q.Select()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err != pg.ErrNoRows {
|
if err == pg.ErrNoRows {
|
||||||
return nil, err
|
return nil, db.ErrNoEntries{}
|
||||||
}
|
}
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return statuses, nil
|
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)
|
q := ps.conn.Model(¬ifications).Where("target_account_id = ?", accountID)
|
||||||
|
|
||||||
if maxID != "" {
|
if maxID != "" {
|
||||||
n := >smodel.Notification{}
|
q = q.Where("id < ?", maxID)
|
||||||
if err := ps.conn.Model(n).Where("id = ?", maxID).Select(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
q = q.Where("created_at < ?", n.CreatedAt)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if sinceID != "" {
|
if sinceID != "" {
|
||||||
n := >smodel.Notification{}
|
q = q.Where("id > ?", sinceID)
|
||||||
if err := ps.conn.Model(n).Where("id = ?", sinceID).Select(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
q = q.Where("created_at > ?", n.CreatedAt)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if limit != 0 {
|
if limit != 0 {
|
||||||
|
|
|
@ -95,7 +95,7 @@ func (f *federatingDB) Accept(ctx context.Context, accept vocab.ActivityStreamsA
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
switch iter.GetType().GetTypeName() {
|
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):
|
case string(gtsmodel.ActivityStreamsFollow):
|
||||||
// ACCEPT FOLLOW
|
// ACCEPT FOLLOW
|
||||||
asFollow, ok := iter.GetType().(vocab.ActivityStreamsFollow)
|
asFollow, ok := iter.GetType().(vocab.ActivityStreamsFollow)
|
||||||
|
|
|
@ -21,9 +21,7 @@ package processing
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"sort"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
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{},
|
Statuses: []*apimodel.Status{},
|
||||||
}
|
}
|
||||||
|
|
||||||
apiStatuses := []*apimodel.Status{}
|
apiStatuses, err := p.timelineManager.HomeTimeline(authed.Account.ID, maxID, sinceID, minID, limit, local)
|
||||||
|
|
||||||
maxIDMarker := maxID
|
|
||||||
sinceIDMarker := sinceID
|
|
||||||
minIDMarker := minID
|
|
||||||
|
|
||||||
gtsStatuses, err := p.db.GetStatusesWhereFollowing(authed.Account.ID, maxIDMarker, sinceIDMarker, minIDMarker, limit, local)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if _, ok := err.(db.ErrNoEntries); !ok {
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
return nil, gtserror.NewErrorInternalError(fmt.Errorf("HomeTimelineGet: error getting statuses from db: %s", 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
|
resp.Statuses = apiStatuses
|
||||||
|
|
||||||
if len(resp.Statuses) != 0 {
|
// prepare the next and previous links
|
||||||
|
if len(apiStatuses) != 0 {
|
||||||
nextLink := &url.URL{
|
nextLink := &url.URL{
|
||||||
Scheme: p.config.Protocol,
|
Scheme: p.config.Protocol,
|
||||||
Host: p.config.Host,
|
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)
|
statuses, err := p.db.GetStatusesWhereFollowing(account.ID, "", "", "", desiredIndexLength, false)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
p.indexAndIngest(statuses, account, desiredIndexLength)
|
p.indexAndIngest(statuses, account, desiredIndexLength)
|
||||||
|
|
|
@ -8,11 +8,53 @@ import (
|
||||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
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) {
|
func (t *timeline) GetXFromTop(amount int) ([]*apimodel.Status, error) {
|
||||||
// make a slice of statuses with the length we need to return
|
// make a slice of statuses with the length we need to return
|
||||||
statuses := make([]*apimodel.Status, 0, amount)
|
statuses := make([]*apimodel.Status, 0, amount)
|
||||||
|
|
||||||
// if there are no prepared posts, just return the empty slice
|
|
||||||
if t.preparedPosts.data == nil {
|
if t.preparedPosts.data == nil {
|
||||||
t.preparedPosts.data = &list.List{}
|
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
|
// make a slice of statuses with the length we need to return
|
||||||
statuses := make([]*apimodel.Status, 0, amount)
|
statuses := make([]*apimodel.Status, 0, amount)
|
||||||
|
|
||||||
// if there are no prepared posts, just return the empty slice
|
|
||||||
if t.preparedPosts.data == nil {
|
if t.preparedPosts.data == nil {
|
||||||
t.preparedPosts.data = &list.List{}
|
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
|
// iterate through the modified list until we hit the mark we're looking for
|
||||||
var position int
|
var position int
|
||||||
var behindIDMark *list.Element
|
var behindIDMark *list.Element
|
||||||
|
|
||||||
findMarkLoop:
|
findMarkLoop:
|
||||||
for e := t.preparedPosts.data.Front(); e != nil; e = e.Next() {
|
for e := t.preparedPosts.data.Front(); e != nil; e = e.Next() {
|
||||||
position = position + 1
|
position = position + 1
|
||||||
|
@ -62,7 +104,6 @@ findMarkLoop:
|
||||||
}
|
}
|
||||||
|
|
||||||
if entry.statusID == behindID {
|
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
|
behindIDMark = e
|
||||||
break findMarkLoop
|
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
|
// we didn't find it, so we need to make sure it's indexed and prepared and then try again
|
||||||
if behindIDMark == nil {
|
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)
|
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)
|
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)
|
return t.GetXBehindID(amount, behindID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure we have enough posts prepared behind it to return what we're being asked for
|
// 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 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, amount); err != nil {
|
||||||
if err := t.PrepareBehind(behindID, false, amount); err != nil {
|
|
||||||
return nil, err
|
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
|
// 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")
|
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
|
// serve up to the amount requested
|
||||||
statuses = append(statuses, entry.prepared)
|
statuses = append(statuses, entry.prepared)
|
||||||
served = served + 1
|
served = served + 1
|
||||||
|
@ -165,8 +212,7 @@ findMarkLoop:
|
||||||
break serveloopFromTop
|
break serveloopFromTop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if !startFromTop {
|
||||||
} else if startFromTop {
|
|
||||||
// start serving from the entry right before the mark
|
// start serving from the entry right before the mark
|
||||||
serveloopFromBottom:
|
serveloopFromBottom:
|
||||||
for e := beforeIDMark.Prev(); e != nil; e = e.Prev() {
|
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
|
// make a slice of statuses with the length we need to return
|
||||||
statuses := make([]*apimodel.Status, 0, amount)
|
statuses := make([]*apimodel.Status, 0, amount)
|
||||||
|
|
||||||
// if there are no prepared posts, just return the empty slice
|
|
||||||
if t.preparedPosts.data == nil {
|
if t.preparedPosts.data == nil {
|
||||||
t.preparedPosts.data = &list.List{}
|
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
|
// 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 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
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
package timeline
|
package timeline
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (t *timeline) IndexBefore(statusID string, include bool, amount int) error {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *timeline) IndexBehind(statusID string, include bool, amount int) error {
|
func (t *timeline) IndexBehind(statusID string, amount int) error {
|
||||||
// filtered := []*gtsmodel.Status{}
|
filtered := []*gtsmodel.Status{}
|
||||||
// offsetStatus := statusID
|
offsetStatus := statusID
|
||||||
|
|
||||||
// grabloop:
|
fmt.Println("\n\n\nENTERING GRABLOOP\n\n\n")
|
||||||
// for len(filtered) < amount {
|
grabloop:
|
||||||
// statuses, err := t.db.GetStatusesWhereFollowing(t.accountID, amount, offsetStatus, include, false)
|
for len(filtered) < amount {
|
||||||
// if err != nil {
|
statuses, err := t.db.GetStatusesWhereFollowing(t.accountID, offsetStatus, "", "", amount, false)
|
||||||
// if _, ok := err.(db.ErrNoEntries); !ok {
|
if err != nil {
|
||||||
// return fmt.Errorf("IndexBehindAndIncluding: error getting statuses from db: %s", err)
|
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
|
||||||
// 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 {
|
for _, s := range statuses {
|
||||||
// relevantAccounts, err := t.db.PullRelevantAccountsFromStatus(s)
|
relevantAccounts, err := t.db.PullRelevantAccountsFromStatus(s)
|
||||||
// if err != nil {
|
if err != nil {
|
||||||
// continue
|
continue
|
||||||
// }
|
}
|
||||||
// visible, err := t.db.StatusVisible(s, t.account, relevantAccounts)
|
visible, err := t.db.StatusVisible(s, t.account, relevantAccounts)
|
||||||
// if err != nil {
|
if err != nil {
|
||||||
// continue
|
continue
|
||||||
// }
|
}
|
||||||
// if visible {
|
if visible {
|
||||||
// filtered = append(filtered, s)
|
filtered = append(filtered, s)
|
||||||
// }
|
}
|
||||||
// offsetStatus = s.ID
|
offsetStatus = s.ID
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
fmt.Println("\n\n\nLEAVING GRABLOOP\n\n\n")
|
||||||
|
|
||||||
// for _, s := range filtered {
|
for _, s := range filtered {
|
||||||
// if err := t.IndexOne(s.CreatedAt, s.ID); err != nil {
|
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 fmt.Errorf("IndexBehindAndIncluding: error indexing status with id %s: %s", s.ID, err)
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -92,8 +98,7 @@ func (t *timeline) IndexOne(statusCreatedAt time.Time, statusID string) error {
|
||||||
defer t.Unlock()
|
defer t.Unlock()
|
||||||
|
|
||||||
postIndexEntry := &postIndexEntry{
|
postIndexEntry := &postIndexEntry{
|
||||||
createdAt: statusCreatedAt,
|
statusID: statusID,
|
||||||
statusID: statusID,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return t.postIndex.insertIndexed(postIndexEntry)
|
return t.postIndex.insertIndexed(postIndexEntry)
|
||||||
|
@ -104,8 +109,7 @@ func (t *timeline) IndexAndPrepareOne(statusCreatedAt time.Time, statusID string
|
||||||
defer t.Unlock()
|
defer t.Unlock()
|
||||||
|
|
||||||
postIndexEntry := &postIndexEntry{
|
postIndexEntry := &postIndexEntry{
|
||||||
createdAt: statusCreatedAt,
|
statusID: statusID,
|
||||||
statusID: statusID,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := t.postIndex.insertIndexed(postIndexEntry); err != nil {
|
if err := t.postIndex.insertIndexed(postIndexEntry); err != nil {
|
||||||
|
@ -118,3 +122,24 @@ func (t *timeline) IndexAndPrepareOne(statusCreatedAt time.Time, statusID string
|
||||||
|
|
||||||
return nil
|
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
|
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.
|
// 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.
|
// 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 returns the amount of posts/statuses that have been *indexed* for the given account ID.
|
||||||
GetIndexedLength(timelineAccountID string) int
|
GetIndexedLength(timelineAccountID string) int
|
||||||
// GetDesiredIndexLength returns the amount of posts that we, ideally, index for each user.
|
// 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)
|
t := m.getOrCreateTimeline(timelineAccountID)
|
||||||
|
|
||||||
var err error
|
statuses, err := t.Get(limit, maxID, sinceID, minID)
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Errorf("error getting statuses: %s", err)
|
l.Errorf("error getting statuses: %s", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ package timeline
|
||||||
import (
|
import (
|
||||||
"container/list"
|
"container/list"
|
||||||
"errors"
|
"errors"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type postIndex struct {
|
type postIndex struct {
|
||||||
|
@ -11,8 +10,7 @@ type postIndex struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type postIndexEntry struct {
|
type postIndexEntry struct {
|
||||||
createdAt time.Time
|
statusID string
|
||||||
statusID string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *postIndex) insertIndexed(i *postIndexEntry) error {
|
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 the post to index is newer than e, insert it before e in the list
|
||||||
if insertMark == nil {
|
if insertMark == nil {
|
||||||
if i.createdAt.After(entry.createdAt) {
|
if i.statusID > entry.statusID {
|
||||||
insertMark = e
|
insertMark = e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,26 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"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()
|
t.Lock()
|
||||||
defer t.Unlock()
|
defer t.Unlock()
|
||||||
|
|
||||||
|
@ -25,9 +44,6 @@ prepareloop:
|
||||||
// we haven't hit the position we need to prepare from yet
|
// we haven't hit the position we need to prepare from yet
|
||||||
if entry.statusID == statusID {
|
if entry.statusID == statusID {
|
||||||
preparing = true
|
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
|
// shove it in prepared posts as a prepared posts entry
|
||||||
preparedPostsEntry := &preparedPostsEntry{
|
preparedPostsEntry := &preparedPostsEntry{
|
||||||
createdAt: gtsStatus.CreatedAt,
|
statusID: statusID,
|
||||||
statusID: statusID,
|
prepared: apiModelStatus,
|
||||||
prepared: apiModelStatus,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return t.preparedPosts.insertPrepared(preparedPostsEntry)
|
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 (
|
import (
|
||||||
"container/list"
|
"container/list"
|
||||||
"errors"
|
"errors"
|
||||||
"time"
|
|
||||||
|
|
||||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
)
|
)
|
||||||
|
@ -13,9 +12,8 @@ type preparedPosts struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type preparedPostsEntry struct {
|
type preparedPostsEntry struct {
|
||||||
createdAt time.Time
|
statusID string
|
||||||
statusID string
|
prepared *apimodel.Status
|
||||||
prepared *apimodel.Status
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *preparedPosts) insertPrepared(i *preparedPostsEntry) error {
|
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 the post to index is newer than e, insert it before e in the list
|
||||||
if insertMark == nil {
|
if insertMark == nil {
|
||||||
if i.createdAt.After(entry.createdAt) {
|
if i.statusID > entry.statusID {
|
||||||
insertMark = e
|
insertMark = e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
package timeline
|
package timeline
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -39,6 +38,7 @@ type Timeline interface {
|
||||||
RETRIEVAL FUNCTIONS
|
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 returns x amount of posts from the top of the timeline, from newest to oldest.
|
||||||
GetXFromTop(amount int) ([]*apimodel.Status, error)
|
GetXFromTop(amount int) ([]*apimodel.Status, error)
|
||||||
// GetXBehindID returns x amount of posts from the given id onwards, from newest to oldest.
|
// 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 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) 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.
|
// 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.
|
// 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)
|
OldestIndexedPostID() (string, error)
|
||||||
|
@ -81,10 +76,13 @@ type Timeline interface {
|
||||||
PrepareFromTop(amount int) error
|
PrepareFromTop(amount int) error
|
||||||
// PrepareBehind instructs the timeline to prepare the next amount of entries for serialization, from position onwards.
|
// 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.
|
// 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,
|
// IndexOne puts a status into the timeline at the appropriate place according to its 'createdAt' property,
|
||||||
// and then immediately prepares it.
|
// and then immediately prepares it.
|
||||||
IndexAndPrepareOne(statusCreatedAt time.Time, statusID string) error
|
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
|
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 instructs the timeline to reset to its base state -- cache only the minimum amount of posts.
|
||||||
Reset() error
|
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
|
// timeline fulfils the Timeline interface
|
||||||
|
@ -134,24 +138,3 @@ func (t *timeline) PostIndexLength() int {
|
||||||
|
|
||||||
return t.postIndex.data.Len()
|
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()
|
acct.URL = uri.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// InboxURI
|
// InboxURI
|
||||||
if accountable.GetActivityStreamsInbox() != nil && accountable.GetActivityStreamsInbox().GetIRI() != nil {
|
if accountable.GetActivityStreamsInbox() != nil && accountable.GetActivityStreamsInbox().GetIRI() != nil {
|
||||||
acct.InboxURI = accountable.GetActivityStreamsInbox().GetIRI().String()
|
acct.InboxURI = accountable.GetActivityStreamsInbox().GetIRI().String()
|
||||||
|
|
Loading…
Reference in New Issue