mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-04-15 13:54:07 +00:00
start adding page validation
This commit is contained in:
parent
f6072a7c8f
commit
f3c05a9145
5 changed files with 80 additions and 84 deletions
26
internal/cache/timeline/status.go
vendored
26
internal/cache/timeline/status.go
vendored
|
@ -159,11 +159,6 @@ func (t *StatusTimelines) Delete(key string) {
|
|||
}
|
||||
}
|
||||
|
||||
// InsertInto allows you to bulk insert many statuses into timeline mapped by key.
|
||||
func (t *StatusTimelines) InsertInto(key string, statuses ...*gtsmodel.Status) {
|
||||
t.MustGet(key).Insert(statuses...)
|
||||
}
|
||||
|
||||
// RemoveByStatusIDs ...
|
||||
func (t *StatusTimelines) RemoveByStatusIDs(statusIDs ...string) {
|
||||
if p := t.ptr.Load(); p != nil {
|
||||
|
@ -200,6 +195,15 @@ func (t *StatusTimelines) UnprepareByAccountIDs(accountIDs ...string) {
|
|||
}
|
||||
}
|
||||
|
||||
// Unprepare ...
|
||||
func (t *StatusTimelines) Unprepare(key string) {
|
||||
if p := t.ptr.Load(); p != nil {
|
||||
if tt := (*p)[key]; tt != nil {
|
||||
tt.UnprepareAll()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// UnprepareAll ...
|
||||
func (t *StatusTimelines) UnprepareAll() {
|
||||
if p := t.ptr.Load(); p != nil {
|
||||
|
@ -270,7 +274,7 @@ func (t *StatusTimeline) Init(cap int) {
|
|||
// ID as primary key is inherently an index.
|
||||
// {Fields: "ID"},
|
||||
{Fields: "AccountID", Multiple: true},
|
||||
{Fields: "BoostOfStatusID", Multiple: true},
|
||||
{Fields: "BoostOfID", Multiple: true},
|
||||
{Fields: "BoostOfAccountID", Multiple: true},
|
||||
},
|
||||
|
||||
|
@ -370,7 +374,7 @@ func (t *StatusTimeline) Load(
|
|||
|
||||
// Before we can do any filtering, we need
|
||||
// to load status models for cached entries.
|
||||
err := loadStatuses(ctx, metas, loadIDs)
|
||||
err := loadStatuses(metas, loadIDs)
|
||||
if err != nil {
|
||||
return nil, "", "", gtserror.Newf("error loading statuses: %w", err)
|
||||
}
|
||||
|
@ -710,12 +714,16 @@ func (t *StatusTimeline) prepare(
|
|||
return apiStatuses, nil
|
||||
}
|
||||
|
||||
// loadStatuses ...
|
||||
// loadStatuses loads statuses using provided callback
|
||||
// for the statuses in meta slice that aren't loaded.
|
||||
// the amount very much depends on whether meta objects
|
||||
// are yet-to-be-cached (i.e. newly loaded, with status),
|
||||
// or are from the timeline cache (unloaded status).
|
||||
func loadStatuses(
|
||||
ctx context.Context,
|
||||
metas []*StatusMeta,
|
||||
loadIDs func([]string) ([]*gtsmodel.Status, error),
|
||||
) error {
|
||||
|
||||
// Determine which of our passed status
|
||||
// meta objects still need statuses loading.
|
||||
toLoadIDs := make([]string, len(metas))
|
||||
|
|
31
internal/cache/timeline/timeline.go
vendored
31
internal/cache/timeline/timeline.go
vendored
|
@ -1,3 +1,20 @@
|
|||
// GoToSocial
|
||||
// Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package timeline
|
||||
|
||||
import (
|
||||
|
@ -5,15 +22,21 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/paging"
|
||||
)
|
||||
|
||||
// nextPageParams gets the next set of paging
|
||||
// parameters to use based on the current set,
|
||||
// and the next set of lo / hi values. This will
|
||||
// correctly handle making sure that, depending
|
||||
// on the paging order, the cursor value gets
|
||||
// updated while maintaining the boundary value.
|
||||
func nextPageParams(
|
||||
curLo, curHi string, // current page params
|
||||
nextLo, nextHi string, // next lo / hi values
|
||||
curLo, curHi string,
|
||||
nextLo, nextHi string,
|
||||
order paging.Order,
|
||||
) (lo string, hi string) {
|
||||
if order.Ascending() {
|
||||
|
||||
return nextLo, curHi
|
||||
} else /* i.e. descending */ {
|
||||
|
||||
return curLo, nextHi
|
||||
}
|
||||
}
|
||||
|
||||
|
|
31
internal/id/page.go
Normal file
31
internal/id/page.go
Normal file
|
@ -0,0 +1,31 @@
|
|||
package id
|
||||
|
||||
import (
|
||||
"github.com/superseriousbusiness/gotosocial/internal/paging"
|
||||
)
|
||||
|
||||
// ValidatePage ...
|
||||
func ValidatePage(page *paging.Page) {
|
||||
if page == nil {
|
||||
// unpaged
|
||||
return
|
||||
}
|
||||
|
||||
switch page.Order() {
|
||||
// If the page order is ascending,
|
||||
// ensure that a minimum value is set.
|
||||
// This will be used as the cursor.
|
||||
case paging.OrderAscending:
|
||||
if page.Min.Value == "" {
|
||||
page.Min.Value = Lowest
|
||||
}
|
||||
|
||||
// If the page order is descending,
|
||||
// ensure that a maximum value is set.
|
||||
// This will be used as the cursor.
|
||||
case paging.OrderDescending:
|
||||
if page.Max.Value == "" {
|
||||
page.Max.Value = Highest
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
// GoToSocial
|
||||
// Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package timeline
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/superseriousbusiness/gotosocial/internal/timeline"
|
||||
)
|
||||
|
||||
// SkipInsert returns a function that satisifes SkipInsertFunction.
|
||||
func SkipInsert() timeline.SkipInsertFunction {
|
||||
// Gap to allow between a status or boost of status,
|
||||
// and reinsertion of a new boost of that status.
|
||||
// This is useful to avoid a heavily boosted status
|
||||
// showing up way too often in a user's timeline.
|
||||
const boostReinsertionDepth = 50
|
||||
|
||||
return func(
|
||||
ctx context.Context,
|
||||
newItemID string,
|
||||
newItemAccountID string,
|
||||
newItemBoostOfID string,
|
||||
newItemBoostOfAccountID string,
|
||||
nextItemID string,
|
||||
nextItemAccountID string,
|
||||
nextItemBoostOfID string,
|
||||
nextItemBoostOfAccountID string,
|
||||
depth int,
|
||||
) (bool, error) {
|
||||
if newItemID == nextItemID {
|
||||
// Don't insert duplicates.
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if newItemBoostOfID != "" {
|
||||
if newItemBoostOfID == nextItemBoostOfID &&
|
||||
depth < boostReinsertionDepth {
|
||||
// Don't insert boosts of items
|
||||
// we've seen boosted recently.
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if newItemBoostOfID == nextItemID &&
|
||||
depth < boostReinsertionDepth {
|
||||
// Don't insert boosts of items when
|
||||
// we've seen the original recently.
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Proceed with insertion
|
||||
// (that's what she said!).
|
||||
return false, nil
|
||||
}
|
||||
}
|
|
@ -31,6 +31,7 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/paging"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
|
@ -106,6 +107,10 @@ func (p *Processor) getStatusTimeline(
|
|||
mutes = usermute.NewCompiledUserMuteList(allMutes)
|
||||
}
|
||||
|
||||
// Ensure we have valid
|
||||
// input paging data.
|
||||
id.ValidatePage(page)
|
||||
|
||||
// ...
|
||||
apiStatuses, lo, hi, err := timeline.Load(ctx,
|
||||
|
||||
|
|
Loading…
Reference in a new issue