✨ Username hint
This commit is contained in:
parent
c48bd3e758
commit
dea743a307
@ -11,6 +11,7 @@ import 'package:solian/platform.dart';
|
|||||||
import 'package:solian/providers/attachment_uploader.dart';
|
import 'package:solian/providers/attachment_uploader.dart';
|
||||||
import 'package:solian/providers/auth.dart';
|
import 'package:solian/providers/auth.dart';
|
||||||
import 'package:solian/providers/stickers.dart';
|
import 'package:solian/providers/stickers.dart';
|
||||||
|
import 'package:solian/widgets/account/account_avatar.dart';
|
||||||
import 'package:solian/widgets/attachments/attachment_editor.dart';
|
import 'package:solian/widgets/attachments/attachment_editor.dart';
|
||||||
import 'package:solian/widgets/chat/chat_event.dart';
|
import 'package:solian/widgets/chat/chat_event.dart';
|
||||||
import 'package:badges/badges.dart' as badges;
|
import 'package:badges/badges.dart' as badges;
|
||||||
@ -55,8 +56,8 @@ class ChatMessageInput extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _ChatMessageInputState extends State<ChatMessageInput> {
|
class _ChatMessageInputState extends State<ChatMessageInput> {
|
||||||
TextEditingController _textController = TextEditingController();
|
final TextEditingController _textController = TextEditingController();
|
||||||
FocusNode _focusNode = FocusNode();
|
final FocusNode _focusNode = FocusNode();
|
||||||
|
|
||||||
final List<int> _attachments = List.empty(growable: true);
|
final List<int> _attachments = List.empty(growable: true);
|
||||||
|
|
||||||
@ -241,6 +242,14 @@ class _ChatMessageInputState extends State<ChatMessageInput> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (suggestion.type == 'users') {
|
||||||
|
insertText = '${suggestion.content} ';
|
||||||
|
startText = replaceText.replaceFirstMapped(
|
||||||
|
RegExp(r'(?:\s|^)@([a-z0-9_+-]+)$'),
|
||||||
|
(Match m) => insertText,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (insertText.isNotEmpty && startText.isNotEmpty) {
|
if (insertText.isNotEmpty && startText.isNotEmpty) {
|
||||||
_textController.text = startText + afterText;
|
_textController.text = startText + afterText;
|
||||||
_textController.selection = TextSelection(
|
_textController.selection = TextSelection(
|
||||||
@ -311,13 +320,11 @@ class _ChatMessageInputState extends State<ChatMessageInput> {
|
|||||||
controller: _textController,
|
controller: _textController,
|
||||||
focusNode: _focusNode,
|
focusNode: _focusNode,
|
||||||
hideOnSelect: false,
|
hideOnSelect: false,
|
||||||
debounceDuration: const Duration(milliseconds: 50),
|
debounceDuration: const Duration(milliseconds: 500),
|
||||||
onSelected: (value) {
|
onSelected: (value) {
|
||||||
_insertSuggestion(value);
|
_insertSuggestion(value);
|
||||||
},
|
},
|
||||||
itemBuilder: (context, item) {
|
itemBuilder: (context, item) => _buildSuggestion(item),
|
||||||
return _buildSuggestion(item);
|
|
||||||
},
|
|
||||||
builder: (context, controller, focusNode) {
|
builder: (context, controller, focusNode) {
|
||||||
return TextField(
|
return TextField(
|
||||||
controller: _textController,
|
controller: _textController,
|
||||||
@ -336,7 +343,7 @@ class _ChatMessageInputState extends State<ChatMessageInput> {
|
|||||||
FocusManager.instance.primaryFocus?.unfocus(),
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
suggestionsCallback: (search) {
|
suggestionsCallback: (search) async {
|
||||||
final searchText = _textController.text
|
final searchText = _textController.text
|
||||||
.substring(0, _textController.selection.baseOffset);
|
.substring(0, _textController.selection.baseOffset);
|
||||||
|
|
||||||
@ -370,6 +377,33 @@ class _ChatMessageInputState extends State<ChatMessageInput> {
|
|||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final userMatch = RegExp(r'(?:\s|^)@([a-z0-9_+-]+)$')
|
||||||
|
.firstMatch(searchText);
|
||||||
|
if (userMatch != null) {
|
||||||
|
final userSearch = userMatch[1]!.toLowerCase();
|
||||||
|
final AuthProvider auth = Get.find();
|
||||||
|
|
||||||
|
final client = auth.configureClient('auth');
|
||||||
|
final resp = await client.get(
|
||||||
|
'/users/search?probe=$userSearch',
|
||||||
|
);
|
||||||
|
|
||||||
|
final List<Account> result = resp.body
|
||||||
|
.map((x) => Account.fromJson(x))
|
||||||
|
.toList()
|
||||||
|
.cast<Account>();
|
||||||
|
return result
|
||||||
|
.map(
|
||||||
|
(x) => ChatMessageSuggestion(
|
||||||
|
type: 'users',
|
||||||
|
leading: AccountAvatar(content: x.avatar),
|
||||||
|
display: x.nick,
|
||||||
|
content: '@${x.name}',
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -503,7 +503,6 @@ class _MeasureSize extends SingleChildRenderObjectWidget {
|
|||||||
final _OnWidgetSizeChange onChange;
|
final _OnWidgetSizeChange onChange;
|
||||||
|
|
||||||
const _MeasureSize({
|
const _MeasureSize({
|
||||||
super.key,
|
|
||||||
required this.onChange,
|
required this.onChange,
|
||||||
required Widget super.child,
|
required Widget super.child,
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user