store both new and old revision emojis in status

This commit is contained in:
kim 2024-12-18 12:49:30 +00:00
commit 7dadeeb4b9
6 changed files with 89 additions and 109 deletions

View file

@ -18,13 +18,55 @@
package util
import (
"errors"
"fmt"
"strconv"
"strings"
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/util"
)
// ParseFocus parses a media attachment focus parameters from incoming API string.
func ParseFocus(focus string) (focusx, focusy float32, errWithCode gtserror.WithCode) {
if focus == "" {
return
}
spl := strings.Split(focus, ",")
if len(spl) != 2 {
const text = "missing comma separator"
errWithCode = gtserror.NewErrorBadRequest(
errors.New(text),
text,
)
return
}
xStr := spl[0]
yStr := spl[1]
fx, err := strconv.ParseFloat(xStr, 32)
if err != nil || fx > 1 || fx < -1 {
text := fmt.Sprintf("invalid x focus: %s", xStr)
errWithCode = gtserror.NewErrorBadRequest(
errors.New(text),
text,
)
return
}
fy, err := strconv.ParseFloat(yStr, 32)
if err != nil || fy > 1 || fy < -1 {
text := fmt.Sprintf("invalid y focus: %s", xStr)
errWithCode = gtserror.NewErrorBadRequest(
errors.New(text),
text,
)
return
}
focusx = float32(fx)
focusy = float32(fy)
return
}
// ParseDuration parses the given raw interface belonging
// the given fieldName as an integer duration.
func ParseDuration(rawI any, fieldName string) (*int, error) {

View file

@ -1197,6 +1197,13 @@ func (d *Dereferencer) handleStatusEdit(
// Attached emojis changed.
cols = append(cols, "emojis") // i.e. EmojiIDs
// We specifically store both *new* AND *old* edit
// revision emojis in the statuses.emojis column.
emojiByID := func(e *gtsmodel.Emoji) string { return e.ID }
status.Emojis = append(status.Emojis, existing.Emojis...)
status.Emojis = xslices.DeduplicateFunc(status.Emojis, emojiByID)
status.EmojiIDs = xslices.Gather(status.EmojiIDs[:0], status.Emojis, emojiByID)
// Emojis changed doesn't necessarily
// indicate an edit, it may just not have
// been previously populated properly.

View file

@ -25,6 +25,7 @@ import (
"codeberg.org/gruf/go-iotools"
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
@ -45,10 +46,9 @@ func (p *Processor) Create(ctx context.Context, account *gtsmodel.Account, form
}
// Parse focus details from API form input.
focusX, focusY, err := parseFocus(form.Focus)
if err != nil {
text := fmt.Sprintf("could not parse focus value %s: %s", form.Focus, err)
return nil, gtserror.NewErrorBadRequest(errors.New(text), text)
focusX, focusY, errWithCode := apiutil.ParseFocus(form.Focus)
if errWithCode != nil {
return nil, errWithCode
}
// Open multipart file reader.

View file

@ -23,6 +23,7 @@ import (
"fmt"
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
@ -52,9 +53,9 @@ func (p *Processor) Update(ctx context.Context, account *gtsmodel.Account, media
}
if form.Focus != nil {
focusx, focusy, err := parseFocus(*form.Focus)
focusx, focusy, errWithCode := apiutil.ParseFocus(*form.Focus)
if err != nil {
return nil, gtserror.NewErrorBadRequest(err)
return nil, errWithCode
}
attachment.FileMeta.Focus.X = focusx
attachment.FileMeta.Focus.Y = focusy

View file

@ -1,62 +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 media
import (
"fmt"
"strconv"
"strings"
)
func parseFocus(focus string) (focusx, focusy float32, err error) {
if focus == "" {
return
}
spl := strings.Split(focus, ",")
if len(spl) != 2 {
err = fmt.Errorf("improperly formatted focus %s", focus)
return
}
xStr := spl[0]
yStr := spl[1]
if xStr == "" || yStr == "" {
err = fmt.Errorf("improperly formatted focus %s", focus)
return
}
fx, err := strconv.ParseFloat(xStr, 32)
if err != nil {
err = fmt.Errorf("improperly formatted focus %s: %s", focus, err)
return
}
if fx > 1 || fx < -1 {
err = fmt.Errorf("improperly formatted focus %s", focus)
return
}
focusx = float32(fx)
fy, err := strconv.ParseFloat(yStr, 32)
if err != nil {
err = fmt.Errorf("improperly formatted focus %s: %s", focus, err)
return
}
if fy > 1 || fy < -1 {
err = fmt.Errorf("improperly formatted focus %s", focus)
return
}
focusy = float32(fy)
return
}

View file

@ -22,18 +22,18 @@ import (
"errors"
"fmt"
"slices"
"strconv"
"strings"
"time"
"github.com/superseriousbusiness/gotosocial/internal/ap"
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/id"
"github.com/superseriousbusiness/gotosocial/internal/log"
"github.com/superseriousbusiness/gotosocial/internal/messages"
"github.com/superseriousbusiness/gotosocial/internal/util/xslices"
)
// Edit ...
@ -222,10 +222,15 @@ func (p *Processor) Edit(
}
if !slices.Equal(status.EmojiIDs, content.EmojiIDs) {
// We specifically store both *new* AND *old* edit
// revision emojis in the statuses.emojis column.
emojiByID := func(e *gtsmodel.Emoji) string { return e.ID }
status.Emojis = append(status.Emojis, content.Emojis...)
status.Emojis = xslices.DeduplicateFunc(status.Emojis, emojiByID)
status.EmojiIDs = xslices.Gather(status.EmojiIDs[:0], status.Emojis, emojiByID)
// Update attached status emojis.
cols = append(cols, "emojis")
status.EmojiIDs = content.EmojiIDs
status.Emojis = content.Emojis
}
}
@ -391,8 +396,8 @@ func (p *Processor) processMediaEdits(
}
if attr.Focus != "" {
// Parse provided media focus string request.
fx, fy, errWithCode := parseFocus(attr.Focus)
// Parse provided media focus parameters from string.
fx, fy, errWithCode := apiutil.ParseFocus(attr.Focus)
if errWithCode != nil {
return false, errWithCode
}
@ -544,40 +549,27 @@ func (p *Processor) deletePoll(ctx context.Context, poll *gtsmodel.Poll) error {
return nil
}
func parseFocus(focus string) (focusx, focusy float32, errWithCode gtserror.WithCode) {
if focus == "" {
return
func deduplicateEmojis(emojis []*gtsmodel.Emoji) ([]*gtsmodel.Emoji, []string) {
set := make(map[string]struct{}, len(emojis))
// Store returning emojis, and their IDs.
ret := make([]*gtsmodel.Emoji, 0, len(emojis))
ids := make([]string, 0, len(emojis))
for _, emoji := range emojis {
emoji := emoji // rescope
// Check if already in ID set.
if _, ok := set[emoji.ID]; ok {
continue
}
// Append emoji to slices.
ret = append(ret, emoji)
ids = append(ids, emoji.ID)
// Add emoji ID to set.
set[emoji.ID] = struct{}{}
}
spl := strings.Split(focus, ",")
if len(spl) != 2 {
const text = "missing comma separator"
errWithCode = gtserror.NewErrorBadRequest(
errors.New(text),
text,
)
return
}
xStr := spl[0]
yStr := spl[1]
fx, err := strconv.ParseFloat(xStr, 32)
if err != nil || fx > 1 || fx < -1 {
text := fmt.Sprintf("invalid x focus: %s", xStr)
errWithCode = gtserror.NewErrorBadRequest(
errors.New(text),
text,
)
return
}
fy, err := strconv.ParseFloat(yStr, 32)
if err != nil || fy > 1 || fy < -1 {
text := fmt.Sprintf("invalid y focus: %s", xStr)
errWithCode = gtserror.NewErrorBadRequest(
errors.New(text),
text,
)
return
}
focusx = float32(fx)
focusy = float32(fy)
return
return ret, ids
}