summaryrefslogtreecommitdiff
path: root/internal/db
diff options
context:
space:
mode:
authorLibravatar tobi <31960611+tsmethurst@users.noreply.github.com>2025-03-26 16:59:39 +0100
committerLibravatar GitHub <noreply@github.com>2025-03-26 15:59:39 +0000
commitb6e481d63eec15191f2717957682c13ee8a68308 (patch)
tree03cb9fc8bcb5f9eefddee754ad64b9de10c44c39 /internal/db
parent[chore] bumps our spf13/viper version (#3943) (diff)
downloadgotosocial-b6e481d63eec15191f2717957682c13ee8a68308.tar.xz
[feature] Allow user to choose "gallery" style layout for web view of profile (#3917)
* [feature] Allow user to choose "gallery" style web layout * find a bug and squish it up and all day long you'll have good luck * just a sec * [performance] reindex public timeline + tinker with query a bit * fiddling * should be good now * last bit of finagling, i'm done now i prommy * panic normally
Diffstat (limited to 'internal/db')
-rw-r--r--internal/db/account.go2
-rw-r--r--internal/db/bundb/account.go67
-rw-r--r--internal/db/bundb/account_test.go10
-rw-r--r--internal/db/bundb/basic_test.go2
-rw-r--r--internal/db/bundb/instance_test.go4
-rw-r--r--internal/db/bundb/migrations/20250314120945_add_gallery_web_layout.go85
6 files changed, 136 insertions, 34 deletions
diff --git a/internal/db/account.go b/internal/db/account.go
index aa0dfd985..cfb81308f 100644
--- a/internal/db/account.go
+++ b/internal/db/account.go
@@ -121,7 +121,7 @@ type Account interface {
// returning statuses that should be visible via the web view of a *LOCAL* account.
//
// In the case of no statuses, this function will return db.ErrNoEntries.
- GetAccountWebStatuses(ctx context.Context, account *gtsmodel.Account, limit int, maxID string) ([]*gtsmodel.Status, error)
+ GetAccountWebStatuses(ctx context.Context, account *gtsmodel.Account, mediaOnly bool, limit int, maxID string) ([]*gtsmodel.Status, error)
// GetInstanceAccount returns the instance account for the given domain.
// If domain is empty, this instance account will be returned.
diff --git a/internal/db/bundb/account.go b/internal/db/bundb/account.go
index f905101e4..aacfcd247 100644
--- a/internal/db/bundb/account.go
+++ b/internal/db/bundb/account.go
@@ -878,6 +878,29 @@ func (a *accountDB) GetAccountFaves(ctx context.Context, accountID string) ([]*g
return *faves, nil
}
+func qMediaOnly(q *bun.SelectQuery) *bun.SelectQuery {
+ // Attachments are stored as a json object; this
+ // implementation differs between SQLite and Postgres,
+ // so we have to be thorough to cover all eventualities
+ return q.WhereGroup(" AND ", func(q *bun.SelectQuery) *bun.SelectQuery {
+ switch d := q.Dialect().Name(); d {
+ case dialect.PG:
+ return q.
+ Where("? IS NOT NULL", bun.Ident("status.attachments")).
+ Where("? != '{}'", bun.Ident("status.attachments"))
+
+ case dialect.SQLite:
+ return q.
+ Where("? IS NOT NULL", bun.Ident("status.attachments")).
+ Where("? != 'null'", bun.Ident("status.attachments")).
+ Where("? != '[]'", bun.Ident("status.attachments"))
+
+ default:
+ panic("dialect " + d.String() + " was neither pg nor sqlite")
+ }
+ })
+}
+
func (a *accountDB) GetAccountStatuses(ctx context.Context, accountID string, limit int, excludeReplies bool, excludeReblogs bool, maxID string, minID string, mediaOnly bool, publicOnly bool) ([]*gtsmodel.Status, error) {
// Ensure reasonable
if limit < 0 {
@@ -918,28 +941,9 @@ func (a *accountDB) GetAccountStatuses(ctx context.Context, accountID string, li
q = q.Where("? IS NULL", bun.Ident("status.boost_of_id"))
}
+ // Respect media-only preference.
if mediaOnly {
- // Attachments are stored as a json object; this
- // implementation differs between SQLite and Postgres,
- // so we have to be thorough to cover all eventualities
- q = q.WhereGroup(" AND ", func(q *bun.SelectQuery) *bun.SelectQuery {
- switch a.db.Dialect().Name() {
- case dialect.PG:
- return q.
- Where("? IS NOT NULL", bun.Ident("status.attachments")).
- Where("? != '{}'", bun.Ident("status.attachments"))
- case dialect.SQLite:
- return q.
- Where("? IS NOT NULL", bun.Ident("status.attachments")).
- Where("? != ''", bun.Ident("status.attachments")).
- Where("? != 'null'", bun.Ident("status.attachments")).
- Where("? != '{}'", bun.Ident("status.attachments")).
- Where("? != '[]'", bun.Ident("status.attachments"))
- default:
- log.Panic(ctx, "db dialect was neither pg nor sqlite")
- return q
- }
- })
+ q = qMediaOnly(q)
}
if publicOnly {
@@ -1018,6 +1022,7 @@ func (a *accountDB) GetAccountPinnedStatuses(ctx context.Context, accountID stri
func (a *accountDB) GetAccountWebStatuses(
ctx context.Context,
account *gtsmodel.Account,
+ mediaOnly bool,
limit int,
maxID string,
) ([]*gtsmodel.Status, error) {
@@ -1046,10 +1051,7 @@ func (a *accountDB) GetAccountWebStatuses(
TableExpr("? AS ?", bun.Ident("statuses"), bun.Ident("status")).
// Select only IDs from table
Column("status.id").
- Where("? = ?", bun.Ident("status.account_id"), account.ID).
- // Don't show replies or boosts.
- Where("? IS NULL", bun.Ident("status.in_reply_to_uri")).
- Where("? IS NULL", bun.Ident("status.boost_of_id"))
+ Where("? = ?", bun.Ident("status.account_id"), account.ID)
// Select statuses for this account according
// to their web visibility preference.
@@ -1074,10 +1076,19 @@ func (a *accountDB) GetAccountWebStatuses(
)
}
- // Don't show local-only statuses on the web view.
- q = q.Where("? = ?", bun.Ident("status.federated"), true)
+ // Don't show replies, boosts, or
+ // local-only statuses on the web view.
+ q = q.
+ Where("? IS NULL", bun.Ident("status.in_reply_to_uri")).
+ Where("? IS NULL", bun.Ident("status.boost_of_id")).
+ Where("? = ?", bun.Ident("status.federated"), true)
+
+ // Respect media-only preference.
+ if mediaOnly {
+ q = qMediaOnly(q)
+ }
- // return only statuses LOWER (ie., older) than maxID
+ // Return only statuses LOWER (ie., older) than maxID
if maxID == "" {
maxID = id.Highest
}
diff --git a/internal/db/bundb/account_test.go b/internal/db/bundb/account_test.go
index 879250408..e3d36855e 100644
--- a/internal/db/bundb/account_test.go
+++ b/internal/db/bundb/account_test.go
@@ -49,6 +49,12 @@ func (suite *AccountTestSuite) TestGetAccountStatuses() {
suite.Len(statuses, 9)
}
+func (suite *AccountTestSuite) TestGetAccountWebStatusesMediaOnly() {
+ statuses, err := suite.db.GetAccountWebStatuses(context.Background(), suite.testAccounts["local_account_3"], true, 20, "")
+ suite.NoError(err)
+ suite.Len(statuses, 2)
+}
+
func (suite *AccountTestSuite) TestGetAccountStatusesPageDown() {
// get the first page
statuses, err := suite.db.GetAccountStatuses(context.Background(), suite.testAccounts["local_account_1"].ID, 3, false, false, "", "", false, false)
@@ -490,7 +496,7 @@ func (suite *AccountTestSuite) TestGetAccountsAll() {
suite.FailNow(err.Error())
}
- suite.Len(accounts, 9)
+ suite.Len(accounts, 10)
}
func (suite *AccountTestSuite) TestGetAccountsMaxID() {
@@ -564,7 +570,7 @@ func (suite *AccountTestSuite) TestGetAccountsMinID() {
suite.FailNow(err.Error())
}
- suite.Len(accounts, 3)
+ suite.Len(accounts, 4)
}
func (suite *AccountTestSuite) TestGetAccountsModsOnly() {
diff --git a/internal/db/bundb/basic_test.go b/internal/db/bundb/basic_test.go
index e20aab765..1f2d1ac48 100644
--- a/internal/db/bundb/basic_test.go
+++ b/internal/db/bundb/basic_test.go
@@ -114,7 +114,7 @@ func (suite *BasicTestSuite) TestGetAllStatuses() {
s := []*gtsmodel.Status{}
err := suite.db.GetAll(context.Background(), &s)
suite.NoError(err)
- suite.Len(s, 28)
+ suite.Len(s, 30)
}
func (suite *BasicTestSuite) TestGetAllNotNull() {
diff --git a/internal/db/bundb/instance_test.go b/internal/db/bundb/instance_test.go
index 1364bacc2..c0d63003d 100644
--- a/internal/db/bundb/instance_test.go
+++ b/internal/db/bundb/instance_test.go
@@ -35,7 +35,7 @@ type InstanceTestSuite struct {
func (suite *InstanceTestSuite) TestCountInstanceUsers() {
count, err := suite.db.CountInstanceUsers(context.Background(), config.GetHost())
suite.NoError(err)
- suite.Equal(4, count)
+ suite.Equal(5, count)
}
func (suite *InstanceTestSuite) TestCountInstanceUsersRemote() {
@@ -47,7 +47,7 @@ func (suite *InstanceTestSuite) TestCountInstanceUsersRemote() {
func (suite *InstanceTestSuite) TestCountInstanceStatuses() {
count, err := suite.db.CountInstanceStatuses(context.Background(), config.GetHost())
suite.NoError(err)
- suite.Equal(21, count)
+ suite.Equal(23, count)
}
func (suite *InstanceTestSuite) TestCountInstanceStatusesRemote() {
diff --git a/internal/db/bundb/migrations/20250314120945_add_gallery_web_layout.go b/internal/db/bundb/migrations/20250314120945_add_gallery_web_layout.go
new file mode 100644
index 000000000..64b133cd5
--- /dev/null
+++ b/internal/db/bundb/migrations/20250314120945_add_gallery_web_layout.go
@@ -0,0 +1,85 @@
+// 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 migrations
+
+import (
+ "context"
+
+ "github.com/superseriousbusiness/gotosocial/internal/log"
+ "github.com/uptrace/bun"
+)
+
+func init() {
+ up := func(ctx context.Context, db *bun.DB) error {
+ return db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error {
+
+ // Add new column to settings.
+ if _, err := tx.
+ NewAddColumn().
+ Table("account_settings").
+ ColumnExpr(
+ "? SMALLINT NOT NULL DEFAULT ?",
+ bun.Ident("web_layout"), 1,
+ ).
+ Exec(ctx); err != nil {
+ return err
+ }
+
+ // Drop existing statuses web index as it's out of date.
+ log.Info(ctx, "updating statuses_profile_web_view_idx, this may take a while, please wait!")
+ if _, err := tx.
+ NewDropIndex().
+ Index("statuses_profile_web_view_idx").
+ IfExists().
+ Exec(ctx); err != nil {
+ return err
+ }
+
+ // Note: "attachments" field is not included in
+ // the index below as SQLite is fussy about using it,
+ // and it prevents this index from being used
+ // properly in non media-only queries.
+ if _, err := tx.
+ NewCreateIndex().
+ Table("statuses").
+ Index("statuses_profile_web_view_idx").
+ Column(
+ "account_id",
+ "visibility",
+ "in_reply_to_uri",
+ "boost_of_id",
+ "federated",
+ ).
+ ColumnExpr("? DESC", bun.Ident("id")).
+ IfNotExists().
+ Exec(ctx); err != nil {
+ return err
+ }
+
+ return nil
+ })
+ }
+
+ down := func(ctx context.Context, db *bun.DB) error {
+ return nil
+ }
+
+ if err := Migrations.Register(up, down); err != nil {
+ panic(err)
+ }
+}