Skip to content

Commit f540626

Browse files
committed
feat: validate badge printer availability
1 parent 18eff36 commit f540626

File tree

7 files changed

+115
-8
lines changed

7 files changed

+115
-8
lines changed

pretixscan/composeApp/src/commonMain/composeResources/values/strings.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,4 +217,11 @@
217217
<string name="settings_label_badge_layout">Orientation</string>
218218
<string name="sync_completed">Sync completed successfully</string>
219219
<string name="sync_error">Sync completed with errors</string>
220+
<string name="badge_printing_not_available">Badge printing unavailable</string>
221+
<string name="badge_printing_not_possible_no_available_printer">There are no available printers on the system.
222+
Please, configure at least one printer and try again.
223+
</string>
224+
<string name="badge_printing_selected_printer_not_available">The selected printer is currently unavailable. Please
225+
check that the device is online and connected to the system.
226+
</string>
220227
</resources>

pretixscan/composeApp/src/commonMain/kotlin/eu/pretix/scan/setup/ErrorDialog.kt renamed to pretixscan/composeApp/src/commonMain/kotlin/eu/pretix/desktop/app/ui/ErrorDialog.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package eu.pretix.scan.setup
1+
package eu.pretix.desktop.app.ui
22

33
import androidx.compose.foundation.layout.*
44
import androidx.compose.foundation.shape.RoundedCornerShape

pretixscan/composeApp/src/commonMain/kotlin/eu/pretix/scan/settings/presentation/SettingsScreen.kt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ fun SettingsScreen(
3838

3939
val viewModel = koinViewModel<SettingsViewModel>()
4040
val form by viewModel.form.collectAsState()
41+
val uiState by viewModel.uiState.collectAsState()
4142
val state = rememberLazyListState()
4243

4344
LaunchedEffect(Unit) {
@@ -298,6 +299,29 @@ fun SettingsScreen(
298299
}
299300
}
300301
}
302+
303+
when (uiState) {
304+
is SettingsUiState.Start -> {}
305+
is SettingsUiState.ErrorNoAvailablePrinters -> {
306+
ErrorDialog(
307+
title = stringResource(Res.string.badge_printing_not_available),
308+
message = stringResource(Res.string.badge_printing_not_possible_no_available_printer),
309+
onDismiss = {
310+
viewModel.dismissError()
311+
}
312+
)
313+
}
314+
315+
is SettingsUiState.ErrorSelectedPrinterNotAvailable -> {
316+
ErrorDialog(
317+
title = stringResource(Res.string.badge_printing_not_available),
318+
message = stringResource(Res.string.badge_printing_selected_printer_not_available),
319+
onDismiss = {
320+
viewModel.dismissError()
321+
}
322+
)
323+
}
324+
}
301325
}
302326

303327
@Composable
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package eu.pretix.scan.settings.presentation
2+
3+
sealed class SettingsUiState<out T> {
4+
object Start : SettingsUiState<Nothing>()
5+
object ErrorNoAvailablePrinters : SettingsUiState<Nothing>()
6+
object ErrorSelectedPrinterNotAvailable : SettingsUiState<Nothing>()
7+
}

pretixscan/composeApp/src/commonMain/kotlin/eu/pretix/scan/settings/presentation/SettingsViewModel.kt

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ import eu.pretix.desktop.cache.Version
1010
import eu.pretix.scan.settings.data.ConfigurableSettings
1111
import eu.pretix.scan.settings.data.PrinterSource
1212
import kotlinx.coroutines.flow.MutableStateFlow
13+
import kotlinx.coroutines.flow.StateFlow
1314
import kotlinx.coroutines.flow.asStateFlow
15+
import kotlinx.coroutines.flow.update
1416

1517

1618
class SettingsViewModel(
@@ -23,7 +25,15 @@ class SettingsViewModel(
2325
private val _form = MutableStateFlow(ConfigurableSettings())
2426
val form = _form.asStateFlow()
2527

28+
private val _uiState = MutableStateFlow<SettingsUiState<String>>(SettingsUiState.Start)
29+
val uiState: StateFlow<SettingsUiState<String>> = _uiState
30+
31+
fun dismissError() {
32+
_uiState.update { SettingsUiState.Start }
33+
}
34+
2635
suspend fun loadSettings() {
36+
val badgePrinterWasSelected = appConfig.printBadges && appConfig.badgePrinterName != null
2737
_form.value = _form.value.copy(
2838
version = "${Version.version} (${Version.versionCode})",
2939
printers = printerSource.listPrinters(),
@@ -37,6 +47,11 @@ class SettingsViewModel(
3747
uiReduceMotion = appConfig.uiReduceMotion,
3848
uiHideNames = appConfig.uiHideNames,
3949
)
50+
51+
// check if printer setup is correct
52+
if (_form.value.printBadges && badgePrinterWasSelected && _form.value.badgePrinter == null) {
53+
_uiState.update { SettingsUiState.ErrorSelectedPrinterNotAvailable }
54+
}
4055
}
4156

4257
suspend fun setBadgePrinter(option: SelectableValue?) {
@@ -56,8 +71,27 @@ class SettingsViewModel(
5671
}
5772

5873
suspend fun setPrintBadges(value: Boolean) {
59-
appConfig.printBadges = value
60-
loadSettings()
74+
if (value) {
75+
// turning badge printing on, check that we have at least one selectable printer
76+
loadSettings()
77+
val firstPrinter = _form.value.printers.firstOrNull()
78+
if (firstPrinter == null) {
79+
// sorry
80+
appConfig.printBadges = false
81+
loadSettings()
82+
_uiState.update { SettingsUiState.ErrorNoAvailablePrinters }
83+
return
84+
}
85+
86+
// turn printing on, preselect the first printer
87+
appConfig.printBadges = true
88+
setBadgePrinter(firstPrinter)
89+
90+
} else {
91+
// turning printing off
92+
appConfig.printBadges = false
93+
loadSettings()
94+
}
6195
}
6296

6397
suspend fun setSyncAuto(value: Boolean) {

pretixscan/composeApp/src/commonMain/kotlin/eu/pretix/scan/tickets/presentation/TicketHandlingDialog.kt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,17 @@ import androidx.compose.ui.graphics.Color
1414
import androidx.compose.ui.input.key.*
1515
import androidx.compose.ui.unit.dp
1616
import com.composables.core.*
17+
import eu.pretix.desktop.app.ui.ErrorDialog
1718
import eu.pretix.scan.tickets.data.ResultState
1819
import eu.pretix.scan.tickets.data.isAutoDismissible
1920
import kotlinx.coroutines.CoroutineScope
2021
import kotlinx.coroutines.Dispatchers
2122
import kotlinx.coroutines.launch
23+
import org.jetbrains.compose.resources.stringResource
2224
import org.jetbrains.compose.ui.tooling.preview.Preview
2325
import org.koin.compose.viewmodel.koinViewModel
26+
import pretixscan.composeapp.generated.resources.Res
27+
import pretixscan.composeapp.generated.resources.badge_printing_not_available
2428
import java.util.logging.Logger
2529

2630
private val log = Logger.getLogger("TicketHandlingDialog")
@@ -36,6 +40,7 @@ fun TicketHandlingDialog(
3640

3741
val viewModel = koinViewModel<TicketHandlingDialogViewModel>()
3842
val uiState by viewModel.uiState.collectAsState()
43+
val localTicketHandlingErrors by viewModel.localTicketHandlingErrors.collectAsState()
3944
val coroutineScope = rememberCoroutineScope()
4045
val dialogState = rememberDialogState(initiallyVisible = true)
4146
var remainingTimeProgress by remember { mutableStateOf(1.0f) }
@@ -169,5 +174,19 @@ fun TicketHandlingDialog(
169174
}
170175
}
171176
}
177+
178+
179+
when (localTicketHandlingErrors) {
180+
is TicketHandlingErrors.None -> {}
181+
is TicketHandlingErrors.Error -> {
182+
ErrorDialog(
183+
title = stringResource(Res.string.badge_printing_not_available),
184+
message = (localTicketHandlingErrors as TicketHandlingErrors.Error).exception,
185+
onDismiss = {
186+
viewModel.dismissError()
187+
}
188+
)
189+
}
190+
}
172191
}
173192

pretixscan/composeApp/src/commonMain/kotlin/eu/pretix/scan/tickets/presentation/TicketHandlingDialogViewModel.kt

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@ import eu.pretix.scan.tickets.data.ResultState
77
import eu.pretix.scan.tickets.data.ResultStateData
88
import eu.pretix.scan.tickets.data.TicketCodeHandler
99
import kotlinx.coroutines.flow.MutableStateFlow
10+
import kotlinx.coroutines.flow.StateFlow
1011
import kotlinx.coroutines.flow.asStateFlow
1112
import kotlinx.coroutines.flow.update
1213
import java.util.logging.Logger
1314

1415
class TicketHandlingDialogViewModel(
1516
private val tickerCodeHandler: TicketCodeHandler,
16-
private val badgeFactory: BadgeFactory
17+
private val badgeFactory: BadgeFactory,
1718
) : ViewModel() {
1819

1920
private val log = Logger.getLogger("TicketHandlingDialogViewModel")
@@ -25,6 +26,13 @@ class TicketHandlingDialogViewModel(
2526
_uiState.value = ResultStateData(resultState = ResultState.EMPTY)
2627
}
2728

29+
private val _localTicketHandlingErrors = MutableStateFlow<TicketHandlingErrors<String>>(TicketHandlingErrors.None)
30+
val localTicketHandlingErrors: StateFlow<TicketHandlingErrors<String>> = _localTicketHandlingErrors
31+
32+
fun dismissError() {
33+
_localTicketHandlingErrors.update { TicketHandlingErrors.None }
34+
}
35+
2836
suspend fun handleTicket(secret: String?, answers: List<Answer>? = null, ignoreUnpaid: Boolean = false) {
2937
log.info("Handling ticket")
3038
_uiState.update {
@@ -41,7 +49,7 @@ class TicketHandlingDialogViewModel(
4149
}
4250

4351
suspend fun printBadges() {
44-
log.info("Printing badges")
52+
log.info("User requested to print a badge")
4553
val layout = _uiState.value.badgeLayout
4654
val position = _uiState.value.position
4755
if (layout == null) {
@@ -54,8 +62,16 @@ class TicketHandlingDialogViewModel(
5462
return
5563
}
5664

57-
58-
badgeFactory.setup()
59-
badgeFactory.printBadges(layout, position)
65+
try {
66+
badgeFactory.setup()
67+
badgeFactory.printBadges(layout, position)
68+
} catch (e: Exception) {
69+
_localTicketHandlingErrors.update { TicketHandlingErrors.Error(e.localizedMessage) }
70+
}
6071
}
72+
}
73+
74+
sealed class TicketHandlingErrors<out T> {
75+
object None : TicketHandlingErrors<Nothing>()
76+
data class Error(val exception: String) : TicketHandlingErrors<Nothing>()
6177
}

0 commit comments

Comments
 (0)