feat: Trenner bleibt 6 s stehen, Testprofil im Fenstertitel
- "Neue Nachrichten"-Trenner verschwand beim Öffnen aus der Benachrichtigung sofort (Bernd). Jetzt: bei den neuesten Nachrichten angekommen noch 6 s stehen, dann weich ausblenden; wer hochscrollt, behält ihn. - Windows: mit PYRAMID_PROFILE_DIR heißt das Fenster "Pyramid - TESTINSTANZ" (Testinstanz mit Testkonto nicht mit der echten Pyramid verwechseln). Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
a30d57ba69
commit
a6461fe23c
@@ -28,6 +28,11 @@ class _ChatViewState extends ConsumerState<ChatView> {
|
||||
// Fade-out of the "Neue Nachrichten" divider once the user reaches the bottom.
|
||||
bool _unreadFading = false;
|
||||
Timer? _unreadFadeTimer;
|
||||
// So lange bleibt der Trenner stehen, nachdem man bei den neuesten
|
||||
// Nachrichten angekommen ist (Bernd 2026-10-07: verschwand beim Öffnen aus
|
||||
// der Benachrichtigung sofort).
|
||||
static const _unreadDwell = Duration(seconds: 6);
|
||||
Timer? _unreadDwellTimer;
|
||||
// Zuletzt von hier als gelesen gemeldetes Event. Verhindert, dass jeder
|
||||
// Neuaufbau erneut eine Lesebestätigung schickt, und markiert nach dem
|
||||
// Zurückkehren in die App, ab wo „Neue Nachrichten“ beginnen.
|
||||
@@ -162,6 +167,7 @@ class _ChatViewState extends ConsumerState<ChatView> {
|
||||
_historyExhausted = false;
|
||||
_unreadFading = false;
|
||||
_unreadFadeTimer?.cancel();
|
||||
_unreadDwellTimer?.cancel();
|
||||
_lastMarkedEventId = null;
|
||||
_popInAfter = null;
|
||||
_jumpEventId = null;
|
||||
@@ -177,6 +183,7 @@ class _ChatViewState extends ConsumerState<ChatView> {
|
||||
@override
|
||||
void dispose() {
|
||||
_unreadFadeTimer?.cancel();
|
||||
_unreadDwellTimer?.cancel();
|
||||
_highlightTimer?.cancel();
|
||||
_scrollCtrl.dispose();
|
||||
super.dispose();
|
||||
@@ -247,10 +254,12 @@ class _ChatViewState extends ConsumerState<ChatView> {
|
||||
duration: const Duration(milliseconds: 250),
|
||||
curve: Curves.easeOut,
|
||||
);
|
||||
_scheduleUnreadFadeIfAtNewest();
|
||||
return;
|
||||
}
|
||||
if (attempt >= 24) {
|
||||
_didScrollToUnread = true; // give up — stay at the newest messages
|
||||
_scheduleUnreadFadeIfAtNewest();
|
||||
return;
|
||||
}
|
||||
if (attempt > 0 && attempt % 4 == 0 && _scrollCtrl.hasClients) {
|
||||
@@ -311,16 +320,46 @@ class _ChatViewState extends ConsumerState<ChatView> {
|
||||
.any((e) => _MessageList._isVisible(e) && e.senderId != me);
|
||||
if (hasNew) {
|
||||
_unreadFadeTimer?.cancel();
|
||||
_unreadDwellTimer?.cancel();
|
||||
setState(() {
|
||||
_sessionFullyReadId = since;
|
||||
_sessionHasUnread = true;
|
||||
_unreadFading = false;
|
||||
});
|
||||
_scheduleUnreadFadeIfAtNewest();
|
||||
}
|
||||
}
|
||||
_markRead(timeline);
|
||||
}
|
||||
|
||||
bool get _atNewest =>
|
||||
!_scrollCtrl.hasClients || _scrollCtrl.position.pixels < 60;
|
||||
|
||||
/// „Neue Nachrichten“-Trenner nach [_unreadDwell] weich ausblenden – nur
|
||||
/// wenn man dann noch bei den neuesten Nachrichten ist. Wer zwischendurch
|
||||
/// hochscrollt, behält ihn; unten angekommen beginnt die Wartezeit neu.
|
||||
void _scheduleUnreadFade() {
|
||||
if (!_sessionHasUnread || _unreadFading) return;
|
||||
if (_unreadDwellTimer?.isActive ?? false) return;
|
||||
_unreadDwellTimer = Timer(_unreadDwell, () {
|
||||
if (!mounted || !_sessionHasUnread || !_atNewest) return;
|
||||
setState(() => _unreadFading = true);
|
||||
_unreadFadeTimer?.cancel();
|
||||
_unreadFadeTimer = Timer(const Duration(milliseconds: 900), () {
|
||||
if (mounted) setState(() { _sessionHasUnread = false; _unreadFading = false; });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Nach dem Sprung zum Trenner (Animation ~250 ms) prüfen: Passt alles
|
||||
// Ungelesene auf den Bildschirm, scrollt niemand mehr – dann läuft die
|
||||
// Verweildauer trotzdem an.
|
||||
void _scheduleUnreadFadeIfAtNewest() {
|
||||
Future.delayed(const Duration(milliseconds: 400), () {
|
||||
if (mounted && _atNewest) _scheduleUnreadFade();
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _onScroll() async {
|
||||
if (!_scrollCtrl.hasClients) return;
|
||||
final pixels = _scrollCtrl.position.pixels;
|
||||
@@ -328,15 +367,9 @@ class _ChatViewState extends ConsumerState<ChatView> {
|
||||
|
||||
if (pixels < 60) {
|
||||
_markRead(null);
|
||||
// Reached the newest messages → the "Neue Nachrichten" divider is no
|
||||
// longer useful. Fade it out, then remove it.
|
||||
if (_sessionHasUnread && !_unreadFading) {
|
||||
setState(() => _unreadFading = true);
|
||||
_unreadFadeTimer?.cancel();
|
||||
_unreadFadeTimer = Timer(const Duration(milliseconds: 900), () {
|
||||
if (mounted) setState(() { _sessionHasUnread = false; _unreadFading = false; });
|
||||
});
|
||||
}
|
||||
// Bei den neuesten Nachrichten angekommen → Trenner nach einer
|
||||
// Verweildauer ausblenden.
|
||||
_scheduleUnreadFade();
|
||||
}
|
||||
final shouldShow = pixels > 400;
|
||||
if (shouldShow != _showScrollDown) {
|
||||
|
||||
Reference in New Issue
Block a user