-
Notifications
You must be signed in to change notification settings - Fork 306
ui: Use faster animation for mark as read #710
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
0d577a0
030128a
9b105a3
791b703
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -458,30 +458,40 @@ class ScrollToBottomButton extends StatelessWidget { | |
} | ||
} | ||
|
||
class MarkAsReadWidget extends StatelessWidget { | ||
class MarkAsReadWidget extends StatefulWidget { | ||
const MarkAsReadWidget({super.key, required this.narrow}); | ||
|
||
final Narrow narrow; | ||
|
||
@override | ||
State<MarkAsReadWidget> createState() => _MarkAsReadWidgetState(); | ||
} | ||
|
||
class _MarkAsReadWidgetState extends State<MarkAsReadWidget> { | ||
bool _loading = false; | ||
|
||
void _handlePress(BuildContext context) async { | ||
if (!context.mounted) return; | ||
|
||
final store = PerAccountStoreWidget.of(context); | ||
final connection = store.connection; | ||
final useLegacy = connection.zulipFeatureLevel! < 155; | ||
setState(() => _loading = true); | ||
|
||
try { | ||
await markNarrowAsRead(context, narrow, useLegacy); | ||
await markNarrowAsRead(context, widget.narrow, useLegacy); | ||
} catch (e) { | ||
if (!context.mounted) return; | ||
final zulipLocalizations = ZulipLocalizations.of(context); | ||
await showErrorDialog(context: context, | ||
showErrorDialog(context: context, | ||
title: zulipLocalizations.errorMarkAsReadFailedTitle, | ||
message: e.toString()); // TODO(#741): extract user-facing message better | ||
return; | ||
} finally { | ||
setState(() => _loading = false); | ||
} | ||
if (!context.mounted) return; | ||
if (narrow is CombinedFeedNarrow && !useLegacy) { | ||
if (widget.narrow is CombinedFeedNarrow && !useLegacy) { | ||
PerAccountStoreWidget.of(context).unreads.handleAllMessagesReadSuccess(); | ||
} | ||
} | ||
|
@@ -490,15 +500,14 @@ class MarkAsReadWidget extends StatelessWidget { | |
Widget build(BuildContext context) { | ||
final zulipLocalizations = ZulipLocalizations.of(context); | ||
final store = PerAccountStoreWidget.of(context); | ||
final unreadCount = store.unreads.countInNarrow(narrow); | ||
final unreadCount = store.unreads.countInNarrow(widget.narrow); | ||
final areMessagesRead = unreadCount == 0; | ||
|
||
return IgnorePointer( | ||
ignoring: areMessagesRead, | ||
child: AnimatedOpacity( | ||
opacity: areMessagesRead ? 0 : 1, | ||
duration: Duration(milliseconds: areMessagesRead ? 2000 : 300), | ||
curve: Curves.easeOut, | ||
child: MarkAsReadAnimation( | ||
loading: _loading, | ||
hidden: areMessagesRead, | ||
child: SizedBox(width: double.infinity, | ||
// Design referenced from: | ||
// https://www.figma.com/file/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?type=design&node-id=132-9684&mode=design&t=jJwHzloKJ0TMOG4M-0 | ||
|
@@ -507,8 +516,7 @@ class MarkAsReadWidget extends StatelessWidget { | |
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10 - ((48 - 38) / 2)), | ||
child: FilledButton.icon( | ||
style: FilledButton.styleFrom( | ||
// TODO(#95) need dark-theme colors (foreground and background) | ||
backgroundColor: _UnreadMarker.color, | ||
splashFactory: NoSplash.splashFactory, | ||
minimumSize: const Size.fromHeight(38), | ||
textStyle: | ||
// Restate [FilledButton]'s default, which inherits from | ||
|
@@ -522,11 +530,60 @@ class MarkAsReadWidget extends StatelessWidget { | |
height: (23 / 18)) | ||
.merge(weightVariableTextStyle(context, wght: 400))), | ||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(7)), | ||
).copyWith( | ||
// Give the buttons a constant color regardless of whether their | ||
// state is disabled, pressed, etc. We handle those states | ||
// separately, via MarkAsReadAnimation. | ||
// TODO(#95) need dark-theme colors (foreground and background) | ||
foregroundColor: WidgetStateColor.resolveWith((_) => Colors.white), | ||
backgroundColor: WidgetStateColor.resolveWith((_) => _UnreadMarker.color), | ||
), | ||
onPressed: () => _handlePress(context), | ||
onPressed: _loading ? null : () => _handlePress(context), | ||
icon: const Icon(Icons.playlist_add_check), | ||
label: Text(zulipLocalizations.markAllAsReadLabel))))), | ||
); | ||
label: Text(zulipLocalizations.markAllAsReadLabel)))))); | ||
} | ||
} | ||
|
||
class MarkAsReadAnimation extends StatefulWidget { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The commit that introduces this widget class:
has a commit message that's about a behavior change, and I think there's effectively just one line that's changing to carry out that behavior change. But that change happens in the middle of a larger amount of code that's getting moved and rearranged. To make clear and coherent commits: |
||
final bool loading; | ||
final bool hidden; | ||
final Widget child; | ||
|
||
const MarkAsReadAnimation({ | ||
super.key, | ||
required this.loading, | ||
required this.hidden, | ||
required this.child | ||
}); | ||
|
||
@override | ||
State<MarkAsReadAnimation> createState() => _MarkAsReadAnimationState(); | ||
} | ||
|
||
class _MarkAsReadAnimationState extends State<MarkAsReadAnimation> { | ||
bool _isPressed = false; | ||
|
||
void _setIsPressed(bool isPressed) { | ||
setState(() { | ||
_isPressed = isPressed; | ||
}); | ||
} | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return GestureDetector( | ||
onTapDown: (_) => _setIsPressed(true), | ||
onTapUp: (_) => _setIsPressed(false), | ||
onTapCancel: () => _setIsPressed(false), | ||
child: AnimatedScale( | ||
scale: _isPressed ? 0.95 : 1, | ||
duration: const Duration(milliseconds: 100), | ||
Khader-1 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
curve: Curves.easeOut, | ||
child: AnimatedOpacity( | ||
opacity: widget.hidden ? 0 : widget.loading ? 0.5 : 1, | ||
duration: const Duration(milliseconds: 500), | ||
curve: Curves.easeOut, | ||
child: widget.child))); | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When Vlad said "i pressed state", I think he probably meant "in pressed state". 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That explains everything!😅