refining status views etc

This commit is contained in:
tsmethurst 2021-05-17 16:54:56 +02:00
commit eb9de53776
14 changed files with 184 additions and 90 deletions

View file

@ -34,6 +34,15 @@ import (
const ( const (
// LimitKey is for setting the return amount limit for eg., requesting an account's statuses // LimitKey is for setting the return amount limit for eg., requesting an account's statuses
LimitKey = "limit" LimitKey = "limit"
// ExcludeRepliesKey is for specifying whether to exclude replies in a list of returned statuses by an account.
ExcludeRepliesKey = "exclude_replies"
// PinnedKey is for specifying whether to include pinned statuses in a list of returned statuses by an account.
PinnedKey = "pinned"
// MaxIDKey is for specifying the maximum ID of the status to retrieve.
MaxIDKey = "max_id"
// MediaOnlyKey is for specifying that only statuses with media should be returned in a list of returned statuses by an account.
MediaOnlyKey = "only_media"
// IDKey is the key to use for retrieving account ID in requests // IDKey is the key to use for retrieving account ID in requests
IDKey = "id" IDKey = "id"
// BasePath is the base API path for this module // BasePath is the base API path for this module

View file

@ -27,15 +27,27 @@ import (
) )
// AccountStatusesGETHandler serves the statuses of the requested account, if they're visible to the requester. // AccountStatusesGETHandler serves the statuses of the requested account, if they're visible to the requester.
//
// Several different filters might be passed into this function in the query:
//
// limit -- show only limit number of statuses
// exclude_replies -- exclude statuses that are a reply to another status
// max_id -- the maximum ID of the status to show
// pinned -- show only pinned statuses
// media_only -- show only statuses that have media attachments
func (m *Module) AccountStatusesGETHandler(c *gin.Context) { func (m *Module) AccountStatusesGETHandler(c *gin.Context) {
l := m.log.WithField("func", "AccountStatusesGETHandler")
authed, err := oauth.Authed(c, false, false, false, false) authed, err := oauth.Authed(c, false, false, false, false)
if err != nil { if err != nil {
l.Debugf("error authing: %s", err)
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"}) c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
return return
} }
targetAcctID := c.Param(IDKey) targetAcctID := c.Param(IDKey)
if targetAcctID == "" { if targetAcctID == "" {
l.Debug("no account id specified in query")
c.JSON(http.StatusBadRequest, gin.H{"error": "no account id specified"}) c.JSON(http.StatusBadRequest, gin.H{"error": "no account id specified"})
return return
} }
@ -43,16 +55,60 @@ func (m *Module) AccountStatusesGETHandler(c *gin.Context) {
limit := 30 limit := 30
limitString := c.Query(LimitKey) limitString := c.Query(LimitKey)
if limitString != "" { if limitString != "" {
l, err := strconv.ParseInt(limitString, 10, 64) i, err := strconv.ParseInt(limitString, 10, 64)
if err != nil { if err != nil {
l.Debugf("error parsing limit string: %s", err)
c.JSON(http.StatusBadRequest, gin.H{"error": "couldn't parse limit query param"}) c.JSON(http.StatusBadRequest, gin.H{"error": "couldn't parse limit query param"})
return return
} }
limit = int(l) limit = int(i)
} }
statuses, errWithCode := m.processor.AccountStatusesGet(authed, targetAcctID, limit) excludeReplies := false
excludeRepliesString := c.Query(ExcludeRepliesKey)
if excludeRepliesString != "" {
i, err := strconv.ParseBool(excludeRepliesString)
if err != nil {
l.Debugf("error parsing replies string: %s", err)
c.JSON(http.StatusBadRequest, gin.H{"error": "couldn't parse exclude replies query param"})
return
}
excludeReplies = i
}
maxID := ""
maxIDString := c.Query(MaxIDKey)
if maxIDString != "" {
maxID = maxIDString
}
pinned := false
pinnedString := c.Query(PinnedKey)
if pinnedString != "" {
i, err := strconv.ParseBool(pinnedString)
if err != nil {
l.Debugf("error parsing pinned string: %s", err)
c.JSON(http.StatusBadRequest, gin.H{"error": "couldn't parse pinned query param"})
return
}
pinned = i
}
mediaOnly := false
mediaOnlyString := c.Query(MediaOnlyKey)
if mediaOnlyString != "" {
i, err := strconv.ParseBool(mediaOnlyString)
if err != nil {
l.Debugf("error parsing media only string: %s", err)
c.JSON(http.StatusBadRequest, gin.H{"error": "couldn't parse media only query param"})
return
}
mediaOnly = i
}
statuses, errWithCode := m.processor.AccountStatusesGet(authed, targetAcctID, limit, excludeReplies, maxID, pinned, mediaOnly)
if errWithCode != nil { if errWithCode != nil {
l.Debugf("error from processor account statuses get: %s", errWithCode)
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()}) c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
return return
} }

View file

@ -55,7 +55,7 @@ type Status struct {
// Have you bookmarked this status? // Have you bookmarked this status?
Bookmarked bool `json:"bookmarked"` Bookmarked bool `json:"bookmarked"`
// Have you pinned this status? Only appears if the status is pinnable. // Have you pinned this status? Only appears if the status is pinnable.
Pinned bool `json:"pinned"` Pinned bool `json:"pinned,omitempty"`
// HTML-encoded status content. // HTML-encoded status content.
Content string `json:"content"` Content string `json:"content"`
// The status being reblogged. // The status being reblogged.

View file

@ -56,7 +56,7 @@ func (m *Module) UsersGETHandler(c *gin.Context) {
// make a copy of the context to pass along so we don't break anything // make a copy of the context to pass along so we don't break anything
cp := c.Copy() cp := c.Copy()
user, err := m.processor.GetFediUser(requestedUsername, cp.Request) // GetAPUser handles auth as well user, err := m.processor.GetFediUser(requestedUsername, cp.Request) // GetFediUser handles auth as well
if err != nil { if err != nil {
l.Info(err.Error()) l.Info(err.Error())
c.JSON(err.Code(), gin.H{"error": err.Safe()}) c.JSON(err.Code(), gin.H{"error": err.Safe()})

View file

@ -160,16 +160,14 @@ type DB interface {
// In case of no entries, a 'no entries' error will be returned // In case of no entries, a 'no entries' error will be returned
GetFavesByAccountID(accountID string, faves *[]gtsmodel.StatusFave) error GetFavesByAccountID(accountID string, faves *[]gtsmodel.StatusFave) error
// GetStatusesByAccountID is a shortcut for the common action of fetching a list of statuses produced by accountID. // CountStatusesByAccountID is a shortcut for the common action of counting statuses produced by accountID.
// The given slice 'statuses' will be set to the result of the query, whatever it is. CountStatusesByAccountID(accountID string) (int, error)
// In case of no entries, a 'no entries' error will be returned
GetStatusesByAccountID(accountID string, statuses *[]gtsmodel.Status) error
// GetStatusesByTimeDescending is a shortcut for getting the most recent statuses. accountID is optional, if not provided // GetStatusesByTimeDescending is a shortcut for getting the most recent statuses. accountID is optional, if not provided
// then all statuses will be returned. If limit is set to 0, the size of the returned slice will not be limited. This can // then all statuses will be returned. If limit is set to 0, the size of the returned slice will not be limited. This can
// be very memory intensive so you probably shouldn't do this! // be very memory intensive so you probably shouldn't do this!
// In case of no entries, a 'no entries' error will be returned // In case of no entries, a 'no entries' error will be returned
GetStatusesByTimeDescending(accountID string, statuses *[]gtsmodel.Status, limit int) error GetStatusesByTimeDescending(accountID string, statuses *[]gtsmodel.Status, limit int, excludeReplies bool, maxID string, pinned bool, mediaOnly bool) error
// GetLastStatusForAccountID simply gets the most recent status by the given account. // GetLastStatusForAccountID simply gets the most recent status by the given account.
// The given slice 'status' pointer will be set to the result of the query, whatever it is. // The given slice 'status' pointer will be set to the result of the query, whatever it is.
@ -251,9 +249,6 @@ type DB interface {
// StatusBookmarkedBy checks if a given status has been bookmarked by a given account ID // StatusBookmarkedBy checks if a given status has been bookmarked by a given account ID
StatusBookmarkedBy(status *gtsmodel.Status, accountID string) (bool, error) StatusBookmarkedBy(status *gtsmodel.Status, accountID string) (bool, error)
// StatusPinnedBy checks if a given status has been pinned by a given account ID
StatusPinnedBy(status *gtsmodel.Status, accountID string) (bool, error)
// FaveStatus faves the given status, using accountID as the faver. // FaveStatus faves the given status, using accountID as the faver.
// The returned fave will be nil if the status was already faved. // The returned fave will be nil if the status was already faved.
FaveStatus(status *gtsmodel.Status, accountID string) (*gtsmodel.StatusFave, error) FaveStatus(status *gtsmodel.Status, accountID string) (*gtsmodel.StatusFave, error)

View file

@ -456,23 +456,35 @@ func (ps *postgresService) GetFavesByAccountID(accountID string, faves *[]gtsmod
return nil return nil
} }
func (ps *postgresService) GetStatusesByAccountID(accountID string, statuses *[]gtsmodel.Status) error { func (ps *postgresService) CountStatusesByAccountID(accountID string) (int, error) {
if err := ps.conn.Model(statuses).Where("account_id = ?", accountID).Select(); err != nil { count, err := ps.conn.Model(&gtsmodel.Status{}).Where("account_id = ?", accountID).Count()
if err != nil {
if err == pg.ErrNoRows { if err == pg.ErrNoRows {
return db.ErrNoEntries{} return 0, nil
} }
return err return 0, err
} }
return nil return count, nil
} }
func (ps *postgresService) GetStatusesByTimeDescending(accountID string, statuses *[]gtsmodel.Status, limit int) error { func (ps *postgresService) GetStatusesByTimeDescending(accountID string, statuses *[]gtsmodel.Status, limit int, excludeReplies bool, maxID string, pinned bool, mediaOnly bool) error {
q := ps.conn.Model(statuses).Order("created_at DESC") q := ps.conn.Model(statuses).Order("created_at DESC")
if accountID != "" {
q = q.Where("account_id = ?", accountID)
}
if limit != 0 { if limit != 0 {
q = q.Limit(limit) q = q.Limit(limit)
} }
if accountID != "" { if excludeReplies {
q = q.Where("account_id = ?", accountID) q = q.Where("? IS NULL", pg.Ident("in_reply_to_id"))
}
if pinned {
q = q.Where("pinned = ?", true)
}
if mediaOnly {
q = q.WhereGroup(func(q *pg.Query) (*pg.Query, error) {
return q.Where("? IS NOT NULL", pg.Ident("attachments")).Where("attachments != '{}'"), nil
})
} }
if err := q.Select(); err != nil { if err := q.Select(); err != nil {
if err == pg.ErrNoRows { if err == pg.ErrNoRows {
@ -824,8 +836,8 @@ func (ps *postgresService) StatusVisible(targetStatus *gtsmodel.Status, targetAc
return true, nil return true, nil
case gtsmodel.VisibilityDirect: case gtsmodel.VisibilityDirect:
// make sure the requesting account is mentioned in the status // make sure the requesting account is mentioned in the status
for _, menchie := range targetStatus.Mentions { for _, acct := range relevantAccounts.MentionedAccounts {
if menchie == requestingAccount.ID { if acct.ID == requestingAccount.ID {
return true, nil // yep it's mentioned! return true, nil // yep it's mentioned!
} }
} }
@ -900,10 +912,16 @@ func (ps *postgresService) PullRelevantAccountsFromStatus(targetStatus *gtsmodel
} }
// now get all accounts with IDs that are mentioned in the status // now get all accounts with IDs that are mentioned in the status
for _, mentionedAccountID := range targetStatus.Mentions { for _, mentionID := range targetStatus.Mentions {
mention := &gtsmodel.Mention{}
if err := ps.conn.Model(mention).Where("id = ?", mentionID).Select(); err != nil {
return accounts, fmt.Errorf("error getting mention with id %s: %s", mentionID, err)
}
mentionedAccount := &gtsmodel.Account{} mentionedAccount := &gtsmodel.Account{}
if err := ps.conn.Model(mentionedAccount).Where("id = ?", mentionedAccountID).Select(); err != nil { if err := ps.conn.Model(mentionedAccount).Where("id = ?", mention.TargetAccountID).Select(); err != nil {
return accounts, err return accounts, fmt.Errorf("error getting mentioned account: %s", err)
} }
accounts.MentionedAccounts = append(accounts.MentionedAccounts, mentionedAccount) accounts.MentionedAccounts = append(accounts.MentionedAccounts, mentionedAccount)
} }
@ -939,10 +957,6 @@ func (ps *postgresService) StatusBookmarkedBy(status *gtsmodel.Status, accountID
return ps.conn.Model(&gtsmodel.StatusBookmark{}).Where("status_id = ?", status.ID).Where("account_id = ?", accountID).Exists() return ps.conn.Model(&gtsmodel.StatusBookmark{}).Where("status_id = ?", status.ID).Where("account_id = ?", accountID).Exists()
} }
func (ps *postgresService) StatusPinnedBy(status *gtsmodel.Status, accountID string) (bool, error) {
return ps.conn.Model(&gtsmodel.StatusPin{}).Where("status_id = ?", status.ID).Where("account_id = ?", accountID).Exists()
}
func (ps *postgresService) FaveStatus(status *gtsmodel.Status, accountID string) (*gtsmodel.StatusFave, error) { func (ps *postgresService) FaveStatus(status *gtsmodel.Status, accountID string) (*gtsmodel.StatusFave, error) {
// first check if a fave already exists, we can just return if so // first check if a fave already exists, we can just return if so
existingFave := &gtsmodel.StatusFave{} existingFave := &gtsmodel.StatusFave{}

View file

@ -68,7 +68,6 @@ var models []interface{} = []interface{}{
&gtsmodel.StatusFave{}, &gtsmodel.StatusFave{},
&gtsmodel.StatusBookmark{}, &gtsmodel.StatusBookmark{},
&gtsmodel.StatusMute{}, &gtsmodel.StatusMute{},
&gtsmodel.StatusPin{},
&gtsmodel.Tag{}, &gtsmodel.Tag{},
&gtsmodel.User{}, &gtsmodel.User{},
&gtsmodel.Emoji{}, &gtsmodel.Emoji{},

View file

@ -34,7 +34,7 @@ type Status struct {
Attachments []string `pg:",array"` Attachments []string `pg:",array"`
// Database IDs of any tags used in this status // Database IDs of any tags used in this status
Tags []string `pg:",array"` Tags []string `pg:",array"`
// Database IDs of any accounts mentioned in this status // Database IDs of any mentions in this status
Mentions []string `pg:",array"` Mentions []string `pg:",array"`
// Database IDs of any emojis used in this status // Database IDs of any emojis used in this status
Emojis []string `pg:",array"` Emojis []string `pg:",array"`
@ -69,6 +69,8 @@ type Status struct {
ActivityStreamsType ActivityStreamsObject ActivityStreamsType ActivityStreamsObject
// Original text of the status without formatting // Original text of the status without formatting
Text string Text string
// Has this status been pinned by its owner?
Pinned bool
/* /*
INTERNAL MODEL NON-DATABASE FIELDS INTERNAL MODEL NON-DATABASE FIELDS

View file

@ -1,33 +0,0 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
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 gtsmodel
import "time"
// StatusPin refers to a status 'pinned' to the top of an account
type StatusPin struct {
// id of this pin in the database
ID string `pg:"type:uuid,default:gen_random_uuid(),pk,notnull,unique"`
// when was this pin created
CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
// id of the account that created ('did') the pinning (this should always be the same as the author of the status)
AccountID string `pg:",notnull"`
// database id of the status that has been pinned
StatusID string `pg:",notnull"`
}

View file

@ -185,7 +185,7 @@ func (p *processor) AccountUpdate(authed *oauth.Auth, form *apimodel.UpdateCrede
return acctSensitive, nil return acctSensitive, nil
} }
func (p *processor) AccountStatusesGet(authed *oauth.Auth, targetAccountID string, limit int) ([]apimodel.Status, ErrorWithCode) { func (p *processor) AccountStatusesGet(authed *oauth.Auth, targetAccountID string, limit int, excludeReplies bool, maxID string, pinned bool, mediaOnly bool) ([]apimodel.Status, ErrorWithCode) {
targetAccount := &gtsmodel.Account{} targetAccount := &gtsmodel.Account{}
if err := p.db.GetByID(targetAccountID, targetAccount); err != nil { if err := p.db.GetByID(targetAccountID, targetAccount); err != nil {
if _, ok := err.(db.ErrNoEntries); ok { if _, ok := err.(db.ErrNoEntries); ok {
@ -196,7 +196,7 @@ func (p *processor) AccountStatusesGet(authed *oauth.Auth, targetAccountID strin
statuses := []gtsmodel.Status{} statuses := []gtsmodel.Status{}
apiStatuses := []apimodel.Status{} apiStatuses := []apimodel.Status{}
if err := p.db.GetStatusesByTimeDescending(targetAccountID, &statuses, limit); err != nil { if err := p.db.GetStatusesByTimeDescending(targetAccountID, &statuses, limit, excludeReplies, maxID, pinned, mediaOnly); err != nil {
if _, ok := err.(db.ErrNoEntries); ok { if _, ok := err.(db.ErrNoEntries); ok {
return apiStatuses, nil return apiStatuses, nil
} }
@ -206,12 +206,12 @@ func (p *processor) AccountStatusesGet(authed *oauth.Auth, targetAccountID strin
for _, s := range statuses { for _, s := range statuses {
relevantAccounts, err := p.db.PullRelevantAccountsFromStatus(&s) relevantAccounts, err := p.db.PullRelevantAccountsFromStatus(&s)
if err != nil { if err != nil {
return nil, NewErrorInternalError(err) return nil, NewErrorInternalError(fmt.Errorf("error getting relevant statuses: %s", err))
} }
visible, err := p.db.StatusVisible(&s, targetAccount, authed.Account, relevantAccounts) visible, err := p.db.StatusVisible(&s, targetAccount, authed.Account, relevantAccounts)
if err != nil { if err != nil {
return nil, NewErrorInternalError(err) return nil, NewErrorInternalError(fmt.Errorf("error checking status visibility: %s", err))
} }
if !visible { if !visible {
continue continue
@ -221,16 +221,16 @@ func (p *processor) AccountStatusesGet(authed *oauth.Auth, targetAccountID strin
if s.BoostOfID != "" { if s.BoostOfID != "" {
bs := &gtsmodel.Status{} bs := &gtsmodel.Status{}
if err := p.db.GetByID(s.BoostOfID, bs); err != nil { if err := p.db.GetByID(s.BoostOfID, bs); err != nil {
return nil, NewErrorInternalError(err) return nil, NewErrorInternalError(fmt.Errorf("error getting boosted status: %s", err))
} }
boostedRelevantAccounts, err := p.db.PullRelevantAccountsFromStatus(bs) boostedRelevantAccounts, err := p.db.PullRelevantAccountsFromStatus(bs)
if err != nil { if err != nil {
return nil, NewErrorInternalError(err) return nil, NewErrorInternalError(fmt.Errorf("error getting relevant accounts from boosted status: %s", err))
} }
boostedVisible, err := p.db.StatusVisible(bs, relevantAccounts.BoostedAccount, authed.Account, boostedRelevantAccounts) boostedVisible, err := p.db.StatusVisible(bs, relevantAccounts.BoostedAccount, authed.Account, boostedRelevantAccounts)
if err != nil { if err != nil {
return nil, NewErrorInternalError(err) return nil, NewErrorInternalError(fmt.Errorf("error checking boosted status visibility: %s", err))
} }
if boostedVisible { if boostedVisible {
@ -240,7 +240,7 @@ func (p *processor) AccountStatusesGet(authed *oauth.Auth, targetAccountID strin
apiStatus, err := p.tc.StatusToMasto(&s, targetAccount, authed.Account, relevantAccounts.BoostedAccount, relevantAccounts.ReplyToAccount, boostedStatus) apiStatus, err := p.tc.StatusToMasto(&s, targetAccount, authed.Account, relevantAccounts.BoostedAccount, relevantAccounts.ReplyToAccount, boostedStatus)
if err != nil { if err != nil {
return nil, NewErrorInternalError(err) return nil, NewErrorInternalError(fmt.Errorf("error converting status to masto: %s", err))
} }
apiStatuses = append(apiStatuses, *apiStatus) apiStatuses = append(apiStatuses, *apiStatus)

View file

@ -21,7 +21,9 @@ package message
import ( import (
"errors" "errors"
"fmt" "fmt"
"net/url"
"github.com/google/uuid"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
@ -99,6 +101,12 @@ func (p *processor) dereferenceStatusFields(status *gtsmodel.Status) error {
return fmt.Errorf("error creating transport: %s", err) return fmt.Errorf("error creating transport: %s", err)
} }
// the status should have an ID by now, but just in case it doesn't let's generate one here
// because we'll need it further down
if status.ID == "" {
status.ID = uuid.NewString()
}
// 1. Media attachments. // 1. Media attachments.
// //
// At this point we should know: // At this point we should know:
@ -132,7 +140,7 @@ func (p *processor) dereferenceStatusFields(status *gtsmodel.Status) error {
continue continue
} }
l.Debugf("dereferenced attachment: %+v", deferencedAttachment) l.Debugf("dereferenced attachment: %+v", deferencedAttachment)
deferencedAttachment.StatusID = deferencedAttachment.ID deferencedAttachment.StatusID = status.ID
if err := p.db.Put(deferencedAttachment); err != nil { if err := p.db.Put(deferencedAttachment); err != nil {
return fmt.Errorf("error inserting dereferenced attachment with remote url %s: %s", a.RemoteURL, err) return fmt.Errorf("error inserting dereferenced attachment with remote url %s: %s", a.RemoteURL, err)
} }
@ -141,5 +149,60 @@ func (p *processor) dereferenceStatusFields(status *gtsmodel.Status) error {
} }
status.Attachments = attachmentIDs status.Attachments = attachmentIDs
// 2. Hashtags
// 3. Emojis
// 4. Mentions
// At this point, mentions should have the namestring and mentionedAccountURI set on them.
//
// We should dereference any accounts mentioned here which we don't have in our db yet, by their URI.
mentions := []string{}
for _, m := range status.GTSMentions {
uri, err := url.Parse(m.MentionedAccountURI)
if err != nil {
l.Debugf("error parsing mentioned account uri %s: %s", m.MentionedAccountURI, err)
continue
}
m.StatusID = status.ID
m.OriginAccountID = status.GTSAccount.ID
m.OriginAccountURI = status.GTSAccount.URI
targetAccount := &gtsmodel.Account{}
if err := p.db.GetWhere("uri", uri.String(), targetAccount); err != nil {
// proper error
if _, ok := err.(db.ErrNoEntries); !ok {
return fmt.Errorf("db error checking for account with uri %s", uri.String())
}
// we just don't have it yet, so we should go get it....
accountable, err := p.federator.DereferenceRemoteAccount(username, uri)
if err != nil {
// we can't dereference it so just skip it
l.Debugf("error dereferencing remote account with uri %s: %s", uri.String(), err)
continue
}
targetAccount, err = p.tc.ASRepresentationToAccount(accountable)
if err != nil {
l.Debugf("error converting remote account with uri %s into gts model: %s", uri.String(), err)
continue
}
if err := p.db.Put(targetAccount); err != nil {
return fmt.Errorf("db error inserting account with uri %s", uri.String())
}
}
// by this point, we know the targetAccount exists in our database with an ID :)
m.TargetAccountID = targetAccount.ID
if err := p.db.Put(m); err != nil {
return fmt.Errorf("error creating mention: %s", err)
}
mentions = append(mentions, m.ID)
}
status.Mentions = mentions
return nil return nil
} }

View file

@ -70,7 +70,7 @@ type Processor interface {
AccountUpdate(authed *oauth.Auth, form *apimodel.UpdateCredentialsRequest) (*apimodel.Account, error) AccountUpdate(authed *oauth.Auth, form *apimodel.UpdateCredentialsRequest) (*apimodel.Account, error)
// AccountStatusesGet fetches a number of statuses (in time descending order) from the given account, filtered by visibility for // AccountStatusesGet fetches a number of statuses (in time descending order) from the given account, filtered by visibility for
// the account given in authed. // the account given in authed.
AccountStatusesGet(authed *oauth.Auth, targetAccountID string, limit int) ([]apimodel.Status, ErrorWithCode) AccountStatusesGet(authed *oauth.Auth, targetAccountID string, limit int, excludeReplies bool, maxID string, pinned bool, mediaOnly bool) ([]apimodel.Status, ErrorWithCode)
// AccountFollowersGet // AccountFollowersGet
AccountFollowersGet(authed *oauth.Auth, targetAccountID string) ([]apimodel.Account, ErrorWithCode) AccountFollowersGet(authed *oauth.Auth, targetAccountID string) ([]apimodel.Account, ErrorWithCode)

View file

@ -87,16 +87,12 @@ func (c *converter) AccountToMastoPublic(a *gtsmodel.Account) (*model.Account, e
} }
// count statuses // count statuses
statuses := []gtsmodel.Status{} statusesCount, err := c.db.CountStatusesByAccountID(a.ID)
if err := c.db.GetStatusesByAccountID(a.ID, &statuses); err != nil { if err != nil {
if _, ok := err.(db.ErrNoEntries); !ok { if _, ok := err.(db.ErrNoEntries); !ok {
return nil, fmt.Errorf("error getting last statuses: %s", err) return nil, fmt.Errorf("error getting last statuses: %s", err)
} }
} }
var statusesCount int
if statuses != nil {
statusesCount = len(statuses)
}
// check when the last status was // check when the last status was
lastStatus := &gtsmodel.Status{} lastStatus := &gtsmodel.Status{}
@ -295,7 +291,6 @@ func (c *converter) StatusToMasto(
var faved bool var faved bool
var reblogged bool var reblogged bool
var bookmarked bool var bookmarked bool
var pinned bool
var muted bool var muted bool
// requestingAccount will be nil for public requests without auth // requestingAccount will be nil for public requests without auth
@ -320,11 +315,6 @@ func (c *converter) StatusToMasto(
if err != nil { if err != nil {
return nil, fmt.Errorf("error checking if requesting account has bookmarked status: %s", err) return nil, fmt.Errorf("error checking if requesting account has bookmarked status: %s", err)
} }
pinned, err = c.db.StatusPinnedBy(s, requestingAccount.ID)
if err != nil {
return nil, fmt.Errorf("error checking if requesting account has pinned status: %s", err)
}
} }
var mastoRebloggedStatus *model.Status var mastoRebloggedStatus *model.Status
@ -523,7 +513,7 @@ func (c *converter) StatusToMasto(
Reblogged: reblogged, Reblogged: reblogged,
Muted: muted, Muted: muted,
Bookmarked: bookmarked, Bookmarked: bookmarked,
Pinned: pinned, Pinned: s.Pinned,
Content: s.Content, Content: s.Content,
Reblog: mastoRebloggedStatus, Reblog: mastoRebloggedStatus,
Application: mastoApplication, Application: mastoApplication,

View file

@ -42,7 +42,6 @@ var testModels []interface{} = []interface{}{
&gtsmodel.StatusFave{}, &gtsmodel.StatusFave{},
&gtsmodel.StatusBookmark{}, &gtsmodel.StatusBookmark{},
&gtsmodel.StatusMute{}, &gtsmodel.StatusMute{},
&gtsmodel.StatusPin{},
&gtsmodel.Tag{}, &gtsmodel.Tag{},
&gtsmodel.User{}, &gtsmodel.User{},
&gtsmodel.Emoji{}, &gtsmodel.Emoji{},