feat(app): 完善播放协议搜索与空状态
This commit is contained in:
1 parent
671e0ce143
commit
ef29fc5f15
85 files changed
+2976
-768
No files matched your search
@@ -110,12 +110,16 @@ class HomeFeedOverlay extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _HomeFeedOverlayState extends State<HomeFeedOverlay> {
|
||||
static const Duration _singleTapDelay = Duration(milliseconds: 320);
|
||||
static const Duration _singleTapDelay = Duration(milliseconds: 400);
|
||||
|
||||
bool _scrubbing = false;
|
||||
bool _wasVisibleBeforeScrub = true;
|
||||
Timer? _singleTapTimer;
|
||||
DateTime? _lastTapAt;
|
||||
DateTime? _lastDownAt;
|
||||
DateTime? _blockSingleTapUntil;
|
||||
bool _doubleTapDown = false;
|
||||
bool _likedThisPair = false;
|
||||
int _downSerial = 0;
|
||||
int _tapSeq = 0;
|
||||
int _nextHeartBurstId = 0;
|
||||
final List<({int id, Offset position})> _heartBursts = [];
|
||||
@@ -146,6 +150,7 @@ class _HomeFeedOverlayState extends State<HomeFeedOverlay> {
|
||||
builder: (context, constraints) => Listener(
|
||||
onPointerUp: (_) => widget.onBoostEnd(),
|
||||
onPointerCancel: (_) => widget.onBoostEnd(),
|
||||
onPointerDown: _handlePointerDown,
|
||||
child: GestureDetector(
|
||||
key: const Key('home-feed-gesture'),
|
||||
behavior: HitTestBehavior.opaque,
|
||||
@@ -258,38 +263,66 @@ class _HomeFeedOverlayState extends State<HomeFeedOverlay> {
|
||||
widget.onScrubEnd?.call(wasVisible);
|
||||
}
|
||||
|
||||
void _handleTapUp(TapUpDetails details) {
|
||||
final tapSeq = ++_tapSeq;
|
||||
final now = widget.now?.call() ?? DateTime.now();
|
||||
final lastTap = _lastTapAt;
|
||||
if (lastTap != null && now.difference(lastTap) <= _singleTapDelay) {
|
||||
_tapSeq++;
|
||||
_singleTapTimer?.cancel();
|
||||
_singleTapTimer = null;
|
||||
_lastTapAt = null;
|
||||
_showHearts(details.localPosition);
|
||||
DateTime _now() => widget.now?.call() ?? DateTime.now();
|
||||
|
||||
void _handlePointerDown(PointerDownEvent event) {
|
||||
final now = _now();
|
||||
final previous = _lastDownAt;
|
||||
_downSerial++;
|
||||
_lastDownAt = now;
|
||||
final repeated =
|
||||
previous != null && now.difference(previous) <= _singleTapDelay;
|
||||
if (!repeated) {
|
||||
if (_singleTapBlocked(now)) return;
|
||||
_doubleTapDown = false;
|
||||
_likedThisPair = false;
|
||||
return;
|
||||
}
|
||||
_lastTapAt = now;
|
||||
_doubleTapDown = true;
|
||||
_blockSingleTapUntil = now.add(_singleTapDelay);
|
||||
_cancelPendingSingleTap();
|
||||
}
|
||||
|
||||
bool _singleTapBlocked(DateTime now) {
|
||||
final blockedUntil = _blockSingleTapUntil;
|
||||
return blockedUntil != null && now.isBefore(blockedUntil);
|
||||
}
|
||||
|
||||
void _cancelPendingSingleTap() {
|
||||
_tapSeq++;
|
||||
_singleTapTimer?.cancel();
|
||||
_singleTapTimer = null;
|
||||
}
|
||||
|
||||
void _handleTapUp(TapUpDetails details) {
|
||||
final now = _now();
|
||||
if (_doubleTapDown || _singleTapBlocked(now)) {
|
||||
_showDoubleTapLike(details.localPosition);
|
||||
return;
|
||||
}
|
||||
final serial = _downSerial;
|
||||
final tapSeq = ++_tapSeq;
|
||||
_singleTapTimer?.cancel();
|
||||
late final Timer pending;
|
||||
pending = Timer(_singleTapDelay, () {
|
||||
if (identical(_singleTapTimer, pending)) {
|
||||
_singleTapTimer = null;
|
||||
}
|
||||
if (tapSeq != _tapSeq) return;
|
||||
final latestTap = _lastTapAt;
|
||||
if (latestTap != null &&
|
||||
(widget.now?.call() ?? DateTime.now()).difference(latestTap) <
|
||||
_singleTapDelay) {
|
||||
return;
|
||||
}
|
||||
_lastTapAt = null;
|
||||
if (mounted) widget.onTogglePlay();
|
||||
if (!identical(_singleTapTimer, pending)) return;
|
||||
_singleTapTimer = null;
|
||||
if (!mounted || tapSeq != _tapSeq || serial != _downSerial) return;
|
||||
if (_doubleTapDown || _singleTapBlocked(_now())) return;
|
||||
_lastDownAt = null;
|
||||
widget.onTogglePlay();
|
||||
});
|
||||
_singleTapTimer = pending;
|
||||
}
|
||||
|
||||
void _showDoubleTapLike(Offset position) {
|
||||
if (_likedThisPair) return;
|
||||
_likedThisPair = true;
|
||||
_blockSingleTapUntil = _now().add(_singleTapDelay);
|
||||
_cancelPendingSingleTap();
|
||||
_showHearts(position);
|
||||
}
|
||||
|
||||
void _showHearts(Offset position) {
|
||||
final id = _nextHeartBurstId++;
|
||||
setState(() => _heartBursts.add((id: id, position: position)));
|
||||
@@ -302,11 +335,11 @@ class _HomeFeedOverlayState extends State<HomeFeedOverlay> {
|
||||
}
|
||||
|
||||
void _handleLongPressStart(LongPressStartDetails details, double width) {
|
||||
_tapSeq++;
|
||||
_singleTapTimer?.cancel();
|
||||
_singleTapTimer = null;
|
||||
_lastTapAt = null;
|
||||
final third = width / 3;
|
||||
_tapSeq++;
|
||||
_cancelPendingSingleTap();
|
||||
_doubleTapDown = false;
|
||||
_lastDownAt = null;
|
||||
final dx = details.localPosition.dx;
|
||||
if (dx < third || dx > width - third) {
|
||||
widget.onBoostStart();
|
||||
|
||||
Reference in new issue
Block a user