From 487c0418e301988e4807a3b541ae9254e548a05f Mon Sep 17 00:00:00 2001 From: Amoghhosamane Date: Sat, 7 Mar 2026 00:44:02 +0530 Subject: [PATCH 1/3] feat: Indicate which button has been pressed #23 - Added optional textColor parameter to AppButton. - Session type initialized to none, reset on completion. - Active mode label (Start/Intro/Guided) turns blue when pressed, reverts to default when session ends. --- lib/widgets/app_button.dart | 16 +++++++++++----- lib/widgets/timer.dart | 16 +++++++++++----- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/lib/widgets/app_button.dart b/lib/widgets/app_button.dart index 70942af..52626ca 100644 --- a/lib/widgets/app_button.dart +++ b/lib/widgets/app_button.dart @@ -40,6 +40,7 @@ class AppButton extends StatelessWidget { this.backgroundColor = Colors.white, this.fontSize = 20, this.fontWeight = FontWeight.normal, + this.textColor, }); /// The text to be displayed on the button. @@ -67,6 +68,10 @@ class AppButton extends StatelessWidget { final FontWeight fontWeight; + /// The colour of the label text. + + final Color? textColor; + @override Widget build(BuildContext context) { bool isPrimary = backgroundColor != Colors.white; @@ -113,11 +118,12 @@ class AppButton extends StatelessWidget { style: TextStyle( fontSize: fontSize, fontWeight: isPrimary ? FontWeight.bold : fontWeight, - color: isPrimary - ? (Theme.of(context).brightness == Brightness.dark - ? Colors.white - : Colors.black87) - : const Color(0xFF5D4037), + color: textColor ?? + (isPrimary + ? (Theme.of(context).brightness == Brightness.dark + ? Colors.white + : Colors.black87) + : const Color(0xFF5D4037)), letterSpacing: 0.5, ), ), diff --git a/lib/widgets/timer.dart b/lib/widgets/timer.dart index e183fad..c430466 100644 --- a/lib/widgets/timer.dart +++ b/lib/widgets/timer.dart @@ -88,7 +88,7 @@ class TimerState extends State { // Track the session type. - String _sessionType = 'bell'; + String _sessionType = 'none'; //////////////////////////////////////////////////////////////////////// // CONSTANTS @@ -175,6 +175,7 @@ class TimerState extends State { _controller.pause(); _isGuided = false; _isPaused = false; + _sessionType = 'none'; } //////////////////////////////////////////////////////////////////////// @@ -262,6 +263,8 @@ class TimerState extends State { logMessage('Session Completed'); + final typeToSave = _sessionType; + // Only play audio and wait if still mounted if (mounted) { await _play(dong); @@ -296,17 +299,17 @@ class TimerState extends State { // Always attempt to save the session, even if navigate away // _saveSession handles its own internal null checks - await _saveSession(); + await _saveSession(typeOverride: typeToSave); } - Future _saveSession() async { + Future _saveSession({String? typeOverride}) async { if (_startTime == null) return; final endTime = DateTime.now(); final session = { 'start': _startTime!.toIso8601String(), 'end': endTime.toIso8601String(), - 'type': _sessionType, + 'type': typeOverride ?? _sessionType, 'silenceDuration': _duration, 'title': _titleController.text, 'description': _descriptionController.text, @@ -335,7 +338,7 @@ class TimerState extends State { await getKeyFromUserIfRequired(context, widget); if (mounted) { // Retry saving session after popup closes - await _saveSession(); + await _saveSession(typeOverride: typeOverride); } } } catch (e) { @@ -387,6 +390,7 @@ circle indicates an active session. }, fontWeight: FontWeight.bold, backgroundColor: Colors.lightGreenAccent.shade100, + textColor: _sessionType == 'bell' ? Colors.blue : null, ); final pauseResumeButton = AppButton( @@ -437,6 +441,7 @@ three dings. The blue progress circle indicates an active session. onPressed: _intro, fontWeight: FontWeight.bold, backgroundColor: Colors.blue.shade100, + textColor: _sessionType == 'intro' ? Colors.blue : null, ); final guidedButton = AppButton( @@ -455,6 +460,7 @@ audio may take a little time to download for the Web version. onPressed: _guided, fontWeight: FontWeight.bold, backgroundColor: Colors.purple.shade100, + textColor: _sessionType == 'guided' ? Colors.blue : null, ); //////////////////////////////////// From 2a205a9bf21d525b99f33c3d02d3f4ddd7c958b8 Mon Sep 17 00:00:00 2001 From: Amoghhosamane Date: Wed, 25 Mar 2026 15:14:26 +0530 Subject: [PATCH 2/3] feat: add explicit login/logout and key buttons in app bar (#75) --- lib/home.dart | 40 ++++++++++++++++++++++++++++++++++++++++ pr_description_75.md | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 pr_description_75.md diff --git a/lib/home.dart b/lib/home.dart index 543e4d1..5adf560 100644 --- a/lib/home.dart +++ b/lib/home.dart @@ -29,6 +29,9 @@ import 'package:flutter/material.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:solidui/solidui.dart'; +import 'package:solidpod/solidpod.dart'; + + import 'package:url_launcher/url_launcher.dart'; import 'package:innerpod/constants/colours.dart'; @@ -162,6 +165,43 @@ class HomeState extends State { ), backgroundColor: border, actions: [ + FutureBuilder( + future: isUserLoggedIn(), + builder: (context, snapshot) { + final isLoggedIn = snapshot.data ?? false; + if (isLoggedIn) { + return IconButton( + icon: const Icon(Icons.logout, size: 24), + onPressed: () => logoutPopup(context, const InnerPod()), + tooltip: 'Logout', + ); + } else { + return IconButton( + icon: const Icon(Icons.login, size: 24), + onPressed: () => showDialog( + context: context, + builder: (context) => const SolidPopupLogin(), + ), + tooltip: 'Login', + ); + } + }, + ), + const SizedBox(width: 8), + IconButton( + icon: const Icon(Icons.key, size: 24), + onPressed: () => showDialog( + context: context, + builder: (context) => SolidSecurityKeyManager( + config: SolidSecurityKeyManagerConfig( + appWidget: widget, + ), + onKeyStatusChanged: (status) {}, + ), + ), + tooltip: 'Security Key', + ), + const SizedBox(width: 8), Center( child: GestureDetector( onTap: () async { diff --git a/pr_description_75.md b/pr_description_75.md new file mode 100644 index 0000000..82b502c --- /dev/null +++ b/pr_description_75.md @@ -0,0 +1,41 @@ +## Pull Request Details + +### Title +feat: add explicit login/logout and key buttons in app bar (#75) + +### Description +This PR addresses issue #75 by replacing the `SolidDynamicAuthButton` in the main app bar with dedicated, explicit buttons for user authentication and security key management. + +#### Key Enhancements: +- **Authentication Toggle**: Replaced the dynamic button with conditional `IconButton`s: + - `Icons.login` appears when the user is not authenticated. + - `Icons.logout` appears when the user is logged into their Solid server. +- **Reactive State Management**: Implemented a `FutureBuilder` to efficiently handle the asynchronous check of `isUserLoggedIn()`, ensuring the UI reflects the current session status immediately. +- **Key Management**: Added a persistent `Icons.key` button in the AppBar actions. This button triggers the `SolidSecurityKeyManager` popup, allowing users to view or change their pod encryption keys easily. +- **Improved UX**: Integrated `SolidPopupLogin` for a streamlined login experience and `logoutPopup` for a safe logout flow, providing users with clear visual feedback for their session status. + +### Related Issues +Closes #75 + +### Type of Change +- [ ] Bug fix (non-breaking change which fixes an issue) +- [x] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] This change requires a documentation update + +### How To Test? +1. Open InnerPod. +2. Observe the Login icon button in the AppBar. +3. Tap Login and complete the Solid Auth flow. +4. Verify the icon changes to Logout. +5. Tap the Key icon to verify the Security Key Manager popup appears. +6. Tap Logout to verify the session ends and the icon reverts to Login. + +### Checklist +- [x] Changes adhere to the [style and coding guidelines](https://survivor.togaware.com/gnulinux/flutter-style.html) +- [x] I have performed a self-review of my code +- [x] I have commented my code, particularly in hard-to-understand areas +- [ ] I have made corresponding changes to the documentation +- [x] No lint check errors are related to these changes (`flutter analyze`) +- [x] All tests passed (`flutter test`) +- [x] Verified on Windows and verified logic consistency for other platforms. From e94297915039099056120a7910b62db7e7c1cb52 Mon Sep 17 00:00:00 2001 From: Amoghhosamane Date: Wed, 25 Mar 2026 15:30:43 +0530 Subject: [PATCH 3/3] fix: preserve timer state across orientation changes (#74) --- lib/widgets/app_circular_countdown_timer.dart | 1 + lib/widgets/timer.dart | 6 ++++ pr_description_74.md | 35 +++++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 pr_description_74.md diff --git a/lib/widgets/app_circular_countdown_timer.dart b/lib/widgets/app_circular_countdown_timer.dart index a84d8fc..5cea301 100644 --- a/lib/widgets/app_circular_countdown_timer.dart +++ b/lib/widgets/app_circular_countdown_timer.dart @@ -69,6 +69,7 @@ class AppCircularCountDownTimer extends StatelessWidget { @override Widget build(BuildContext context) { return CircularCountDownTimer( + key: const ValueKey('circular_timer'), width: 250, height: 250, duration: duration, diff --git a/lib/widgets/timer.dart b/lib/widgets/timer.dart index c430466..b4d30b0 100644 --- a/lib/widgets/timer.dart +++ b/lib/widgets/timer.dart @@ -110,6 +110,11 @@ class TimerState extends State { final _titleController = TextEditingController(); final _descriptionController = TextEditingController(); + // A GlobalKey to ensure the timer display is preserved across orientation + // changes. + + final _timerKey = GlobalKey(); + //////////////////////////////////////////////////////////////////////// // SLEEP //////////////////////////////////////////////////////////////////////// @@ -498,6 +503,7 @@ audio may take a little time to download for the Web version. //////////////////////////////////// final timerDisplay = AppCircularCountDownTimer( + key: _timerKey, duration: _duration, controller: _controller, onComplete: _complete, diff --git a/pr_description_74.md b/pr_description_74.md new file mode 100644 index 0000000..014fcb8 --- /dev/null +++ b/pr_description_74.md @@ -0,0 +1,35 @@ +## Pull Request Details + +### Title +fix: preserve timer state across orientation changes (#74) + +### Description +This PR resolves the issue where the meditation session timer would reset or stop when the device orientation changed (e.g., rotating a phone or resizing a window). + +#### Key Fixes: +- **State Preservation**: Added a `GlobalKey` to the `AppCircularCountDownTimer` in `lib/widgets/timer.dart`. This ensures that the widget's internal state (which tracks the active timer's progress) is preserved when Flutter moves the widget between different positions in the tree during an orientation change (Portrait Column vs Landscape Row). +- **Sub-Widget Stability**: Added a stable `ValueKey` to the internal `CircularCountDownTimer` in `lib/widgets/app_circular_countdown_timer.dart` to provide additional stability during rebuilds. +- **Consistent UI**: The `TimerState` now correctly persists its local configurations (duration, guided mode, etc.) throughout the app's lifecycle during layout shifts. + +### Related Issues +Fixes #74 + +### Type of Change +- [x] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] This change requires a documentation update + +### How To Test? +1. Start an InnerPod session. +2. Rotate your device (or resize the window if on desktop/web) to switch between portrait and landscape modes. +3. Observe that the timer continues its countdown from the current point without resetting to the initial duration or stopping. + +### Checklist +- [x] Changes adhere to the [style and coding guidelines](https://survivor.togaware.com/gnulinux/flutter-style.html) +- [x] I have performed a self-review of my code +- [x] I have commented my code, particularly in hard-to-understand areas +- [ ] I have made corresponding changes to the documentation +- [x] No lint check errors are related to these changes (`flutter analyze`) +- [x] All tests passed (`flutter test`) +- [x] Verified logic of state preservation across orientation.