Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions composeApp/src/commonMain/composeResources/values-de/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,11 @@
<item quantity="one">eine Minute</item>
<item quantity="other">%1$d Minuten</item>
</plurals>
<plurals name="common_plural_seconds">
<item quantity="zero">no seconds</item>
<item quantity="one">one second</item>
<item quantity="other">%1$d seconds</item>
</plurals>
<plurals name="common_plural_portion">
<item quantity="zero">keine Portionen</item>
<item quantity="one">eine Portion</item>
Expand Down Expand Up @@ -192,6 +197,8 @@
<string name="common_website">Webseite</string>
<string name="common_welcome">Willkommen</string>
<string name="common_yesterday">Gestern</string>
<string name="common_default">Standard</string>

<string name="error">Fehler</string>
<string name="error_couldnt_connect_to_tandoor">Es konnte keine Verbindung zu deinem Tandoor-Server hergestellt werden.</string>
<string name="error_instance_inaccessible">Die Instanz, mit der du dich verbinden möchtest, ist nicht in dieser App eingerichtet.</string>
Expand Down Expand Up @@ -360,6 +367,16 @@
<string name="settings_section_server_delete_and_manage_data_label">Daten löschen und verwalten</string>
<string name="settings_section_server_description">Informationen über deinen Server</string>
<string name="settings_section_server_label">Server</string>
<string name="settings_section_server_advanced_label">Erweitert</string>
<string name="settings_section_server_advanced_description">Timeouts und weitere Netzwerkeinstellungen</string>
<string name="settings_section_server_advanced_short_timeout_label">Kurzes Timeout</string>
<string name="settings_section_server_advanced_short_timeout_description">Wird für reguläre API-Anfragen verwendet (%1$d Sekunden)</string>
<string name="settings_section_server_advanced_long_timeout_label">Langes Timeout</string>
<string name="settings_section_server_advanced_long_timeout_description">Für zeitintensive Vorgänge wie KI-Importe (%1$d Sekunden)</string>
<string name="settings_section_server_advanced_reset_label">Auf Standard zurücksetzen</string>
<string name="settings_section_server_advanced_reset_description">Alle Netzwerkeinstellungen auf die Standardwerte zurücksetzen</string>

<string name="settings_section_server_advanced_timeout_selection_title">Timeout auswählen</string>
<string name="share_incentive">Hey! Schau dir dieses Rezept in Tandoor an:</string>
<string name="share_title">Teile dieses Rezept von Tandoor</string>
<string name="share_wrapper_dialog_message">Wenn %1$s vor der geteilten URL steht, können andere kitshn-Nutzer den Link direkt in der App öffnen.</string>
Expand Down
10 changes: 10 additions & 0 deletions composeApp/src/commonMain/composeResources/values-fr/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@
<string name="common_username">Nom d'utilisateur</string>
<string name="common_website">Site web</string>
<string name="common_welcome">Bienvenue</string>
<string name="common_default">Défaut</string>
<string name="error_couldnt_connect_to_tandoor">Aucune connexion n'a pu être établie avec votre serveur Tandoor.</string>
<string name="error_request">Demande erronée</string>
<string name="error_unallocated_ingredients">Ingrédients non attribués</string>
Expand Down Expand Up @@ -422,5 +423,14 @@
<string name="settings_section_behavior_shopping_item_double_click_check_label">Cocher les articles par double-clic</string>
<string name="settings_section_behavior_shopping_item_double_click_check_description">Marquez rapidement vos articles comme terminés en double-cliquant dessus</string>
<string name="common_shopping_list">Liste de courses</string>
<string name="settings_section_server_advanced_label">Avancé</string>
<string name="settings_section_server_advanced_description">Délais d'attente et autres paramètres réseau</string>
<string name="settings_section_server_advanced_short_timeout_label">Délai d'attente court</string>
<string name="settings_section_server_advanced_short_timeout_description">Utilisé pour les requêtes API classiques (%1$d secondes)</string>
<string name="settings_section_server_advanced_long_timeout_label">Délai d'attente prolongé</string>
<string name="settings_section_server_advanced_long_timeout_description">Utilisé pour les opérations longues comme l'importation IA (%1$d secondes)</string>
<string name="settings_section_server_advanced_reset_label">Réinitialiser par défaut</string>
<string name="settings_section_server_advanced_reset_description">Rétablir toutes les valeurs par défaut des paramètres réseau</string>
<string name="settings_section_server_advanced_timeout_selection_title">Sélectionner le délai</string>
<string name="recipe_step_step_description">Ajoutez les instructions pour cette étape</string>
</resources>
15 changes: 15 additions & 0 deletions composeApp/src/commonMain/composeResources/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,11 @@
<item quantity="one">one minute</item>
<item quantity="other">%1$d minutes</item>
</plurals>
<plurals name="common_plural_seconds">
<item quantity="zero">no seconds</item>
<item quantity="one">one second</item>
<item quantity="other">%1$d seconds</item>
</plurals>
<plurals name="common_plural_portion">
<item quantity="zero">no servings</item>
<item quantity="one">one serving</item>
Expand Down Expand Up @@ -214,6 +219,7 @@
<string name="common_website">Website</string>
<string name="common_welcome">Welcome</string>
<string name="common_yesterday">Yesterday</string>
<string name="common_default">Default</string>

<string name="error">Error</string>
<string name="error_couldnt_connect_to_tandoor">No connection to your Tandoor server could be established.</string>
Expand Down Expand Up @@ -423,6 +429,15 @@
<string name="settings_section_server_delete_and_manage_data_label">Delete and manage data</string>
<string name="settings_section_server_description">Information about your instance</string>
<string name="settings_section_server_label">Server</string>
<string name="settings_section_server_advanced_label">Advanced</string>
<string name="settings_section_server_advanced_description">Timeout and other network settings</string>
<string name="settings_section_server_advanced_short_timeout_label">Short timeout</string>
<string name="settings_section_server_advanced_short_timeout_description">Used for regular API requests (%1$d seconds)</string>
<string name="settings_section_server_advanced_long_timeout_label">Long timeout</string>
<string name="settings_section_server_advanced_long_timeout_description">Used for long-running operations like AI imports (%1$d seconds)</string>
<string name="settings_section_server_advanced_reset_label">Restore default settings</string>
<string name="settings_section_server_advanced_reset_description">Set all network settings back to their default values</string>
<string name="settings_section_server_advanced_timeout_selection_title">Select timeout</string>

<string name="share_incentive">Hey! Check out this recipe I've found on Tandoor:</string>
<string name="share_title">Share this recipe on Tandoor</string>
Expand Down
10 changes: 10 additions & 0 deletions composeApp/src/commonMain/kotlin/de/kitshn/KitshnViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.navigation.NavHostController
Expand All @@ -23,6 +24,7 @@ import io.ktor.client.plugins.HttpTimeout
import io.ktor.http.HttpMethod
import io.ktor.http.HttpStatusCode
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.serialization.SerializationException
Expand Down Expand Up @@ -107,6 +109,14 @@ class KitshnViewModel(
if(tandoorClient == null) tandoorClient = TandoorClient(credentials)
favorites.init(tandoorClient!!)

viewModelScope.launch {
snapshotFlow { tandoorClient }
.combine(settings.getTandoorTimeoutSettings) { client, timeout -> client to timeout }
.collect { (client, timeout) ->
client?.configureTimeouts(timeout)
}
}

connectivityCheck()

try {
Expand Down
10 changes: 10 additions & 0 deletions composeApp/src/commonMain/kotlin/de/kitshn/Settings.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import com.russhwolf.settings.coroutines.getStringFlow
import com.russhwolf.settings.coroutines.getStringOrNullFlow
import com.russhwolf.settings.observable.makeObservable
import de.kitshn.api.tandoor.TandoorCredentials
import de.kitshn.api.tandoor.TandoorTimeoutSettings
import de.kitshn.api.tandoor.model.shopping.TandoorShoppingList
import de.kitshn.api.tandoor.model.shopping.TandoorSupermarket
import kotlinx.coroutines.flow.Flow
Expand Down Expand Up @@ -60,6 +61,7 @@ const val KEY_SETTINGS_SHOPPING_LISTS = "shopping_lists"

const val KEY_SETTINGS_ONBOARDING_COMPLETED = "onboarding_completed"
const val KEY_SETTINGS_TANDOOR_CREDENTIALS = "tandoor_credentials"
const val KEY_SETTINGS_TANDOOR_TIMEOUT_SETTINGS = "tandoor_timeout_settings"

const val KEY_SETTINGS_IOS_TIMER_SHORTCUT_INSTALLED = "ios_timer_shortcut_installed"

Expand Down Expand Up @@ -111,6 +113,14 @@ class SettingsViewModel : ViewModel() {
fun saveTandoorCredentials(credentials: TandoorCredentials?) =
obs.putString(KEY_SETTINGS_TANDOOR_CREDENTIALS, json.encodeToString(credentials))

// timeouts
val getTandoorTimeoutSettings: Flow<TandoorTimeoutSettings> = obs.getStringFlow(
KEY_SETTINGS_TANDOOR_TIMEOUT_SETTINGS, "{}"
).map { json.maybeDecodeFromString<TandoorTimeoutSettings>(it) ?: TandoorTimeoutSettings() }

fun setTandoorTimeoutSettings(settings: TandoorTimeoutSettings) =
obs.putString(KEY_SETTINGS_TANDOOR_TIMEOUT_SETTINGS, json.encodeToString(settings))

// iOS timer shortcut installed
val getIosTimerShortcutInstalled: Flow<Boolean> =
obs.getBooleanFlow(KEY_SETTINGS_IOS_TIMER_SHORTCUT_INSTALLED, false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.decodeFromJsonElement
import kotlinx.serialization.json.put

@Serializable
data class TandoorTimeoutSettings(
val shortTimeout: Long = 10000L,
val longTimeout: Long = 60000L
)

@Serializable
data class TandoorCredentialsCustomHeader(
var field: String,
Expand All @@ -52,23 +58,45 @@ data class TandoorCredentials(
)

class TandoorClient(
val credentials: TandoorCredentials
val credentials: TandoorCredentials,
var timeoutSettings: TandoorTimeoutSettings = TandoorTimeoutSettings()
) {

val httpClient = HttpClient {
var httpClient = createHttpClient(timeoutSettings.shortTimeout)

var longHttpClient = createLongHttpClient(timeoutSettings.longTimeout)

private fun createHttpClient(timeout: Long) = HttpClient {
followRedirects = true

install(HttpTimeout) {
connectTimeoutMillis = timeout
requestTimeoutMillis = timeout
socketTimeoutMillis = timeout
}
}

val longHttpClient = HttpClient {
private fun createLongHttpClient(timeout: Long) = HttpClient {
followRedirects = true

install(HttpTimeout) {
connectTimeoutMillis = 60000
requestTimeoutMillis = 60000
socketTimeoutMillis = 60000
connectTimeoutMillis = timeout
requestTimeoutMillis = timeout
socketTimeoutMillis = timeout
}
}

fun configureTimeouts(settings: TandoorTimeoutSettings) {
if(settings == timeoutSettings) return
timeoutSettings = settings

httpClient.close()
longHttpClient.close()

httpClient = createHttpClient(settings.shortTimeout)
longHttpClient = createLongHttpClient(settings.longTimeout)
}

val container = TandoorContainer(this)
val media = TandoorMedia(this)

Expand Down Expand Up @@ -122,5 +150,4 @@ class TandoorClient(
return false
}
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package de.kitshn.ui.dialog

import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Check
import androidx.compose.material.icons.rounded.Timer
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import de.kitshn.ui.component.settings.SettingsListItem
import de.kitshn.ui.component.settings.SettingsListItemPosition
import kitshn.composeapp.generated.resources.Res
import kitshn.composeapp.generated.resources.common_default
import kitshn.composeapp.generated.resources.common_plural_seconds
import org.jetbrains.compose.resources.pluralStringResource
import org.jetbrains.compose.resources.stringResource

@Composable
fun rememberTimeoutSelectionBottomSheetState(): TimeoutSelectionBottomSheetState {
return remember {
TimeoutSelectionBottomSheetState()
}
}

class TimeoutSelectionBottomSheetState(
val shown: MutableState<Boolean> = mutableStateOf(false)
) {
var options by mutableStateOf<List<Long>>(listOf())
var selectedValue by mutableStateOf(0L)
var defaultValue by mutableStateOf(0L)
var onSelect: (Long) -> Unit = {}

fun open(
options: List<Long>,
selectedValue: Long,
defaultValue: Long,
onSelect: (Long) -> Unit
) {
this.options = options
this.selectedValue = selectedValue
this.defaultValue = defaultValue
this.onSelect = onSelect
this.shown.value = true
}

fun dismiss() {
this.shown.value = false
}
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TimeoutSelectionBottomSheet(
state: TimeoutSelectionBottomSheetState
) {
if(!state.shown.value) return

ModalBottomSheet(
onDismissRequest = {
state.dismiss()
}
) {
LazyColumn {
items(state.options.size) { index ->
val option = state.options[index]
val position = when {
state.options.size == 1 -> SettingsListItemPosition.SINGULAR
index == 0 -> SettingsListItemPosition.TOP
index == state.options.size - 1 -> SettingsListItemPosition.BOTTOM
else -> SettingsListItemPosition.BETWEEN
}

val label = pluralStringResource(
Res.plurals.common_plural_seconds,
(option / 1000).toInt(),
(option / 1000).toInt()
)

SettingsListItem(
position = position,
label = {
Row(verticalAlignment = Alignment.CenterVertically) {
Text(label)
if(option == state.defaultValue) {
Text(
modifier = Modifier.padding(start = 8.dp),
text = "(${stringResource(Res.string.common_default)})",
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.primary
)
}
}
},
icon = Icons.Rounded.Timer,
trailingContent = {
if(option == state.selectedValue) {
Icon(Icons.Rounded.Check, null)
}
},
contentDescription = label
) {
state.onSelect(option)
}
}
}
}
}
Loading