implemented a central haptic feedback system with configurable levels (off, reduced, full), added a Haptics facade providing semantic feedback methods, integrated haptic cues across navigation, settings toggles, and async action results, and updated version to 1.1.0+54
This commit is contained in:
@@ -9,6 +9,7 @@ import '../../../../routing/app_routes.dart';
|
||||
import '../../../../share_intent/remote_file_ref.dart';
|
||||
import '../../../../state/app/modules/chat/bloc/chat_bloc.dart';
|
||||
import '../../../../utils/download_manager.dart';
|
||||
import '../../../../utils/haptics.dart';
|
||||
import '../../../../widget/confirm_dialog.dart';
|
||||
import '../../../../widget/info_dialog.dart';
|
||||
import '../data/chat_bubble_styles.dart';
|
||||
@@ -63,6 +64,7 @@ class _ChatBubbleState extends State<ChatBubble>
|
||||
|
||||
Offset _position = Offset.zero;
|
||||
Offset _dragStartPosition = Offset.zero;
|
||||
bool _swipeActionArmed = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -97,6 +99,7 @@ class _ChatBubbleState extends State<ChatBubble>
|
||||
if (job == null) return;
|
||||
final status = job.status.value;
|
||||
if (status is DownloadDone) {
|
||||
Haptics.success();
|
||||
DownloadManager.instance.clear(job.remotePath);
|
||||
_detachJob();
|
||||
final talkFile = message.file;
|
||||
@@ -197,13 +200,16 @@ class _ChatBubbleState extends State<ChatBubble>
|
||||
}
|
||||
}
|
||||
|
||||
void _showOptionsDialog() => showChatMessageOptionsDialog(
|
||||
context,
|
||||
chatData: widget.chatData,
|
||||
bubbleData: widget.bubbleData,
|
||||
isSender: widget.isSender,
|
||||
onRefetch: widget.refetch,
|
||||
);
|
||||
void _showOptionsDialog() {
|
||||
Haptics.longPress();
|
||||
showChatMessageOptionsDialog(
|
||||
context,
|
||||
chatData: widget.chatData,
|
||||
bubbleData: widget.bubbleData,
|
||||
isSender: widget.isSender,
|
||||
onRefetch: widget.refetch,
|
||||
);
|
||||
}
|
||||
|
||||
/// True only for messages whose body has a meaningful tap action (poll
|
||||
/// dialog or file download/cancel). For plain text messages we leave
|
||||
@@ -302,10 +308,16 @@ class _ChatBubbleState extends State<ChatBubble>
|
||||
? Offset(_position.dx, 0)
|
||||
: Offset(_position.dx + dx, 0);
|
||||
});
|
||||
// Beim Überqueren der Action-Schwelle einmalig Haptik feuern,
|
||||
// damit der User physisch spürt: "jetzt löst's beim Loslassen aus".
|
||||
final isArmed = _position.dx.abs() > 50;
|
||||
if (isArmed && !_swipeActionArmed) Haptics.longPress();
|
||||
_swipeActionArmed = isArmed;
|
||||
},
|
||||
onHorizontalDragEnd: (_) {
|
||||
final isAction = _position.dx.abs() > 50;
|
||||
setState(() => _position = Offset.zero);
|
||||
_swipeActionArmed = false;
|
||||
if (widget.bubbleData.isReplyable && isAction) {
|
||||
context.read<ChatBloc>().setReferenceMessageId(
|
||||
widget.bubbleData.id,
|
||||
|
||||
Reference in New Issue
Block a user