[feature/frontend] Reports frontend v2 (#3022)

* use apiutil + paging in admin processor+handlers

* we're making it happen

* fix little whoopsie

* styling for report list

* don't youuuu forget about meee don't don't don't don't

* last bits

* sanitize content before showing in report statuses

* update report docs
This commit is contained in:
tobi 2024-06-18 18:18:00 +02:00 committed by GitHub
commit d2b3d37724
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
56 changed files with 1389 additions and 726 deletions

View file

@ -20,6 +20,7 @@ package bundb
import (
"context"
"errors"
"slices"
"time"
"github.com/superseriousbusiness/gotosocial/internal/db"
@ -27,6 +28,7 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/log"
"github.com/superseriousbusiness/gotosocial/internal/paging"
"github.com/superseriousbusiness/gotosocial/internal/state"
"github.com/uptrace/bun"
)
@ -51,14 +53,23 @@ func (r *reportDB) GetReportByID(ctx context.Context, id string) (*gtsmodel.Repo
)
}
func (r *reportDB) GetReports(ctx context.Context, resolved *bool, accountID string, targetAccountID string, maxID string, sinceID string, minID string, limit int) ([]*gtsmodel.Report, error) {
reportIDs := []string{}
func (r *reportDB) GetReports(ctx context.Context, resolved *bool, accountID string, targetAccountID string, page *paging.Page) ([]*gtsmodel.Report, error) {
var (
// Get paging params.
minID = page.GetMin()
maxID = page.GetMax()
limit = page.GetLimit()
order = page.GetOrder()
// Make educated guess for slice size
reportIDs = make([]string, 0, limit)
)
q := r.db.
NewSelect().
TableExpr("? AS ?", bun.Ident("reports"), bun.Ident("report")).
Column("report.id").
Order("report.id DESC")
// Select only IDs from table.
Column("report.id")
if resolved != nil {
i := bun.Ident("report.action_taken_by_account_id")
@ -77,22 +88,32 @@ func (r *reportDB) GetReports(ctx context.Context, resolved *bool, accountID str
q = q.Where("? = ?", bun.Ident("report.target_account_id"), targetAccountID)
}
// Return only reports with id
// lower than provided maxID.
if maxID != "" {
q = q.Where("? < ?", bun.Ident("report.id"), maxID)
}
if sinceID != "" {
q = q.Where("? > ?", bun.Ident("report.id"), minID)
}
// Return only reports with id
// greater than provided minID.
if minID != "" {
q = q.Where("? > ?", bun.Ident("report.id"), minID)
}
if limit != 0 {
if limit > 0 {
// Limit amount of
// reports returned.
q = q.Limit(limit)
}
if order == paging.OrderAscending {
// Page up.
q = q.OrderExpr("? ASC", bun.Ident("report.id"))
} else {
// Page down.
q = q.OrderExpr("? DESC", bun.Ident("report.id"))
}
if err := q.Scan(ctx, &reportIDs); err != nil {
return nil, err
}
@ -102,6 +123,12 @@ func (r *reportDB) GetReports(ctx context.Context, resolved *bool, accountID str
return nil, db.ErrNoEntries
}
// If we're paging up, we still want reports
// to be sorted by ID desc, so reverse ids slice.
if order == paging.OrderAscending {
slices.Reverse(reportIDs)
}
// Allocate return slice (will be at most len reportIDs)
reports := make([]*gtsmodel.Report, 0, len(reportIDs))
for _, id := range reportIDs {

View file

@ -24,6 +24,8 @@ import (
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/id"
"github.com/superseriousbusiness/gotosocial/internal/paging"
"github.com/superseriousbusiness/gotosocial/internal/util"
"github.com/superseriousbusiness/gotosocial/testrig"
)
@ -61,14 +63,109 @@ func (suite *ReportTestSuite) TestGetReportByURI() {
}
func (suite *ReportTestSuite) TestGetAllReports() {
reports, err := suite.db.GetReports(context.Background(), nil, "", "", "", "", "", 0)
reports, err := suite.db.GetReports(
context.Background(),
nil,
"",
"",
&paging.Page{},
)
suite.NoError(err)
suite.NotEmpty(reports)
}
func (suite *ReportTestSuite) TestReportPagingDown() {
// Get one from the top.
reports1, err := suite.db.GetReports(
context.Background(),
nil,
"",
"",
&paging.Page{
Limit: 1,
},
)
if err != nil {
suite.FailNow(err.Error())
}
if l := len(reports1); l != 1 {
suite.FailNowf("", "expected reports len 1, got %d", l)
}
id1 := reports1[0].ID
// Use this one to page down.
reports2, err := suite.db.GetReports(
context.Background(),
nil,
"",
"",
&paging.Page{
Limit: 1,
Max: paging.MaxID(id1),
},
)
if err != nil {
suite.FailNow(err.Error())
}
if l := len(reports2); l != 1 {
suite.FailNowf("", "expected reports len 1, got %d", l)
}
id2 := reports2[0].ID
suite.Greater(id1, id2)
}
func (suite *ReportTestSuite) TestReportPagingUp() {
// Get one from the bottom.
reports1, err := suite.db.GetReports(
context.Background(),
nil,
"",
"",
&paging.Page{
Limit: 1,
Min: paging.MinID(id.Lowest),
},
)
if err != nil {
suite.FailNow(err.Error())
}
if l := len(reports1); l != 1 {
suite.FailNowf("", "expected reports len 1, got %d", l)
}
id1 := reports1[0].ID
// Use this one to page up.
reports2, err := suite.db.GetReports(
context.Background(),
nil,
"",
"",
&paging.Page{
Limit: 1,
Min: paging.MinID(id1),
},
)
if err != nil {
suite.FailNow(err.Error())
}
if l := len(reports2); l != 1 {
suite.FailNowf("", "expected reports len 1, got %d", l)
}
id2 := reports2[0].ID
suite.Less(id1, id2)
}
func (suite *ReportTestSuite) TestGetAllReportsByAccountID() {
accountID := suite.testAccounts["local_account_2"].ID
reports, err := suite.db.GetReports(context.Background(), nil, accountID, "", "", "", "", 0)
reports, err := suite.db.GetReports(
context.Background(),
nil,
accountID,
"",
&paging.Page{},
)
suite.NoError(err)
suite.NotEmpty(reports)
for _, r := range reports {

View file

@ -21,6 +21,7 @@ import (
"context"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/paging"
)
// Report handles getting/creation/deletion/updating of user reports/flags.
@ -30,7 +31,7 @@ type Report interface {
// GetReports gets limit n reports using the given parameters.
// Parameters that are empty / zero are ignored.
GetReports(ctx context.Context, resolved *bool, accountID string, targetAccountID string, maxID string, sinceID string, minID string, limit int) ([]*gtsmodel.Report, error)
GetReports(ctx context.Context, resolved *bool, accountID string, targetAccountID string, page *paging.Page) ([]*gtsmodel.Report, error)
// PopulateReport populates the struct pointers on the given report.
PopulateReport(ctx context.Context, report *gtsmodel.Report) error