mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-10-29 18:42:26 -05:00
start adding page validation
This commit is contained in:
parent
e96bf44497
commit
4e80d0bf79
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 ...
|
// RemoveByStatusIDs ...
|
||||||
func (t *StatusTimelines) RemoveByStatusIDs(statusIDs ...string) {
|
func (t *StatusTimelines) RemoveByStatusIDs(statusIDs ...string) {
|
||||||
if p := t.ptr.Load(); p != nil {
|
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 ...
|
// UnprepareAll ...
|
||||||
func (t *StatusTimelines) UnprepareAll() {
|
func (t *StatusTimelines) UnprepareAll() {
|
||||||
if p := t.ptr.Load(); p != nil {
|
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.
|
// ID as primary key is inherently an index.
|
||||||
// {Fields: "ID"},
|
// {Fields: "ID"},
|
||||||
{Fields: "AccountID", Multiple: true},
|
{Fields: "AccountID", Multiple: true},
|
||||||
{Fields: "BoostOfStatusID", Multiple: true},
|
{Fields: "BoostOfID", Multiple: true},
|
||||||
{Fields: "BoostOfAccountID", Multiple: true},
|
{Fields: "BoostOfAccountID", Multiple: true},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -370,7 +374,7 @@ func (t *StatusTimeline) Load(
|
||||||
|
|
||||||
// Before we can do any filtering, we need
|
// Before we can do any filtering, we need
|
||||||
// to load status models for cached entries.
|
// to load status models for cached entries.
|
||||||
err := loadStatuses(ctx, metas, loadIDs)
|
err := loadStatuses(metas, loadIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", "", gtserror.Newf("error loading statuses: %w", err)
|
return nil, "", "", gtserror.Newf("error loading statuses: %w", err)
|
||||||
}
|
}
|
||||||
|
|
@ -710,12 +714,16 @@ func (t *StatusTimeline) prepare(
|
||||||
return apiStatuses, nil
|
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(
|
func loadStatuses(
|
||||||
ctx context.Context,
|
|
||||||
metas []*StatusMeta,
|
metas []*StatusMeta,
|
||||||
loadIDs func([]string) ([]*gtsmodel.Status, error),
|
loadIDs func([]string) ([]*gtsmodel.Status, error),
|
||||||
) error {
|
) error {
|
||||||
|
|
||||||
// Determine which of our passed status
|
// Determine which of our passed status
|
||||||
// meta objects still need statuses loading.
|
// meta objects still need statuses loading.
|
||||||
toLoadIDs := make([]string, len(metas))
|
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
|
package timeline
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
@ -5,15 +22,21 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/paging"
|
"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(
|
func nextPageParams(
|
||||||
curLo, curHi string, // current page params
|
curLo, curHi string,
|
||||||
nextLo, nextHi string, // next lo / hi values
|
nextLo, nextHi string,
|
||||||
order paging.Order,
|
order paging.Order,
|
||||||
) (lo string, hi string) {
|
) (lo string, hi string) {
|
||||||
if order.Ascending() {
|
if order.Ascending() {
|
||||||
|
return nextLo, curHi
|
||||||
} else /* i.e. descending */ {
|
} 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/filter/visibility"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/paging"
|
"github.com/superseriousbusiness/gotosocial/internal/paging"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||||
|
|
@ -106,6 +107,10 @@ func (p *Processor) getStatusTimeline(
|
||||||
mutes = usermute.NewCompiledUserMuteList(allMutes)
|
mutes = usermute.NewCompiledUserMuteList(allMutes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure we have valid
|
||||||
|
// input paging data.
|
||||||
|
id.ValidatePage(page)
|
||||||
|
|
||||||
// ...
|
// ...
|
||||||
apiStatuses, lo, hi, err := timeline.Load(ctx,
|
apiStatuses, lo, hi, err := timeline.Load(ctx,
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue