mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-12-30 00:36:14 -06:00
store both new and old revision emojis in status
This commit is contained in:
parent
870b511481
commit
7dadeeb4b9
6 changed files with 89 additions and 109 deletions
|
|
@ -18,13 +18,55 @@
|
||||||
package util
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
"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
|
// ParseDuration parses the given raw interface belonging
|
||||||
// the given fieldName as an integer duration.
|
// the given fieldName as an integer duration.
|
||||||
func ParseDuration(rawI any, fieldName string) (*int, error) {
|
func ParseDuration(rawI any, fieldName string) (*int, error) {
|
||||||
|
|
|
||||||
|
|
@ -1197,6 +1197,13 @@ func (d *Dereferencer) handleStatusEdit(
|
||||||
// Attached emojis changed.
|
// Attached emojis changed.
|
||||||
cols = append(cols, "emojis") // i.e. EmojiIDs
|
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
|
// Emojis changed doesn't necessarily
|
||||||
// indicate an edit, it may just not have
|
// indicate an edit, it may just not have
|
||||||
// been previously populated properly.
|
// been previously populated properly.
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import (
|
||||||
|
|
||||||
"codeberg.org/gruf/go-iotools"
|
"codeberg.org/gruf/go-iotools"
|
||||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
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/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"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.
|
// Parse focus details from API form input.
|
||||||
focusX, focusY, err := parseFocus(form.Focus)
|
focusX, focusY, errWithCode := apiutil.ParseFocus(form.Focus)
|
||||||
if err != nil {
|
if errWithCode != nil {
|
||||||
text := fmt.Sprintf("could not parse focus value %s: %s", form.Focus, err)
|
return nil, errWithCode
|
||||||
return nil, gtserror.NewErrorBadRequest(errors.New(text), text)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open multipart file reader.
|
// Open multipart file reader.
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
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/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"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 {
|
if form.Focus != nil {
|
||||||
focusx, focusy, err := parseFocus(*form.Focus)
|
focusx, focusy, errWithCode := apiutil.ParseFocus(*form.Focus)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, gtserror.NewErrorBadRequest(err)
|
return nil, errWithCode
|
||||||
}
|
}
|
||||||
attachment.FileMeta.Focus.X = focusx
|
attachment.FileMeta.Focus.X = focusx
|
||||||
attachment.FileMeta.Focus.Y = focusy
|
attachment.FileMeta.Focus.Y = focusy
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -22,18 +22,18 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
||||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
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/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/id"
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/util/xslices"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Edit ...
|
// Edit ...
|
||||||
|
|
@ -222,10 +222,15 @@ func (p *Processor) Edit(
|
||||||
}
|
}
|
||||||
|
|
||||||
if !slices.Equal(status.EmojiIDs, content.EmojiIDs) {
|
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.
|
// Update attached status emojis.
|
||||||
cols = append(cols, "emojis")
|
cols = append(cols, "emojis")
|
||||||
status.EmojiIDs = content.EmojiIDs
|
|
||||||
status.Emojis = content.Emojis
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -391,8 +396,8 @@ func (p *Processor) processMediaEdits(
|
||||||
}
|
}
|
||||||
|
|
||||||
if attr.Focus != "" {
|
if attr.Focus != "" {
|
||||||
// Parse provided media focus string request.
|
// Parse provided media focus parameters from string.
|
||||||
fx, fy, errWithCode := parseFocus(attr.Focus)
|
fx, fy, errWithCode := apiutil.ParseFocus(attr.Focus)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
return false, errWithCode
|
return false, errWithCode
|
||||||
}
|
}
|
||||||
|
|
@ -544,40 +549,27 @@ func (p *Processor) deletePoll(ctx context.Context, poll *gtsmodel.Poll) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseFocus(focus string) (focusx, focusy float32, errWithCode gtserror.WithCode) {
|
func deduplicateEmojis(emojis []*gtsmodel.Emoji) ([]*gtsmodel.Emoji, []string) {
|
||||||
if focus == "" {
|
set := make(map[string]struct{}, len(emojis))
|
||||||
return
|
|
||||||
|
// 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
|
||||||
}
|
}
|
||||||
spl := strings.Split(focus, ",")
|
|
||||||
if len(spl) != 2 {
|
// Append emoji to slices.
|
||||||
const text = "missing comma separator"
|
ret = append(ret, emoji)
|
||||||
errWithCode = gtserror.NewErrorBadRequest(
|
ids = append(ids, emoji.ID)
|
||||||
errors.New(text),
|
|
||||||
text,
|
// Add emoji ID to set.
|
||||||
)
|
set[emoji.ID] = struct{}{}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
xStr := spl[0]
|
|
||||||
yStr := spl[1]
|
return ret, ids
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue