fix(mentions)_: add callID for `OnChangeText` (#3806)

This commit is contained in:
frank 2024-09-19 13:03:56 +08:00 committed by GitHub
parent 11a27bb2bd
commit 6e5a32c022
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 68 additions and 29 deletions

View File

@ -7,6 +7,7 @@ import (
"fmt" "fmt"
"regexp" "regexp"
"strings" "strings"
"sync"
"unicode" "unicode"
"unicode/utf8" "unicode/utf8"
@ -167,6 +168,10 @@ type ChatMentionContext struct {
MentionState *MentionState MentionState *MentionState
PreviousText string // user input text before the last change PreviousText string // user input text before the last change
NewText string NewText string
CallID uint64
mu *sync.Mutex
LatestCallID uint64
} }
func NewChatMentionContext(chatID string) *ChatMentionContext { func NewChatMentionContext(chatID string) *ChatMentionContext {
@ -174,6 +179,7 @@ func NewChatMentionContext(chatID string) *ChatMentionContext {
ChatID: chatID, ChatID: chatID,
MentionSuggestions: make(map[string]*MentionableUser), MentionSuggestions: make(map[string]*MentionableUser),
MentionState: new(MentionState), MentionState: new(MentionState),
mu: new(sync.Mutex),
} }
} }
@ -295,11 +301,27 @@ func (m *MentionManager) ReplaceWithPublicKey(chatID, text string) (string, erro
return newText, nil return newText, nil
} }
func (m *MentionManager) OnChangeText(chatID, fullText string) (*ChatMentionContext, error) { func withCallID(ctx *ChatMentionContext, callID uint64) *ChatMentionContext {
result := *ctx
result.CallID = callID
return &result
}
func (m *MentionManager) OnChangeText(chatID, fullText string, callID uint64) (*ChatMentionContext, error) {
ctx := m.getChatMentionContext(chatID) ctx := m.getChatMentionContext(chatID)
if callID > 0 {
ctx.mu.Lock()
if callID <= ctx.LatestCallID {
ctx.mu.Unlock()
return withCallID(ctx, callID), fmt.Errorf("callID is less than or equal to latestCallID, callID: %d, maxCallID: %d", callID, ctx.LatestCallID)
}
ctx.LatestCallID = callID
ctx.mu.Unlock()
}
diff := diffText(ctx.PreviousText, fullText) diff := diffText(ctx.PreviousText, fullText)
if diff == nil { if diff == nil {
return ctx, nil return withCallID(ctx, callID), nil
} }
ctx.PreviousText = fullText ctx.PreviousText = fullText
if ctx.MentionState == nil { if ctx.MentionState == nil {
@ -313,11 +335,12 @@ func (m *MentionManager) OnChangeText(chatID, fullText string) (*ChatMentionCont
atIndexes, err := calculateAtIndexEntries(ctx.MentionState) atIndexes, err := calculateAtIndexEntries(ctx.MentionState)
if err != nil { if err != nil {
return ctx, err return withCallID(ctx, callID), err
} }
ctx.MentionState.AtIdxs = atIndexes ctx.MentionState.AtIdxs = atIndexes
m.logger.Debug("OnChangeText", zap.String("chatID", chatID), zap.Any("state", ctx.MentionState)) m.logger.Debug("OnChangeText", zap.String("chatID", chatID), zap.Any("state", ctx.MentionState))
return m.calculateSuggestions(chatID, fullText) ctx, err = m.calculateSuggestions(chatID, fullText)
return withCallID(ctx, callID), err
} }
func (m *MentionManager) calculateSuggestions(chatID, fullText string) (*ChatMentionContext, error) { func (m *MentionManager) calculateSuggestions(chatID, fullText string) (*ChatMentionContext, error) {
@ -403,7 +426,7 @@ func (m *MentionManager) SelectMention(chatID, text, primaryName, publicKey stri
ctx.NewText = string(tr[:atSignIdx+1]) + primaryName + space + string(tr[mentionEnd:]) ctx.NewText = string(tr[:atSignIdx+1]) + primaryName + space + string(tr[mentionEnd:])
ctx, err := m.OnChangeText(chatID, ctx.NewText) _, err := m.OnChangeText(chatID, ctx.NewText, 0)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -794,7 +794,7 @@ func TestMentionSuggestionCases(t *testing.T) {
for i, tc := range testCases { for i, tc := range testCases {
t.Run(fmt.Sprintf("%d", i+1), func(t *testing.T) { t.Run(fmt.Sprintf("%d", i+1), func(t *testing.T) {
ctx, err := mentionManager.OnChangeText(chatID, tc.inputText) ctx, err := mentionManager.OnChangeText(chatID, tc.inputText, uint64(i+1))
require.NoError(t, err) require.NoError(t, err)
t.Logf("Input: %+v, MentionState:%+v, InputSegments:%+v\n", tc.inputText, ctx.MentionState, ctx.InputSegments) t.Logf("Input: %+v, MentionState:%+v, InputSegments:%+v\n", tc.inputText, ctx.MentionState, ctx.InputSegments)
require.Equal(t, tc.expectedSize, len(ctx.MentionSuggestions)) require.Equal(t, tc.expectedSize, len(ctx.MentionSuggestions))
@ -806,7 +806,7 @@ func TestMentionSuggestionAfterToInputField(t *testing.T) {
mentionableUserMap, chatID, mentionManager := setupMentionSuggestionTest(t, nil) mentionableUserMap, chatID, mentionManager := setupMentionSuggestionTest(t, nil)
_, err := mentionManager.ToInputField(chatID, "abc") _, err := mentionManager.ToInputField(chatID, "abc")
require.NoError(t, err) require.NoError(t, err)
ctx, err := mentionManager.OnChangeText(chatID, "@") ctx, err := mentionManager.OnChangeText(chatID, "@", 1)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, len(mentionableUserMap), len(ctx.MentionSuggestions)) require.Equal(t, len(mentionableUserMap), len(ctx.MentionSuggestions))
} }
@ -826,7 +826,7 @@ func TestMentionSuggestionSpecialInputModeForAndroid(t *testing.T) {
for i, tc := range testCases { for i, tc := range testCases {
t.Run(fmt.Sprintf("%d", i+1), func(t *testing.T) { t.Run(fmt.Sprintf("%d", i+1), func(t *testing.T) {
ctx, err := mentionManager.OnChangeText(chatID, tc.inputText) ctx, err := mentionManager.OnChangeText(chatID, tc.inputText, uint64(i+1))
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, tc.expectedSize, len(ctx.MentionSuggestions)) require.Equal(t, tc.expectedSize, len(ctx.MentionSuggestions))
t.Logf("Input: %+v, MentionState:%+v, InputSegments:%+v\n", tc.inputText, ctx.MentionState, ctx.InputSegments) t.Logf("Input: %+v, MentionState:%+v, InputSegments:%+v\n", tc.inputText, ctx.MentionState, ctx.InputSegments)
@ -849,7 +849,7 @@ func TestMentionSuggestionSpecialChars(t *testing.T) {
for i, tc := range testCases { for i, tc := range testCases {
t.Run(fmt.Sprintf("%d", i+1), func(t *testing.T) { t.Run(fmt.Sprintf("%d", i+1), func(t *testing.T) {
ctx, err := mentionManager.OnChangeText(chatID, tc.inputText) ctx, err := mentionManager.OnChangeText(chatID, tc.inputText, uint64(i+1))
require.NoError(t, err) require.NoError(t, err)
t.Logf("Input: %+v, MentionState:%+v, InputSegments:%+v\n", tc.inputText, ctx.MentionState, ctx.InputSegments) t.Logf("Input: %+v, MentionState:%+v, InputSegments:%+v\n", tc.inputText, ctx.MentionState, ctx.InputSegments)
require.Equal(t, tc.expectedSize, len(ctx.MentionSuggestions)) require.Equal(t, tc.expectedSize, len(ctx.MentionSuggestions))
@ -878,8 +878,8 @@ func TestMentionSuggestionAtSignSpaceCases(t *testing.T) {
var ctx *ChatMentionContext var ctx *ChatMentionContext
var err error var err error
for _, tc := range testCases { for i, tc := range testCases {
ctx, err = mentionManager.OnChangeText(chatID, tc.inputText) ctx, err = mentionManager.OnChangeText(chatID, tc.inputText, uint64(i+1))
require.NoError(t, err) require.NoError(t, err)
t.Logf("After OnChangeText, Input: %+v, MentionState:%+v, InputSegments:%+v\n", tc.inputText, ctx.MentionState, ctx.InputSegments) t.Logf("After OnChangeText, Input: %+v, MentionState:%+v, InputSegments:%+v\n", tc.inputText, ctx.MentionState, ctx.InputSegments)
require.Equal(t, tc.expectedSize, len(ctx.MentionSuggestions)) require.Equal(t, tc.expectedSize, len(ctx.MentionSuggestions))
@ -894,12 +894,14 @@ func TestMentionSuggestionAtSignSpaceCases(t *testing.T) {
func TestSelectMention(t *testing.T) { func TestSelectMention(t *testing.T) {
mentionableUsers, chatID, mentionManager := setupMentionSuggestionTest(t, nil) mentionableUsers, chatID, mentionManager := setupMentionSuggestionTest(t, nil)
var callID uint64 = 1
text := "@u2 abc" text := "@u2 abc"
ctx, err := mentionManager.OnChangeText(chatID, text) ctx, err := mentionManager.OnChangeText(chatID, text, callID)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 0, len(ctx.MentionSuggestions)) require.Equal(t, 0, len(ctx.MentionSuggestions))
ctx, err = mentionManager.OnChangeText(chatID, "@u abc") callID++
ctx, err = mentionManager.OnChangeText(chatID, "@u abc", callID)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, len(mentionableUsers), len(ctx.MentionSuggestions)) require.Equal(t, len(mentionableUsers), len(ctx.MentionSuggestions))
@ -909,20 +911,23 @@ func TestSelectMention(t *testing.T) {
require.Equal(t, text, ctx.NewText) require.Equal(t, text, ctx.NewText)
require.Equal(t, text, ctx.PreviousText) require.Equal(t, text, ctx.PreviousText)
ctx, err = mentionManager.OnChangeText(chatID, text) callID++
ctx, err = mentionManager.OnChangeText(chatID, text, callID)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 0, len(ctx.MentionSuggestions)) require.Equal(t, 0, len(ctx.MentionSuggestions))
} }
func TestInputSegments(t *testing.T) { func TestInputSegments(t *testing.T) {
_, chatID, mentionManager := setupMentionSuggestionTest(t, nil) _, chatID, mentionManager := setupMentionSuggestionTest(t, nil)
ctx, err := mentionManager.OnChangeText(chatID, "@u1") var callID uint64 = 1
ctx, err := mentionManager.OnChangeText(chatID, "@u1", callID)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(ctx.InputSegments)) require.Equal(t, 1, len(ctx.InputSegments))
require.Equal(t, Text, ctx.InputSegments[0].Type) require.Equal(t, Text, ctx.InputSegments[0].Type)
require.Equal(t, "@u1", ctx.InputSegments[0].Value) require.Equal(t, "@u1", ctx.InputSegments[0].Value)
ctx, err = mentionManager.OnChangeText(chatID, "@u1 @User Number One") callID++
ctx, err = mentionManager.OnChangeText(chatID, "@u1 @User Number One", callID)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 2, len(ctx.InputSegments)) require.Equal(t, 2, len(ctx.InputSegments))
require.Equal(t, Text, ctx.InputSegments[0].Type) require.Equal(t, Text, ctx.InputSegments[0].Type)
@ -930,13 +935,15 @@ func TestInputSegments(t *testing.T) {
require.Equal(t, Mention, ctx.InputSegments[1].Type) require.Equal(t, Mention, ctx.InputSegments[1].Type)
require.Equal(t, "@User Number One", ctx.InputSegments[1].Value) require.Equal(t, "@User Number One", ctx.InputSegments[1].Value)
ctx, err = mentionManager.OnChangeText(chatID, "@u1 @User Number O") callID++
ctx, err = mentionManager.OnChangeText(chatID, "@u1 @User Number O", callID)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 2, len(ctx.InputSegments)) require.Equal(t, 2, len(ctx.InputSegments))
require.Equal(t, Text, ctx.InputSegments[1].Type) require.Equal(t, Text, ctx.InputSegments[1].Type)
require.Equal(t, "@User Number O", ctx.InputSegments[1].Value) require.Equal(t, "@User Number O", ctx.InputSegments[1].Value)
ctx, err = mentionManager.OnChangeText(chatID, "@u2 @User Number One") callID++
ctx, err = mentionManager.OnChangeText(chatID, "@u2 @User Number One", callID)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 3, len(ctx.InputSegments)) require.Equal(t, 3, len(ctx.InputSegments))
require.Equal(t, Mention, ctx.InputSegments[0].Type) require.Equal(t, Mention, ctx.InputSegments[0].Type)
@ -946,7 +953,8 @@ func TestInputSegments(t *testing.T) {
require.Equal(t, Mention, ctx.InputSegments[2].Type) require.Equal(t, Mention, ctx.InputSegments[2].Type)
require.Equal(t, "@User Number One", ctx.InputSegments[2].Value) require.Equal(t, "@User Number One", ctx.InputSegments[2].Value)
ctx, err = mentionManager.OnChangeText(chatID, "@u2 @User Number One a ") callID++
ctx, err = mentionManager.OnChangeText(chatID, "@u2 @User Number One a ", callID)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 4, len(ctx.InputSegments)) require.Equal(t, 4, len(ctx.InputSegments))
require.Equal(t, Mention, ctx.InputSegments[2].Type) require.Equal(t, Mention, ctx.InputSegments[2].Type)
@ -954,7 +962,8 @@ func TestInputSegments(t *testing.T) {
require.Equal(t, Text, ctx.InputSegments[3].Type) require.Equal(t, Text, ctx.InputSegments[3].Type)
require.Equal(t, " a ", ctx.InputSegments[3].Value) require.Equal(t, " a ", ctx.InputSegments[3].Value)
ctx, err = mentionManager.OnChangeText(chatID, "@u2 @User Numbed One a ") callID++
ctx, err = mentionManager.OnChangeText(chatID, "@u2 @User Numbed One a ", callID)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 3, len(ctx.InputSegments)) require.Equal(t, 3, len(ctx.InputSegments))
require.Equal(t, Mention, ctx.InputSegments[0].Type) require.Equal(t, Mention, ctx.InputSegments[0].Type)
@ -962,7 +971,8 @@ func TestInputSegments(t *testing.T) {
require.Equal(t, Text, ctx.InputSegments[2].Type) require.Equal(t, Text, ctx.InputSegments[2].Type)
require.Equal(t, "@User Numbed One a ", ctx.InputSegments[2].Value) require.Equal(t, "@User Numbed One a ", ctx.InputSegments[2].Value)
ctx, err = mentionManager.OnChangeText(chatID, "@ @ ") callID++
ctx, err = mentionManager.OnChangeText(chatID, "@ @ ", callID)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 2, len(ctx.InputSegments)) require.Equal(t, 2, len(ctx.InputSegments))
require.Equal(t, Text, ctx.InputSegments[0].Type) require.Equal(t, Text, ctx.InputSegments[0].Type)
@ -970,7 +980,8 @@ func TestInputSegments(t *testing.T) {
require.Equal(t, Text, ctx.InputSegments[1].Type) require.Equal(t, Text, ctx.InputSegments[1].Type)
require.Equal(t, "@ ", ctx.InputSegments[1].Value) require.Equal(t, "@ ", ctx.InputSegments[1].Value)
ctx, err = mentionManager.OnChangeText(chatID, "@u3 @ ") callID++
ctx, err = mentionManager.OnChangeText(chatID, "@u3 @ ", callID)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 3, len(ctx.InputSegments)) require.Equal(t, 3, len(ctx.InputSegments))
require.Equal(t, Mention, ctx.InputSegments[0].Type) require.Equal(t, Mention, ctx.InputSegments[0].Type)
@ -980,20 +991,25 @@ func TestInputSegments(t *testing.T) {
require.Equal(t, Text, ctx.InputSegments[2].Type) require.Equal(t, Text, ctx.InputSegments[2].Type)
require.Equal(t, "@ ", ctx.InputSegments[2].Value) require.Equal(t, "@ ", ctx.InputSegments[2].Value)
_, err = mentionManager.OnChangeText(chatID, " @ @User Number Three ") callID++
_, err = mentionManager.OnChangeText(chatID, " @ @User Number Three ", callID)
require.NoError(t, err) require.NoError(t, err)
_, err = mentionManager.OnChangeText(chatID, "@U @ @User Number Three ") callID++
_, err = mentionManager.OnChangeText(chatID, "@U @ @User Number Three ", callID)
require.NoError(t, err) require.NoError(t, err)
ctx, err = mentionManager.SelectMention(chatID, "@U @ @User Number Three ", "User Number Three", "0xpk3") ctx, err = mentionManager.SelectMention(chatID, "@U @ @User Number Three ", "User Number Three", "0xpk3")
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 2, mentionTypeNum(ctx.InputSegments)) require.Equal(t, 2, mentionTypeNum(ctx.InputSegments))
ctx, _ = mentionManager.OnChangeText(chatID, "@User Number Threea") callID++
ctx, _ = mentionManager.OnChangeText(chatID, "@User Number Threea", callID)
require.Equal(t, 0, mentionTypeNum(ctx.InputSegments)) require.Equal(t, 0, mentionTypeNum(ctx.InputSegments))
ctx, _ = mentionManager.OnChangeText(chatID, "@User Number Threea\n@u2\nabc@u3 asa") callID++
ctx, _ = mentionManager.OnChangeText(chatID, "@User Number Threea\n@u2\nabc@u3 asa", callID)
require.Equal(t, 2, mentionTypeNum(ctx.InputSegments)) require.Equal(t, 2, mentionTypeNum(ctx.InputSegments))
ctx, _ = mentionManager.OnChangeText(chatID, "@User Number Thre\n@u2\nabc@u3 asa") callID++
ctx, _ = mentionManager.OnChangeText(chatID, "@User Number Thre\n@u2\nabc@u3 asa", callID)
require.Equal(t, 2, mentionTypeNum(ctx.InputSegments)) require.Equal(t, 2, mentionTypeNum(ctx.InputSegments))
require.Equal(t, "@u2", ctx.InputSegments[1].Value) require.Equal(t, "@u2", ctx.InputSegments[1].Value)
require.Equal(t, "@u3", ctx.InputSegments[3].Value) require.Equal(t, "@u3", ctx.InputSegments[3].Value)

View File

@ -1705,8 +1705,8 @@ func (api *PublicAPI) ChatMentionReplaceWithPublicKey(chatID, text string) (stri
// 2. user input "c", call this function with text "abc" // 2. user input "c", call this function with text "abc"
// whatever, we should ensure ChatMentionOnChangeText know(invoked) the latest full text. // whatever, we should ensure ChatMentionOnChangeText know(invoked) the latest full text.
// ChatMentionOnChangeText will maintain state of fulltext and diff between previous/latest full text internally. // ChatMentionOnChangeText will maintain state of fulltext and diff between previous/latest full text internally.
func (api *PublicAPI) ChatMentionOnChangeText(chatID, text string) (*protocol.ChatMentionContext, error) { func (api *PublicAPI) ChatMentionOnChangeText(chatID, text string, callID uint64) (*protocol.ChatMentionContext, error) {
return api.service.messenger.GetMentionsManager().OnChangeText(chatID, text) return api.service.messenger.GetMentionsManager().OnChangeText(chatID, text, callID)
} }
// ChatMentionSelectMention select mention from mention suggestion list // ChatMentionSelectMention select mention from mention suggestion list