diff options
author | 2021-06-13 18:42:28 +0200 | |
---|---|---|
committer | 2021-06-13 18:42:28 +0200 | |
commit | b4288f3c47a9ff9254b933dcb9ee7274d4a4135c (patch) | |
tree | 3fe1bb1ab8d4b8c5d9a83df708e5088f35c3150a /internal/timeline/prepare.go | |
parent | Tidy + timeline embetterment (#38) (diff) | |
download | gotosocial-b4288f3c47a9ff9254b933dcb9ee7274d4a4135c.tar.xz |
Timeline manager (#40)
* start messing about with timeline manager
* i have no idea what i'm doing
* i continue to not know what i'm doing
* it's coming along
* bit more progress
* update timeline with new posts as they come in
* lint and fmt
* Select accounts where empty string
* restructure a bunch, get unfaves working
* moving stuff around
* federate status deletes properly
* mention regex better but not 100% there
* fix regex
* some more hacking away at the timeline code phew
* fix up some little things
* i can't even
* more timeline stuff
* move to ulid
* fiddley
* some lil fixes for kibou compatibility
* timelines working pretty alright!
* tidy + lint
Diffstat (limited to 'internal/timeline/prepare.go')
-rw-r--r-- | internal/timeline/prepare.go | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/internal/timeline/prepare.go b/internal/timeline/prepare.go new file mode 100644 index 000000000..1fb1cd714 --- /dev/null +++ b/internal/timeline/prepare.go @@ -0,0 +1,215 @@ +package timeline + +import ( + "errors" + "fmt" + + "github.com/superseriousbusiness/gotosocial/internal/db" + "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" +) + +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() + + var prepared int + var preparing bool +prepareloop: + for e := t.postIndex.data.Front(); e != nil; e = e.Next() { + entry, ok := e.Value.(*postIndexEntry) + if !ok { + return errors.New("PrepareBehind: could not parse e as a postIndexEntry") + } + + if !preparing { + // we haven't hit the position we need to prepare from yet + if entry.statusID == statusID { + preparing = true + } + } + + if preparing { + if err := t.prepare(entry.statusID); err != nil { + // there's been an error + if _, ok := err.(db.ErrNoEntries); !ok { + // it's a real error + return fmt.Errorf("PrepareBehind: error preparing status with id %s: %s", entry.statusID, err) + } + // the status just doesn't exist (anymore) so continue to the next one + continue + } + if prepared == amount { + // we're done + break prepareloop + } + prepared = prepared + 1 + } + } + + return nil +} + +func (t *timeline) PrepareBefore(statusID string, include bool, amount int) error { + t.Lock() + defer t.Unlock() + + var prepared int + var preparing bool +prepareloop: + for e := t.postIndex.data.Back(); e != nil; e = e.Prev() { + entry, ok := e.Value.(*postIndexEntry) + if !ok { + return errors.New("PrepareBefore: could not parse e as a postIndexEntry") + } + + if !preparing { + // we haven't hit the position we need to prepare from yet + if entry.statusID == statusID { + preparing = true + if !include { + continue + } + } + } + + if preparing { + if err := t.prepare(entry.statusID); err != nil { + // there's been an error + if _, ok := err.(db.ErrNoEntries); !ok { + // it's a real error + return fmt.Errorf("PrepareBefore: error preparing status with id %s: %s", entry.statusID, err) + } + // the status just doesn't exist (anymore) so continue to the next one + continue + } + if prepared == amount { + // we're done + break prepareloop + } + prepared = prepared + 1 + } + } + + return nil +} + +func (t *timeline) PrepareFromTop(amount int) error { + t.Lock() + defer t.Unlock() + + t.preparedPosts.data.Init() + + var prepared int +prepareloop: + for e := t.postIndex.data.Front(); e != nil; e = e.Next() { + entry, ok := e.Value.(*postIndexEntry) + if !ok { + return errors.New("PrepareFromTop: could not parse e as a postIndexEntry") + } + + if err := t.prepare(entry.statusID); err != nil { + // there's been an error + if _, ok := err.(db.ErrNoEntries); !ok { + // it's a real error + return fmt.Errorf("PrepareFromTop: error preparing status with id %s: %s", entry.statusID, err) + } + // the status just doesn't exist (anymore) so continue to the next one + continue + } + + prepared = prepared + 1 + if prepared == amount { + // we're done + break prepareloop + } + } + + return nil +} + +func (t *timeline) prepare(statusID string) error { + + // start by getting the status out of the database according to its indexed ID + gtsStatus := >smodel.Status{} + if err := t.db.GetByID(statusID, gtsStatus); err != nil { + return err + } + + // if the account pointer hasn't been set on this timeline already, set it lazily here + if t.account == nil { + timelineOwnerAccount := >smodel.Account{} + if err := t.db.GetByID(t.accountID, timelineOwnerAccount); err != nil { + return err + } + 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) + if err != nil { + return err + } + + // shove it in prepared posts as a prepared posts entry + preparedPostsEntry := &preparedPostsEntry{ + 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 +} |