Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import android.content.Intent
import android.net.Uri
import android.os.Build
import android.provider.Settings
import android.widget.Toast
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import androidx.core.net.toUri
Expand Down Expand Up @@ -60,7 +61,40 @@ internal class AndroidIntentManager(
data = Uri.fromParts(PACKAGE_SCHEME, context.packageName, null)
}

context.startActivity(intent, null)
try {
context.startActivity(intent, null)
} catch (_: Exception) {
Toast.makeText(context, "Couldn't not open app settings", Toast.LENGTH_SHORT).show()
}
}

override fun launchEmail(
email: String,
subject: String?,
body: String?,
) {
val emailIntent = Intent(Intent.ACTION_SENDTO).apply {
val uriString = buildString {
append("mailto:$email")

val params = mutableListOf<String>()
subject?.let { params.add("subject=${Uri.encode(it)}") }
body?.let { params.add("body=${Uri.encode(it)}") }

if (params.isNotEmpty()) {
append("?")
append(params.joinToString("&"))
}
}

data = uriString.toUri()
}

try {
context.startActivity(emailIntent)
} catch (_: Exception) {
Toast.makeText(context, "No email app installed", Toast.LENGTH_SHORT).show()
}
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ object TestTags {
const val USERNAME_TEXT_FIELD = "Username input"
const val PASSWORD_TEXT_FIELD = "Password input"

const val EMAIL_SUPPORT = "Email support field"
const val JELLYSEERR_LOGIN_BUTTON = "Jellyseerr Login Button"
const val JELLYSEERR_LOGOUT_BUTTON = "Jellyseerr Logout Button"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ interface IntentManager {

fun shareErrorReport(throwable: Throwable)

fun launchEmail(
email: String,
subject: String?,
body: String?,
)

fun navigateToAppSettings()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@ package com.divinelink.core.ui.manager

import androidx.compose.runtime.Composable
import com.divinelink.core.commons.provider.BuildConfigProvider
import kotlinx.cinterop.BetaInteropApi
import platform.Foundation.NSCharacterSet
import platform.Foundation.NSString
import platform.Foundation.NSURL
import platform.Foundation.URLQueryAllowedCharacterSet
import platform.Foundation.create
import platform.Foundation.stringByAddingPercentEncodingWithAllowedCharacters
import platform.UIKit.UIActivityViewController
import platform.UIKit.UIApplication
import platform.UIKit.popoverPresentationController
Expand Down Expand Up @@ -49,6 +56,39 @@ class IOSIntentManager(
.toString(),
)

override fun launchEmail(
email: String,
subject: String?,
body: String?,
) {
val urlString = buildString {
append("mailto:$email")

val params = mutableListOf<String>()
subject?.let { params.add("subject=${it.encodeURLComponent()}") }
body?.let { params.add("body=${it.encodeURLComponent()}") }

if (params.isNotEmpty()) {
append("?")
append(params.joinToString("&"))
}
}

val url = NSURL.URLWithString(urlString) ?: return

UIApplication.sharedApplication.openURL(url, options = emptyMap<Any?, Any?>()) { success ->
if (!success) {
println("Failed to open email app")
}
}
}

@OptIn(BetaInteropApi::class)
private fun String.encodeURLComponent(): String =
NSString.create(string = this).stringByAddingPercentEncodingWithAllowedCharacters(
NSCharacterSet.URLQueryAllowedCharacterSet,
) ?: this

override fun navigateToAppSettings() {
// Do nothing
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,16 @@ class JellyseerrSettingsScreenTest : ComposeTest() {
TestTags.Settings.Jellyseerr.JELLYSEERR_LOGIN_BUTTON,
).assertIsNotEnabled()

onNodeWithTag(TestTags.Settings.Jellyseerr.INITIAL_CONTENT).performScrollToNode(
hasText("Jellyfin"),
)

onNodeWithText("Jellyfin").assertIsDisplayed().performClick()

onNodeWithTag(TestTags.Settings.Jellyseerr.INITIAL_CONTENT).performScrollToNode(
hasTestTag(TestTags.Settings.Jellyseerr.JELLYSEERR_LOGIN_BUTTON),
)

onNodeWithTag(
TestTags.Settings.Jellyseerr.JELLYSEERR_LOGIN_BUTTON,
).assertIsNotEnabled()
Expand Down Expand Up @@ -225,8 +233,16 @@ class JellyseerrSettingsScreenTest : ComposeTest() {
TestTags.Settings.Jellyseerr.JELLYSEERR_LOGIN_BUTTON,
).assertIsNotEnabled()

onNodeWithTag(TestTags.Settings.Jellyseerr.INITIAL_CONTENT).performScrollToNode(
hasText("Jellyseerr"),
)

onNodeWithText("Jellyseerr").assertIsDisplayed().performClick()

onNodeWithTag(TestTags.Settings.Jellyseerr.INITIAL_CONTENT).performScrollToNode(
hasTestTag(TestTags.Settings.Jellyseerr.JELLYSEERR_LOGIN_BUTTON),
)

onNodeWithTag(
TestTags.Settings.Jellyseerr.JELLYSEERR_LOGIN_BUTTON,
).assertIsNotEnabled()
Expand Down Expand Up @@ -313,8 +329,16 @@ class JellyseerrSettingsScreenTest : ComposeTest() {
TestTags.Settings.Jellyseerr.JELLYSEERR_LOGIN_BUTTON,
).assertIsNotEnabled()

onNodeWithTag(TestTags.Settings.Jellyseerr.INITIAL_CONTENT).performScrollToNode(
hasText("Emby"),
)

onNodeWithText("Emby").assertIsDisplayed().performClick()

onNodeWithTag(TestTags.Settings.Jellyseerr.INITIAL_CONTENT).performScrollToNode(
hasTestTag(TestTags.Settings.Jellyseerr.JELLYSEERR_LOGIN_BUTTON),
)

onNodeWithTag(
TestTags.Settings.Jellyseerr.JELLYSEERR_LOGIN_BUTTON,
).assertIsNotEnabled()
Expand Down Expand Up @@ -465,8 +489,16 @@ class JellyseerrSettingsScreenTest : ComposeTest() {
TestTags.Settings.Jellyseerr.JELLYSEERR_LOGIN_BUTTON,
).assertIsNotEnabled()

onNodeWithTag(TestTags.Settings.Jellyseerr.INITIAL_CONTENT).performScrollToNode(
hasText("Emby"),
)

onNodeWithText("Emby").assertIsDisplayed().performClick()

onNodeWithTag(TestTags.Settings.Jellyseerr.INITIAL_CONTENT).performScrollToNode(
hasTestTag(TestTags.Settings.Jellyseerr.JELLYSEERR_LOGIN_BUTTON),
)

onNodeWithTag(
TestTags.Settings.Jellyseerr.JELLYSEERR_LOGIN_BUTTON,
).assertIsNotEnabled()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,13 @@
<string name="feature_settings_episodes_rating_preference">Episodes</string>
<string name="feature_settings_seasons_rating_preference">Seasons</string>

<string name="support_email">support@scenepeek.app</string>
<string name="seerr_support_email_subject">Trouble connecting to Seerr</string>
<string name="seerr_support_email_body">"Please specify your login method, server versions and attach the error logs to help identify the issue."\n\nSeerr version: (enter your Seerr server version)\nJellyfin version: (enter your Jellyfin version if applicable)\n%1$s</string>

<string name="seerr_login_info_placeholder_pt1">This is an experimental feature.\nIf you have trouble connecting, please get in contact at </string>
<string name="seerr_login_info_placeholder_pt2"> to troubleshoot the issue.</string>

<!-- preferences.xml -->
<string name="preferences__none">None</string>
<string name="preferences__account">Account</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,10 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.semantics.contentType
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.sp
import com.divinelink.core.designsystem.component.ScenePeekLazyColumn
import com.divinelink.core.designsystem.theme.AppTheme
import com.divinelink.core.designsystem.theme.dimensions
Expand All @@ -55,6 +53,7 @@ import com.divinelink.core.ui.PasswordOutlinedTextField
import com.divinelink.core.ui.Previews
import com.divinelink.core.ui.TestTags
import com.divinelink.feature.settings.app.account.jellyseerr.preview.JellyseerrLoginStatePreviewParameterProvider
import com.divinelink.feature.settings.app.account.jellyseerr.ui.EmailSupportField
import com.divinelink.feature.settings.resources.Res
import com.divinelink.feature.settings.resources.feature_settings_email
import com.divinelink.feature.settings.resources.feature_settings_jellyseerr_address_placeholder
Expand Down Expand Up @@ -131,6 +130,10 @@ fun JellyseerrLoginContent(
)
}

item {
EmailSupportField()
}

item {
Spacer(modifier = Modifier.height(MaterialTheme.dimensions.keyline_96))
}
Expand Down Expand Up @@ -210,8 +213,7 @@ private fun AuthMethodButton(
Text(
text = method.displayName,
color = textColor,
fontWeight = FontWeight.Medium,
fontSize = 14.sp,
style = MaterialTheme.typography.labelLarge,
textAlign = TextAlign.Center,
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.divinelink.feature.settings.app.account.jellyseerr.ui

import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Info
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.withStyle
import com.divinelink.core.designsystem.theme.dimensions
import com.divinelink.core.ui.TestTags
import com.divinelink.core.ui.composition.LocalIntentManager
import com.divinelink.core.ui.rememberConfigProvider
import com.divinelink.feature.settings.resources.Res
import com.divinelink.feature.settings.resources.seerr_login_info_placeholder_pt1
import com.divinelink.feature.settings.resources.seerr_login_info_placeholder_pt2
import com.divinelink.feature.settings.resources.seerr_support_email_body
import com.divinelink.feature.settings.resources.seerr_support_email_subject
import com.divinelink.feature.settings.resources.support_email
import org.jetbrains.compose.resources.stringResource

@Composable
fun EmailSupportField() {
val intentManager = LocalIntentManager.current
val configProvider = rememberConfigProvider()

val email = stringResource(Res.string.support_email)
val subject = stringResource(Res.string.seerr_support_email_subject)
val body = stringResource(
Res.string.seerr_support_email_body,
configProvider.versionData,
)

Row(
modifier = Modifier
.testTag(TestTags.Settings.Jellyseerr.EMAIL_SUPPORT)
.clip(MaterialTheme.shapes.medium)
.clickable {
intentManager.launchEmail(
email = email,
subject = subject,
body = body,
)
},
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.dimensions.keyline_4),
verticalAlignment = Alignment.CenterVertically,
) {
Icon(
imageVector = Icons.Outlined.Info,
contentDescription = null,
)
Text(
style = MaterialTheme.typography.labelMedium,
fontStyle = FontStyle.Italic,
textAlign = TextAlign.Center,
text = buildAnnotatedString {
append(stringResource(Res.string.seerr_login_info_placeholder_pt1))
withStyle(
style = SpanStyle(
color = MaterialTheme.colorScheme.primary,
textDecoration = TextDecoration.Underline,
),
) {
append(email)
}
append(stringResource(Res.string.seerr_login_info_placeholder_pt2))
},
)
}
}