diff --git a/internal/db/db.go b/internal/db/db.go index f3a2889..4e21358 100644 --- a/internal/db/db.go +++ b/internal/db/db.go @@ -217,6 +217,12 @@ type DB interface { // GetFaveCountForStatus returns the amount of faves/likes recorded for a status, or an error if something goes wrong GetFaveCountForStatus(status *gtsmodel.Status) (int, error) + // StatusParents get the parent statuses of a given status. + StatusParents(status *gtsmodel.Status) ([]*gtsmodel.Status, error) + + // StatusChildren gets the child statuses of a given status. + StatusChildren(status *gtsmodel.Status) ([]*gtsmodel.Status, error) + // StatusFavedBy checks if a given status has been faved by a given account ID StatusFavedBy(status *gtsmodel.Status, accountID string) (bool, error) diff --git a/internal/db/pg/pg.go b/internal/db/pg/pg.go index 5a03d81..8515013 100644 --- a/internal/db/pg/pg.go +++ b/internal/db/pg/pg.go @@ -807,14 +807,26 @@ func (ps *postgresService) GetRelationship(requestingAccount string, targetAccou } func (ps *postgresService) Follows(sourceAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) (bool, error) { + if sourceAccount == nil || targetAccount == nil { + return false, nil + } + return ps.conn.Model(>smodel.Follow{}).Where("account_id = ?", sourceAccount.ID).Where("target_account_id = ?", targetAccount.ID).Exists() } func (ps *postgresService) FollowRequested(sourceAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) (bool, error) { + if sourceAccount == nil || targetAccount == nil { + return false, nil + } + return ps.conn.Model(>smodel.FollowRequest{}).Where("account_id = ?", sourceAccount.ID).Where("target_account_id = ?", targetAccount.ID).Exists() } func (ps *postgresService) Mutuals(account1 *gtsmodel.Account, account2 *gtsmodel.Account) (bool, error) { + if account1 == nil || account2 == nil { + return false, nil + } + // make sure account 1 follows account 2 f1, err := ps.conn.Model(>smodel.Follow{}).Where("account_id = ?", account1.ID).Where("target_account_id = ?", account2.ID).Exists() if err != nil { diff --git a/internal/db/pg/statuscontext.go b/internal/db/pg/statuscontext.go new file mode 100644 index 0000000..b88ef2e --- /dev/null +++ b/internal/db/pg/statuscontext.go @@ -0,0 +1,50 @@ +package pg + +import ( + "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" + "sync" +) + +func (ps *postgresService) StatusParents(status *gtsmodel.Status) ([]*gtsmodel.Status, error) { + parents := []*gtsmodel.Status{} + ps.statusParent(status, &parents) + + return parents, nil +} + +func (ps *postgresService) statusParent(status *gtsmodel.Status, foundStatuses *[]*gtsmodel.Status) { + if status.InReplyToID == "" { + return + } + + parentStatus := >smodel.Status{} + if err := ps.conn.Model(parentStatus).Where("id = ?", status.InReplyToID).Select(); err == nil { + *foundStatuses = append(*foundStatuses, parentStatus) + } + + ps.statusParent(parentStatus, foundStatuses) +} + +func (ps *postgresService) StatusChildren(status *gtsmodel.Status) ([]*gtsmodel.Status, error) { + children := []*gtsmodel.Status{} + // ps.statusChildren(status, &children) + + return children, nil +} + +func (ps *postgresService) statusChildren(status *gtsmodel.Status, foundStatuses *sync.Map) { + // immediateChildren := []*gtsmodel.Status{} + + // foundStatuses.Store() + + // err := ps.conn.Model(&immediateChildren).Where("in_reply_to_id = ?", status.ID).Select() + // if err != nil { + // return + // } + + // for _, child := range immediateChildren { + // f[""][0] = child + // } + + return +} diff --git a/internal/processing/synchronous/status/context.go b/internal/processing/synchronous/status/context.go index cac8681..4673982 100644 --- a/internal/processing/synchronous/status/context.go +++ b/internal/processing/synchronous/status/context.go @@ -1,14 +1,73 @@ package status import ( + "fmt" + "sort" + apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" + "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" ) func (p *processor) Context(account *gtsmodel.Account, targetStatusID string) (*apimodel.Context, gtserror.WithCode) { - return &apimodel.Context{ + + context := &apimodel.Context{ Ancestors: []apimodel.Status{}, Descendants: []apimodel.Status{}, - }, nil + } + + targetStatus := >smodel.Status{} + if err := p.db.GetByID(targetStatusID, targetStatus); err != nil { + if _, ok := err.(db.ErrNoEntries); ok { + return nil, gtserror.NewErrorNotFound(err) + } + return nil, gtserror.NewErrorInternalError(err) + } + + visible, err := p.filter.StatusVisible(targetStatus, account) + if err != nil { + return nil, gtserror.NewErrorNotFound(err) + } + if !visible { + return nil, gtserror.NewErrorForbidden(fmt.Errorf("account with id %s does not have permission to view status %s", account.ID, targetStatusID)) + } + + parents, err := p.db.StatusParents(targetStatus) + if err != nil { + return nil, gtserror.NewErrorInternalError(err) + } + + for _, status := range parents { + if v, err := p.filter.StatusVisible(status, account); err == nil && v { + mastoStatus, err := p.tc.StatusToMasto(status, account) + if err == nil { + context.Ancestors = append(context.Ancestors, *mastoStatus) + } + } + } + + children, err := p.db.StatusChildren(targetStatus) + if err != nil { + return nil, gtserror.NewErrorInternalError(err) + } + + for _, status := range children { + if v, err := p.filter.StatusVisible(status, account); err == nil && v { + mastoStatus, err := p.tc.StatusToMasto(status, account) + if err == nil { + context.Ancestors = append(context.Ancestors, *mastoStatus) + } + } + } + + sort.Slice(context.Ancestors, func(i int, j int) bool { + return context.Ancestors[i].ID < context.Ancestors[j].ID + }) + + sort.Slice(context.Descendants, func(i int, j int) bool { + return context.Descendants[i].ID < context.Descendants[j].ID + }) + + return context, nil } diff --git a/internal/visibility/statushometimelineable.go b/internal/visibility/statushometimelineable.go index 130c2df..62c0acc 100644 --- a/internal/visibility/statushometimelineable.go +++ b/internal/visibility/statushometimelineable.go @@ -11,11 +11,10 @@ func (f *filter) StatusHometimelineable(targetStatus *gtsmodel.Status, requestin l := f.log.WithFields(logrus.Fields{ "func": "StatusHometimelineable", "statusID": targetStatus.ID, - "requestingAccountID": requestingAccount.ID, }) // status owner should always be able to see their status in their timeline so we can return early if this is the case - if targetStatus.AccountID == requestingAccount.ID { + if requestingAccount != nil && targetStatus.AccountID == requestingAccount.ID { return true, nil }