fix(shopinbit): stop polling a ticket once it reaches a terminal state
What changed, and why it matters
This commit fixes a minor behavior bug in the Stack Wallet app's ShopInBit ticket screen. Previously, the app kept checking (polling) a support or order ticket every 30 seconds even after the ticket was already closed, merged, or refunded. The change makes the app stop polling once the ticket reaches a final, unchangeable state. This is mainly a resource/battery/network efficiency fix, not a security fix.
No security action required. Treat as a normal bug fix / performance improvement. If reviewing for broader issues, consider whether repeated polling elsewhere in the app has similar inefficiencies or whether terminal-state definitions are complete.
Security signals we found
No direct security signal in diff: no input validation, crypto, authentication, or authorization change
Change is a client-side resource-usage optimization, not a vulnerability patch
No mention of security, CVE, researcher, or incident in commit title/message
Evidence from the diff
In lib/pages/shopinbit/shopinbit_ticket_detail.dart, the _poll() method now reads the current ticket via ref.read(pShopInBitTicket(_id)).asData?.value and checks whether TicketState.fromString(ticket.statusRaw).isTerminal is true. If so, it returns before rescheduling the 30-second _pollingTimer. This prevents infinite polling loops for tickets in terminal states.
Changed components
lib/pages/shopinbit/shopinbit_ticket_detail.dartShopInBit ticket detail page polling logicInspect captured patch +9 / −0
diff --git a/lib/pages/shopinbit/shopinbit_ticket_detail.dart b/lib/pages/shopinbit/shopinbit_ticket_detail.dart
index 364407a..8424551 100644
--- a/lib/pages/shopinbit/shopinbit_ticket_detail.dart
+++ b/lib/pages/shopinbit/shopinbit_ticket_detail.dart
@@ -10,6 +10,7 @@ import '../../db/drift/shared_db/shared_database.dart';
import '../../models/shopinbit/shopinbit_enums.dart';
import '../../providers/global/shopin_bit_service_provider.dart';
import '../../services/shopinbit/src/models/message.dart';
+import '../../services/shopinbit/src/models/ticket.dart';
import '../../themes/stack_colors.dart';
import '../../utilities/assets.dart';
import '../../utilities/text_styles.dart';
@@ -70,6 +71,14 @@ class _ShopInBitTicketDetailState extends ConsumerState<ShopInBitTicketDetail> {
Future<void> _poll() async {
await _refresh();
if (!mounted) return;
+
+ // Stop polling once the ticket reaches a terminal state; nothing about a
+ // closed/merged/refunded ticket will change server-side.
+ final ticket = ref.read(pShopInBitTicket(_id)).asData?.value;
+ if (ticket != null && TicketState.fromString(ticket.statusRaw).isTerminal) {
+ return;
+ }
+
_pollingTimer = Timer(const Duration(seconds: 30), _poll);
}
Why this scored 18/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.