mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-09 16:11:20 +02:00
WIP login workflow
This commit is contained in:
parent
1030741631
commit
2161a9de56
5 changed files with 294 additions and 4 deletions
|
|
@ -20,10 +20,10 @@ import org.jellyfin.sdk.model.serializer.UUIDSerializer
|
||||||
@Serializable
|
@Serializable
|
||||||
data class SeerrServer(
|
data class SeerrServer(
|
||||||
@PrimaryKey(autoGenerate = true)
|
@PrimaryKey(autoGenerate = true)
|
||||||
val id: Int,
|
val id: Int = 0,
|
||||||
val name: String?,
|
|
||||||
val url: String,
|
val url: String,
|
||||||
val version: String?,
|
val name: String? = null,
|
||||||
|
val version: String? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Entity(
|
@Entity(
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ class SeerrApi(
|
||||||
|
|
||||||
fun update(
|
fun update(
|
||||||
baseUrl: String,
|
baseUrl: String,
|
||||||
apiKey: String,
|
apiKey: String?,
|
||||||
) {
|
) {
|
||||||
api = SeerrApiClient(baseUrl, apiKey, okHttpClient)
|
api = SeerrApiClient(baseUrl, apiKey, okHttpClient)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,132 @@
|
||||||
|
package com.github.damontecres.wholphin.services
|
||||||
|
|
||||||
|
import com.github.damontecres.wholphin.api.seerr.model.AuthJellyfinPostRequest
|
||||||
|
import com.github.damontecres.wholphin.api.seerr.model.AuthLocalPostRequest
|
||||||
|
import com.github.damontecres.wholphin.data.SeerrServerDao
|
||||||
|
import com.github.damontecres.wholphin.data.ServerRepository
|
||||||
|
import com.github.damontecres.wholphin.data.model.SeerrAuthMethod
|
||||||
|
import com.github.damontecres.wholphin.data.model.SeerrServer
|
||||||
|
import com.github.damontecres.wholphin.data.model.SeerrUser
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class SeerrServerRepository
|
||||||
|
@Inject
|
||||||
|
constructor(
|
||||||
|
private val seerrApi: SeerrApi,
|
||||||
|
private val seerrServerDao: SeerrServerDao,
|
||||||
|
private val serverRepository: ServerRepository,
|
||||||
|
) {
|
||||||
|
private val _currentServer = MutableStateFlow<SeerrServer?>(null)
|
||||||
|
val currentServer: StateFlow<SeerrServer?> = _currentServer
|
||||||
|
|
||||||
|
private val _currentUser = MutableStateFlow<SeerrUser?>(null)
|
||||||
|
val currentUser: StateFlow<SeerrUser?> = _currentUser
|
||||||
|
|
||||||
|
val current =
|
||||||
|
currentServer.combine(currentUser) { server, user ->
|
||||||
|
if (server != null && user != null) {
|
||||||
|
CurrentSeerr(server, user)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun addAndChangeServer(
|
||||||
|
url: String,
|
||||||
|
apiKey: String,
|
||||||
|
) {
|
||||||
|
var server = seerrServerDao.getServer(url)
|
||||||
|
if (server == null) {
|
||||||
|
seerrServerDao.addServer(SeerrServer(url = url))
|
||||||
|
server = seerrServerDao.getServer(url)
|
||||||
|
}
|
||||||
|
server?.server?.let { server ->
|
||||||
|
serverRepository.currentUser.value?.let { jellyfinUser ->
|
||||||
|
// TODO test api key
|
||||||
|
val user =
|
||||||
|
SeerrUser(
|
||||||
|
jellyfinRowId = jellyfinUser.rowId,
|
||||||
|
serverId = server.id,
|
||||||
|
authMethod = SeerrAuthMethod.API_KEY,
|
||||||
|
username = null,
|
||||||
|
password = null,
|
||||||
|
credential = apiKey,
|
||||||
|
)
|
||||||
|
seerrServerDao.addUser(user)
|
||||||
|
// TODO user info
|
||||||
|
seerrApi.api.usersApi.authMeGet()
|
||||||
|
|
||||||
|
seerrApi.update(server.url, apiKey)
|
||||||
|
_currentServer.update {
|
||||||
|
_currentUser.update {
|
||||||
|
user
|
||||||
|
}
|
||||||
|
server
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun addAndChangeServer(
|
||||||
|
url: String,
|
||||||
|
authMethod: SeerrAuthMethod,
|
||||||
|
username: String,
|
||||||
|
password: String,
|
||||||
|
) {
|
||||||
|
var server = seerrServerDao.getServer(url)
|
||||||
|
if (server == null) {
|
||||||
|
seerrServerDao.addServer(SeerrServer(url = url))
|
||||||
|
server = seerrServerDao.getServer(url)
|
||||||
|
}
|
||||||
|
server?.server?.let { server ->
|
||||||
|
serverRepository.currentUser.value?.let { jellyfinUser ->
|
||||||
|
val response =
|
||||||
|
when (authMethod) {
|
||||||
|
SeerrAuthMethod.LOCAL -> {
|
||||||
|
seerrApi.api.authApi.authLocalPostWithHttpInfo(
|
||||||
|
AuthLocalPostRequest(
|
||||||
|
email = username,
|
||||||
|
password = password,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
SeerrAuthMethod.JELLYFIN -> {
|
||||||
|
seerrApi.api.authApi.authJellyfinPostWithHttpInfo(
|
||||||
|
AuthJellyfinPostRequest(
|
||||||
|
username = username,
|
||||||
|
password = password,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
SeerrAuthMethod.API_KEY -> {
|
||||||
|
throw IllegalArgumentException("Cannot authenticate with API key using this function")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
response.headers["Cookies"]
|
||||||
|
// TODO get session cookie
|
||||||
|
val user =
|
||||||
|
SeerrUser(
|
||||||
|
jellyfinRowId = jellyfinUser.rowId,
|
||||||
|
serverId = server.id,
|
||||||
|
authMethod = SeerrAuthMethod.API_KEY,
|
||||||
|
username = username,
|
||||||
|
password = password,
|
||||||
|
credential = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class CurrentSeerr(
|
||||||
|
val server: SeerrServer,
|
||||||
|
val user: SeerrUser,
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,137 @@
|
||||||
|
package com.github.damontecres.wholphin.ui.setup.seerr
|
||||||
|
|
||||||
|
import androidx.compose.foundation.focusGroup
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.wrapContentSize
|
||||||
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
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.focus.FocusRequester
|
||||||
|
import androidx.compose.ui.focus.focusRequester
|
||||||
|
import androidx.compose.ui.text.input.ImeAction
|
||||||
|
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||||
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.tv.material3.MaterialTheme
|
||||||
|
import androidx.tv.material3.Text
|
||||||
|
import com.github.damontecres.wholphin.R
|
||||||
|
import com.github.damontecres.wholphin.ui.components.EditTextBox
|
||||||
|
import com.github.damontecres.wholphin.ui.components.TextButton
|
||||||
|
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
||||||
|
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun AddSeerrServerContent(
|
||||||
|
onSubmit: (url: String, apiKey: String) -> Unit,
|
||||||
|
errorMessage: String?,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
var error by remember(errorMessage) { mutableStateOf(errorMessage) }
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
modifier =
|
||||||
|
modifier
|
||||||
|
.focusGroup()
|
||||||
|
.padding(16.dp)
|
||||||
|
.wrapContentSize(),
|
||||||
|
) {
|
||||||
|
var url by remember { mutableStateOf("") }
|
||||||
|
var apiKey by remember { mutableStateOf("") }
|
||||||
|
|
||||||
|
val focusRequester = remember { FocusRequester() }
|
||||||
|
val passwordFocusRequester = remember { FocusRequester() }
|
||||||
|
LaunchedEffect(Unit) { focusRequester.tryRequestFocus() }
|
||||||
|
Text(
|
||||||
|
text = "Enter URL & API Key",
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
)
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier.align(Alignment.CenterHorizontally),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "URL",
|
||||||
|
modifier = Modifier.padding(end = 8.dp),
|
||||||
|
)
|
||||||
|
EditTextBox(
|
||||||
|
value = url,
|
||||||
|
onValueChange = {
|
||||||
|
error = null
|
||||||
|
url = it
|
||||||
|
},
|
||||||
|
keyboardOptions =
|
||||||
|
KeyboardOptions(
|
||||||
|
capitalization = KeyboardCapitalization.None,
|
||||||
|
autoCorrectEnabled = false,
|
||||||
|
keyboardType = KeyboardType.Text,
|
||||||
|
imeAction = ImeAction.Next,
|
||||||
|
),
|
||||||
|
keyboardActions =
|
||||||
|
KeyboardActions(
|
||||||
|
onNext = {
|
||||||
|
passwordFocusRequester.tryRequestFocus()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
isInputValid = { true },
|
||||||
|
modifier = Modifier.focusRequester(focusRequester),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier.align(Alignment.CenterHorizontally),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "API Key",
|
||||||
|
modifier = Modifier.padding(end = 8.dp),
|
||||||
|
)
|
||||||
|
EditTextBox(
|
||||||
|
value = apiKey,
|
||||||
|
onValueChange = {
|
||||||
|
error = null
|
||||||
|
apiKey = it
|
||||||
|
},
|
||||||
|
keyboardOptions =
|
||||||
|
KeyboardOptions(
|
||||||
|
capitalization = KeyboardCapitalization.None,
|
||||||
|
autoCorrectEnabled = false,
|
||||||
|
keyboardType = KeyboardType.Password,
|
||||||
|
imeAction = ImeAction.Go,
|
||||||
|
),
|
||||||
|
keyboardActions =
|
||||||
|
KeyboardActions(
|
||||||
|
onGo = { onSubmit.invoke(url, apiKey) },
|
||||||
|
),
|
||||||
|
isInputValid = { true },
|
||||||
|
modifier = Modifier.focusRequester(passwordFocusRequester),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
error?.let {
|
||||||
|
Text(
|
||||||
|
text = it,
|
||||||
|
color = MaterialTheme.colorScheme.error,
|
||||||
|
style = MaterialTheme.typography.titleLarge,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
TextButton(
|
||||||
|
stringRes = R.string.submit,
|
||||||
|
onClick = { onSubmit.invoke(url, apiKey) },
|
||||||
|
enabled = error.isNullOrBlank() && url.isNotNullOrBlank() && apiKey.isNotNullOrBlank(),
|
||||||
|
modifier = Modifier.align(Alignment.CenterHorizontally),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
package com.github.damontecres.wholphin.ui.setup.seerr
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import com.github.damontecres.wholphin.services.SeerrServerRepository
|
||||||
|
import com.github.damontecres.wholphin.services.SeerrService
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import jakarta.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class SwitchSeerrViewModel
|
||||||
|
@Inject
|
||||||
|
constructor(
|
||||||
|
private val seerrServerRepository: SeerrServerRepository,
|
||||||
|
private val seerrService: SeerrService,
|
||||||
|
) : ViewModel() {
|
||||||
|
fun submitServer(
|
||||||
|
url: String,
|
||||||
|
apiKey: String,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue