diff --git a/lib/models/global_state.dart b/lib/models/global_state.dart index 43e1b7c..2ebb682 100644 --- a/lib/models/global_state.dart +++ b/lib/models/global_state.dart @@ -10,7 +10,7 @@ import 'reminder_config.dart'; export 'package:provider/provider.dart'; -enum Status { idle, waitingFloor, waitingAll, pay, mode, using } +enum Status { idle, waiting, pay, mode, using } class GlobalState with ChangeNotifier { Dormitory? dormitory; @@ -19,10 +19,12 @@ class GlobalState with ChangeNotifier { int viewIndex = 0; // 0 -> floor only, 1 -> All floors Status status = Status.idle; Type? waitingMachine = WashingMachine; + Set subscribedFloors = {}; String? defaultPaymentMethod; ReminderConfig machineAvailable = ReminderConfig.defaultConfig; ReminderConfig laundryDone = ReminderConfig.defaultConfig; bool get anonymous => dormitory == null || floor == null; + String? get subscribedFloorsString => subscribedFloors.isEmpty ? null : "${(subscribedFloors.toList()..sort((a, b) => (a - b))).join(',')}F"; static get init => LocalData.loadGlobalState; reset() { @@ -31,6 +33,7 @@ class GlobalState with ChangeNotifier { status = Status.idle; currentMachine = null; waitingMachine = WashingMachine; + subscribedFloors = {}; defaultPaymentMethod = null; notifyListeners(); } @@ -42,6 +45,7 @@ class GlobalState with ChangeNotifier { viewIndex, currentMachine = "", waitingMachine, + subscribedFloors, defaultPaymentMethod = "", machineAvailable, laundryDone, @@ -52,6 +56,7 @@ class GlobalState with ChangeNotifier { this.viewIndex = viewIndex ?? this.viewIndex; this.currentMachine = currentMachine != "" ? currentMachine : this.currentMachine; this.waitingMachine = waitingMachine ?? this.waitingMachine; + this.subscribedFloors = subscribedFloors ?? this.subscribedFloors; this.defaultPaymentMethod = defaultPaymentMethod != "" ? defaultPaymentMethod : this.defaultPaymentMethod; this.machineAvailable = machineAvailable ?? this.machineAvailable; this.laundryDone = laundryDone ?? this.laundryDone; @@ -64,6 +69,7 @@ class GlobalState with ChangeNotifier { dormitory = "", floor = "", currentMachine = "", + subscribedFloors, viewIndex, status, defaultPaymentMethod = "", @@ -74,6 +80,7 @@ class GlobalState with ChangeNotifier { dormitory: dormitory, floor: floor, currentMachine: currentMachine, + subscribedFloors: subscribedFloors, status: status, viewIndex: viewIndex, defaultPaymentMethod: defaultPaymentMethod, @@ -89,6 +96,7 @@ class GlobalState with ChangeNotifier { 'floor': floor, 'status': status.name, 'waitingMachine': waitingMachine?.toString(), + 'subscribedFloors': subscribedFloors.toList(), 'defaultPaymentMethod': defaultPaymentMethod, 'machineAvailable': machineAvailable.toMap(), 'laundryDone': laundryDone.toMap(), @@ -102,6 +110,7 @@ class GlobalState with ChangeNotifier { floor: map['floor'] != null ? map['floor'] as int : null, status: Status.values.byName(map['status'] as String), waitingMachine: map['waitingMachine'] != null && map['waitingMachine'] == "DryerMachine" ? DryerMachine : WashingMachine, + subscribedFloors: map['subscribedFloors'] != null ? List.from(map['subscribedFloors'].cast()).toSet() : {}, defaultPaymentMethod: map['defaultPaymentMethod'] != null ? map['defaultPaymentMethod'] as String : null, machineAvailable: ReminderConfig.fromMap(map['machineAvailable'] as Map), laundryDone: ReminderConfig.fromMap(map['laundryDone'] as Map), diff --git a/lib/services/fake_data.dart b/lib/services/fake_data.dart index 3b9559e..cdf576e 100644 --- a/lib/services/fake_data.dart +++ b/lib/services/fake_data.dart @@ -78,7 +78,7 @@ class FakeData { static setReminder(context) async { // var state = GlobalState.of(context); - if (GlobalState.of(context).status == Status.waitingAll || GlobalState.of(context).status == Status.waitingFloor) { + if (GlobalState.of(context).status == Status.waiting) { Future.delayed(const Duration(seconds: 10), () { GlobalState.set(context, currentMachine: FakeData.washingMachine); }); diff --git a/lib/views/components/machine_list.dart b/lib/views/components/machine_list.dart index d9fcf92..405e5c2 100644 --- a/lib/views/components/machine_list.dart +++ b/lib/views/components/machine_list.dart @@ -25,11 +25,11 @@ class MachineList extends StatefulWidget { class _MachineListState extends State { List machines = []; - Set subscribedFloors = {}; bool selectFloors = false; GlobalState? state; List get floors => state?.dormitory?.floors ?? []; + Set get subscribedFloors => state?.subscribedFloors ?? {}; Type get type => widget.type; String get iconName => type == WashingMachine ? "drop_filled" : "wind"; String get title => type == WashingMachine ? "Washing Machine" : "Dryer Machine"; @@ -59,11 +59,11 @@ class _MachineListState extends State { const SizedBox(width: 8), Text(title.capitalizeEach, style: ThemeFont.header()), const SizedBox(width: 8), - _waitingButton(), + state?.currentMachine == null ? _waitingButton() : const SizedBox(height: 48), ], ), - _floorChipsPanel(), - const SizedBox(height: 2), + if (state?.currentMachine == null) _floorChipsPanel(), + const SizedBox(height: 8), GridView.count( clipBehavior: Clip.none, crossAxisCount: 3, @@ -89,7 +89,7 @@ class _MachineListState extends State { children: [ Flexible( child: Text( - subscribedFloors.isEmpty ? "Select Floors" : "${(subscribedFloors.toList()..sort((a, b) => (a - b))).join(',')}F", + state?.status != Status.waiting || subscribedFloors.isEmpty ? "Select Floors" : state!.subscribedFloorsString!, style: ThemeFont.small, // overflow: TextOverflow.ellipsis, ), @@ -117,7 +117,7 @@ class _MachineListState extends State { crossAxisAlignment: WrapCrossAlignment.center, children: [ /// Select All or Clear - subscribedFloors.length >= floors.length? _clearAllButton : _selectAllButton, + subscribedFloors.length >= floors.length ? _clearAllButton : _selectAllButton, /// Floor chips ...floors.map(floorChip).toList(), @@ -129,30 +129,32 @@ class _MachineListState extends State { ); Widget get _selectAllButton => SelectChip( - label: " All", - icon: "bell_outlined", - onSelected: (_) => setState(() { - subscribedFloors = floors.toSet(); - }), - ); + label: " All", + icon: "bell_outlined", + onSelected: (_) => setState(() { + GlobalState.set(context, subscribedFloors: floors.toSet(), status: Status.waiting); + }), + ); Widget get _clearAllButton => SelectChip( - label: " Clear", - onSelected: (_) => setState(() { - subscribedFloors = {}; - }), - ); + label: " Clear", + onSelected: (_) => setState(() { + GlobalState.set(context, subscribedFloors: {}, status: Status.idle); + }), + ); SelectChip floorChip(int floor) => SelectChip( - label: " ${floor}F", - icon: "bell_outlined", - isSelected: subscribedFloors.contains(floor), - onSelected: (isSelected) => setState(() { - if (isSelected) { - subscribedFloors.add(floor); - } else { - subscribedFloors.remove(floor); - } - }), - ); + label: " ${floor}F", + icon: "bell_outlined", + isSelected: state?.status == Status.waiting && subscribedFloors.contains(floor), + onSelected: (isSelected) => setState(() { + if (isSelected) { + subscribedFloors.add(floor); + GlobalState.set(context, status: Status.waiting); + } else { + subscribedFloors.remove(floor); + GlobalState.set(context, status: subscribedFloors.isEmpty ? Status.idle : null); + } + }), + ); } diff --git a/lib/views/components/status_card.dart b/lib/views/components/status_card.dart index 5567cf1..6417166 100644 --- a/lib/views/components/status_card.dart +++ b/lib/views/components/status_card.dart @@ -24,15 +24,13 @@ class StatusCard extends StatelessWidget { ? statusCard_available(state, context) : state.status == Status.idle ? statusCard_busy(state, context) - : state.status == Status.waitingFloor - ? statusCard_waitingFloor(state, context) - : state.status == Status.waitingAll - ? statusCard_waitingAll(state, context) - : state.status == Status.using - ? state.currentMachine?.status.code == StatusCode.in_use - ? statusCard_using(state, context) - : statusCard_overdue(state, context) - : const SizedBox(height: 0); + : state.status == Status.waiting + ? statusCard_waiting(state, context) + : state.status == Status.using + ? state.currentMachine?.status.code == StatusCode.in_use + ? statusCard_using(state, context) + : statusCard_overdue(state, context) + : const SizedBox(height: 0); } Widget statusCard_anonymous(BuildContext context) { @@ -57,12 +55,13 @@ class StatusCard extends StatelessWidget { Widget statusCard_busy(GlobalState state, BuildContext context) { return _statusCard( title: "Washing machines on ${state.floor}F are busy.", - description: 'Remind when any washing machine available on ${state.floor!}F?', + description: 'Remind when any washing machine available on ${state.subscribedFloorsString ?? "${state.floor}F"}?', actionWidget: const ActionText('Notify me', color: ThemeColors.royalBlue), onTap: () { - state.update(status: Status.waitingFloor, waitingMachine: WashingMachine); + if (state.subscribedFloors.isEmpty) state.subscribedFloors.add(state.floor!); + state.update(status: Status.waiting, waitingMachine: WashingMachine); // FakeData.setReminder(context); - if (state.status == Status.waitingAll || state.status == Status.waitingFloor) { + if (state.status == Status.waiting) { Future.delayed(const Duration(seconds: 10), () { GlobalState.set(context, currentMachine: FakeData.washingMachine); }); @@ -71,23 +70,18 @@ class StatusCard extends StatelessWidget { ); } - Widget statusCard_waitingFloor(GlobalState state, BuildContext context) { - return _statusCard( - title: "Waiting for a ${state.waitingMachine == WashingMachine ? 'washing' : 'dryer'} machine", - description: 'We’ll send you ${state.machineAvailable.remindMethod.toLowerCase()} when any machine available on ${state.floor}F!', - actionWidget: const ActionText('Check Other Floors', color: ThemeColors.royalBlue), - onTap: () { - state.update(status: Status.waitingAll, viewIndex: 1); - }); - } - - Widget statusCard_waitingAll(GlobalState state, BuildContext context) { + Widget statusCard_waiting(GlobalState state, BuildContext context) { return _statusCard( title: "Waiting for a ${state.waitingMachine == WashingMachine ? 'washing' : 'dryer'} machine", - description: - 'We’ll send you ${state.machineAvailable.remindMethod.toLowerCase()} when any machine available on ${state.floor! - 1 < state.dormitory!.floors[0] ? state.floor : state.floor! - 1}~${state.floor! + 1}F', - actionWidget: const ActionText('Cancel Waiting', icon: null), - onTap: () => state.update(status: Status.idle), + description: 'We’ll send you ${state.machineAvailable.remindMethod.toLowerCase()} when any machine available on ${state.subscribedFloorsString!}!', + actionWidget: state.viewIndex == 0 ? const ActionText('Check Other Floors', color: ThemeColors.royalBlue) : const ActionText('Cancel Waiting', icon: null), + onTap: () { + if (state.viewIndex == 0) { + state.update(viewIndex: 1); + } else { + state.update(status: Status.idle); + } + }, ); } diff --git a/lib/views/components/waiting_switch.dart b/lib/views/components/waiting_switch.dart index 21ab190..d4d7cb3 100644 --- a/lib/views/components/waiting_switch.dart +++ b/lib/views/components/waiting_switch.dart @@ -3,7 +3,6 @@ import 'package:flutter/material.dart'; import '../../models/global_state.dart'; import '../../theme/theme.dart'; - class WaitingSwitch extends StatelessWidget { const WaitingSwitch({ Key? key, @@ -15,29 +14,27 @@ class WaitingSwitch extends StatelessWidget { @override Widget build(BuildContext context) { var state = GlobalState.of(context); - // if (true) { - if (state.currentMachine == null) { - return SizedBox( - height: 48, - child: FittedBox( - fit: BoxFit.fill, - child: Switch( - thumbColor: MaterialStateProperty.all(ThemeColors.backgroundColor), - activeTrackColor: ThemeColors.primaryColor, - inactiveTrackColor: ThemeColors.grey, - activeThumbImage: const AssetImage("assets/images/switch_active_thumb.png"), - inactiveThumbImage: const AssetImage("assets/images/switch_inactive_thumb.png"), - value: state.waitingMachine == machineType && (state.status == Status.waitingFloor || state.status == Status.waitingAll), - onChanged: (value) { - if (state.waitingMachine != machineType || (state.status != Status.waitingFloor && state.status != Status.waitingAll)) { - state.update(status: Status.waitingFloor, waitingMachine: machineType); - } else if (state.status != Status.waitingFloor || state.status != Status.waitingAll) { - state.update(status: Status.idle); - } - }, - ), - ), - ); - } else return SizedBox(height: 42); + return SizedBox( + height: 48, + child: FittedBox( + fit: BoxFit.fill, + child: Switch( + thumbColor: MaterialStateProperty.all(ThemeColors.backgroundColor), + activeTrackColor: ThemeColors.primaryColor, + inactiveTrackColor: ThemeColors.grey, + activeThumbImage: const AssetImage("assets/images/switch_active_thumb.png"), + inactiveThumbImage: const AssetImage("assets/images/switch_inactive_thumb.png"), + value: state.waitingMachine == machineType && (state.status == Status.waiting), + onChanged: (value) { + if (state.waitingMachine != machineType || state.status != Status.waiting) { + if (state.subscribedFloors.isEmpty) state.subscribedFloors.add(state.floor!); + state.update(status: Status.waiting, waitingMachine: machineType); + } else { + state.update(status: Status.idle); + } + }, + ), + ), + ); } }