From 6e5a32c02215f36509b21645a32361fca85b10f7 Mon Sep 17 00:00:00 2001 From: frank Date: Thu, 19 Sep 2024 13:03:56 +0800 Subject: [PATCH] fix(mentions)_: add callID for `OnChangeText` (#3806) --- protocol/messenger_mention.go | 33 +++++++++++++--- protocol/messenger_mention_test.go | 60 +++++++++++++++++++----------- services/ext/api.go | 4 +- 3 files changed, 68 insertions(+), 29 deletions(-) diff --git a/protocol/messenger_mention.go b/protocol/messenger_mention.go index 8627d1971..d624d0448 100644 --- a/protocol/messenger_mention.go +++ b/protocol/messenger_mention.go @@ -7,6 +7,7 @@ import ( "fmt" "regexp" "strings" + "sync" "unicode" "unicode/utf8" @@ -167,6 +168,10 @@ type ChatMentionContext struct { MentionState *MentionState PreviousText string // user input text before the last change NewText string + + CallID uint64 + mu *sync.Mutex + LatestCallID uint64 } func NewChatMentionContext(chatID string) *ChatMentionContext { @@ -174,6 +179,7 @@ func NewChatMentionContext(chatID string) *ChatMentionContext { ChatID: chatID, MentionSuggestions: make(map[string]*MentionableUser), MentionState: new(MentionState), + mu: new(sync.Mutex), } } @@ -295,11 +301,27 @@ func (m *MentionManager) ReplaceWithPublicKey(chatID, text string) (string, erro 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) + 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) if diff == nil { - return ctx, nil + return withCallID(ctx, callID), nil } ctx.PreviousText = fullText if ctx.MentionState == nil { @@ -313,11 +335,12 @@ func (m *MentionManager) OnChangeText(chatID, fullText string) (*ChatMentionCont atIndexes, err := calculateAtIndexEntries(ctx.MentionState) if err != nil { - return ctx, err + return withCallID(ctx, callID), err } ctx.MentionState.AtIdxs = atIndexes 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) { @@ -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, err := m.OnChangeText(chatID, ctx.NewText) + _, err := m.OnChangeText(chatID, ctx.NewText, 0) if err != nil { return nil, err } diff --git a/protocol/messenger_mention_test.go b/protocol/messenger_mention_test.go index 296632ea0..6ce89a8cb 100644 --- a/protocol/messenger_mention_test.go +++ b/protocol/messenger_mention_test.go @@ -794,7 +794,7 @@ func TestMentionSuggestionCases(t *testing.T) { for i, tc := range testCases { 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) t.Logf("Input: %+v, MentionState:%+v, InputSegments:%+v\n", tc.inputText, ctx.MentionState, ctx.InputSegments) require.Equal(t, tc.expectedSize, len(ctx.MentionSuggestions)) @@ -806,7 +806,7 @@ func TestMentionSuggestionAfterToInputField(t *testing.T) { mentionableUserMap, chatID, mentionManager := setupMentionSuggestionTest(t, nil) _, err := mentionManager.ToInputField(chatID, "abc") require.NoError(t, err) - ctx, err := mentionManager.OnChangeText(chatID, "@") + ctx, err := mentionManager.OnChangeText(chatID, "@", 1) require.NoError(t, err) require.Equal(t, len(mentionableUserMap), len(ctx.MentionSuggestions)) } @@ -826,7 +826,7 @@ func TestMentionSuggestionSpecialInputModeForAndroid(t *testing.T) { for i, tc := range testCases { 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.Equal(t, tc.expectedSize, len(ctx.MentionSuggestions)) 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 { 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) t.Logf("Input: %+v, MentionState:%+v, InputSegments:%+v\n", tc.inputText, ctx.MentionState, ctx.InputSegments) require.Equal(t, tc.expectedSize, len(ctx.MentionSuggestions)) @@ -878,8 +878,8 @@ func TestMentionSuggestionAtSignSpaceCases(t *testing.T) { var ctx *ChatMentionContext var err error - for _, tc := range testCases { - ctx, err = mentionManager.OnChangeText(chatID, tc.inputText) + for i, tc := range testCases { + ctx, err = mentionManager.OnChangeText(chatID, tc.inputText, uint64(i+1)) require.NoError(t, err) 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)) @@ -894,12 +894,14 @@ func TestMentionSuggestionAtSignSpaceCases(t *testing.T) { func TestSelectMention(t *testing.T) { mentionableUsers, chatID, mentionManager := setupMentionSuggestionTest(t, nil) + var callID uint64 = 1 text := "@u2 abc" - ctx, err := mentionManager.OnChangeText(chatID, text) + ctx, err := mentionManager.OnChangeText(chatID, text, callID) require.NoError(t, err) 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.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.PreviousText) - ctx, err = mentionManager.OnChangeText(chatID, text) + callID++ + ctx, err = mentionManager.OnChangeText(chatID, text, callID) require.NoError(t, err) require.Equal(t, 0, len(ctx.MentionSuggestions)) } func TestInputSegments(t *testing.T) { _, 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.Equal(t, 1, len(ctx.InputSegments)) require.Equal(t, Text, ctx.InputSegments[0].Type) 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.Equal(t, 2, len(ctx.InputSegments)) 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, "@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.Equal(t, 2, len(ctx.InputSegments)) require.Equal(t, Text, ctx.InputSegments[1].Type) 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.Equal(t, 3, len(ctx.InputSegments)) 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, "@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.Equal(t, 4, len(ctx.InputSegments)) 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, " 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.Equal(t, 3, len(ctx.InputSegments)) 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, "@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.Equal(t, 2, len(ctx.InputSegments)) 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, "@ ", ctx.InputSegments[1].Value) - ctx, err = mentionManager.OnChangeText(chatID, "@u3 @ ") + callID++ + ctx, err = mentionManager.OnChangeText(chatID, "@u3 @ ", callID) require.NoError(t, err) require.Equal(t, 3, len(ctx.InputSegments)) 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, "@ ", ctx.InputSegments[2].Value) - _, err = mentionManager.OnChangeText(chatID, " @ @User Number Three ") + callID++ + _, err = mentionManager.OnChangeText(chatID, " @ @User Number Three ", callID) 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) ctx, err = mentionManager.SelectMention(chatID, "@U @ @User Number Three ", "User Number Three", "0xpk3") require.NoError(t, err) 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)) - 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)) - 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, "@u2", ctx.InputSegments[1].Value) require.Equal(t, "@u3", ctx.InputSegments[3].Value) diff --git a/services/ext/api.go b/services/ext/api.go index c24c2d0b2..054053fcb 100644 --- a/services/ext/api.go +++ b/services/ext/api.go @@ -1705,8 +1705,8 @@ func (api *PublicAPI) ChatMentionReplaceWithPublicKey(chatID, text string) (stri // 2. user input "c", call this function with text "abc" // 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. -func (api *PublicAPI) ChatMentionOnChangeText(chatID, text string) (*protocol.ChatMentionContext, error) { - return api.service.messenger.GetMentionsManager().OnChangeText(chatID, text) +func (api *PublicAPI) ChatMentionOnChangeText(chatID, text string, callID uint64) (*protocol.ChatMentionContext, error) { + return api.service.messenger.GetMentionsManager().OnChangeText(chatID, text, callID) } // ChatMentionSelectMention select mention from mention suggestion list