-
-
Notifications
You must be signed in to change notification settings - Fork 73
steps reset at midnight, and a dot for whether anything is syncing #216
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
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| // SyncDot — the quietest possible "data is arriving". | ||
| // | ||
| // From TestFlight: "don't get to know if syncing is happening or not". The sync | ||
| // is deliberately invisible in this app — no progress bars, no spinners, no | ||
| // "syncing…" copy — and that is the right default, because the band syncs | ||
| // constantly and a user cannot act on any of it. But invisible and broken look | ||
| // identical, which is what the report is really about. | ||
| // | ||
| // So: a 6pt dot beside the title that breathes while records are landing, and | ||
| // is absent otherwise. No text, no layout shift (the space is held either way), | ||
| // nothing to dismiss. If you are not looking for it you will not notice it; if | ||
| // you are wondering whether the thing works, it answers you. | ||
|
|
||
| import 'package:flutter/material.dart'; | ||
|
|
||
| import '../../theme/tokens.dart'; | ||
|
|
||
| class SyncDot extends StatefulWidget { | ||
| const SyncDot({super.key, required this.active, this.size = 6}); | ||
|
|
||
| /// The fixed-size box, so a test can measure the reserved space directly | ||
| /// rather than through the StatefulWidget element. | ||
| static const Key sizeKey = Key('sync-dot-box'); | ||
|
|
||
| /// Records are landing right now (`AppState.syncingNow`). | ||
| final bool active; | ||
| final double size; | ||
|
|
||
| @override | ||
| State<SyncDot> createState() => _SyncDotState(); | ||
| } | ||
|
|
||
| class _SyncDotState extends State<SyncDot> | ||
| with SingleTickerProviderStateMixin { | ||
| // Created in initState, NOT as a lazy `late final` initialiser. A dot that | ||
| // never animates never touches the field, so the first read would be | ||
| // `dispose()` — building a Ticker against an already-deactivated element, | ||
| // which throws "Looking up a deactivated widget's ancestor is unsafe". The | ||
| // quiet path is the common one, so the lazy version was broken for almost | ||
| // every user of this widget. | ||
| late final AnimationController _pulse; | ||
|
|
||
| @override | ||
| void initState() { | ||
| super.initState(); | ||
| _pulse = AnimationController( | ||
| vsync: this, | ||
| duration: const Duration(milliseconds: 1400), | ||
| ); | ||
| if (widget.active) _pulse.repeat(reverse: true); | ||
| } | ||
|
|
||
| @override | ||
| void didUpdateWidget(covariant SyncDot old) { | ||
| super.didUpdateWidget(old); | ||
| if (widget.active == old.active) return; | ||
| // Never leave the controller running while the dot is invisible — a | ||
| // repeating animation on an off-screen widget is a permanent 60 Hz wake-up | ||
| // on a screen that already fights for the main isolate during a drain. | ||
| if (widget.active) { | ||
| _pulse.repeat(reverse: true); | ||
| } else { | ||
| _pulse.stop(); | ||
| _pulse.value = 0; | ||
| } | ||
| } | ||
|
|
||
| @override | ||
| void dispose() { | ||
| _pulse.dispose(); | ||
| super.dispose(); | ||
| } | ||
|
|
||
| @override | ||
| Widget build(BuildContext context) { | ||
| // The box is always occupied, so the title never shifts when the dot | ||
| // appears or goes — a jumping wordmark would be far louder than the dot. | ||
| return SizedBox( | ||
| key: SyncDot.sizeKey, | ||
| width: widget.size, | ||
| height: widget.size, | ||
| child: !widget.active | ||
| ? const SizedBox.shrink() | ||
| : Semantics( | ||
| label: 'Syncing with your band', | ||
| liveRegion: true, | ||
| child: FadeTransition( | ||
| opacity: Tween<double>(begin: 0.25, end: 1).animate( | ||
| CurvedAnimation(parent: _pulse, curve: Curves.easeInOut), | ||
| ), | ||
| child: DecoratedBox( | ||
| decoration: BoxDecoration( | ||
| color: AppColors.accent, | ||
| shape: BoxShape.circle, | ||
| ), | ||
| ), | ||
| ), | ||
| ), | ||
| ); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.