Skip to content

Commit

Permalink
feat: add option to open links in internal web view (#482)
Browse files Browse the repository at this point in the history
* refactor: cleanup ImageDetailScreen

* update build scripts

* implement WebViewScreen

* add method to DetailOpener

* add new UrlOpeningMode

* add management in CustomUriHandler

* add option in settings

* update l10n
  • Loading branch information
AkesiSeli authored Nov 11, 2024
1 parent da75cad commit 88eb352
Show file tree
Hide file tree
Showing 20 changed files with 246 additions and 99 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.livefast.eattrash.raccoonforfriendica.navigation

import com.livefast.eattrash.feature.userdetail.classic.UserDetailScreen
import com.livefast.eattrash.feature.userdetail.forum.ForumListScreen
import com.livefast.eattrash.raccoonforfriendica.core.commonui.content.WebViewScreen
import com.livefast.eattrash.raccoonforfriendica.core.navigation.DetailOpener
import com.livefast.eattrash.raccoonforfriendica.core.navigation.NavigationCoordinator
import com.livefast.eattrash.raccoonforfriendica.domain.content.data.EventModel
Expand Down Expand Up @@ -380,4 +381,9 @@ class DefaultDetailOpener(
val screen = LicencesScreen()
navigationCoordinator.push(screen)
}

override fun openInternalWebView(url: String) {
val screen = WebViewScreen(url)
navigationCoordinator.push(screen)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.livefast.eattrash.raccoonforfriendica.navigation

import com.livefast.eattrash.feature.userdetail.classic.UserDetailScreen
import com.livefast.eattrash.feature.userdetail.forum.ForumListScreen
import com.livefast.eattrash.raccoonforfriendica.core.commonui.content.WebViewScreen
import com.livefast.eattrash.raccoonforfriendica.core.navigation.NavigationCoordinator
import com.livefast.eattrash.raccoonforfriendica.domain.content.data.EventModel
import com.livefast.eattrash.raccoonforfriendica.domain.content.data.TimelineEntryModel
Expand Down Expand Up @@ -463,4 +464,13 @@ class DefaultDetailOpenerTest {
navigationCoordinator.push(any<LicencesScreen>())
}
}

@Test
fun `when openInternalWebView then interactions are as expected`() {
sut.openInternalWebView("https://www.google.com")

verify {
navigationCoordinator.push(any<WebViewScreen>())
}
}
}
3 changes: 3 additions & 0 deletions core/commonui/content/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,15 @@ kotlin {
implementation(compose.material3)
implementation(compose.materialIconsExtended)

implementation(libs.calf)
implementation(libs.koin.core)
implementation(libs.voyager.navigator)

implementation(projects.core.appearance)
implementation(projects.core.commonui.components)
implementation(projects.core.htmlparse)
implementation(projects.core.l10n)
implementation(projects.core.navigation)
implementation(projects.core.utils)

implementation(projects.domain.content.data)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package com.livefast.eattrash.raccoonforfriendica.core.commonui.content

import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow
import cafe.adriel.voyager.core.screen.Screen
import com.livefast.eattrash.raccoonforfriendica.core.navigation.di.getDrawerCoordinator
import com.livefast.eattrash.raccoonforfriendica.core.navigation.di.getNavigationCoordinator
import com.mohamedrejeb.calf.ui.web.WebView
import com.mohamedrejeb.calf.ui.web.rememberWebViewState

class WebViewScreen(
private val url: String,
) : Screen {
@OptIn(ExperimentalMaterial3Api::class)
@Composable
override fun Content() {
val navigationCoordinator = remember { getNavigationCoordinator() }
val drawerCoordinator = remember { getDrawerCoordinator() }
val state =
rememberWebViewState(
url = url,
)

LaunchedEffect(Unit) {
state.settings.javaScriptEnabled = true
state.settings.androidSettings.supportZoom = true
}
LaunchedEffect(drawerCoordinator) {
drawerCoordinator.setGesturesEnabled(false)
}
DisposableEffect(drawerCoordinator) {
onDispose {
drawerCoordinator.setGesturesEnabled(true)
}
}

Scaffold(
topBar = {
TopAppBar(
title = {
Text(
text = url,
style = MaterialTheme.typography.titleMedium,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
},
navigationIcon = {
if (navigationCoordinator.canPop.value) {
IconButton(
onClick = {
navigationCoordinator.pop()
},
) {
Icon(
imageVector = Icons.AutoMirrored.Default.ArrowBack,
contentDescription = null,
)
}
}
},
)
},
) { padding ->
WebView(
modifier = Modifier.padding(top = padding.calculateTopPadding()).fillMaxSize(),
state = state,
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,9 @@ internal val DeStrings =
override val fontScaleLarger = "Größer"
override val fontScaleLargest = "Größte"
override val settingsItemUrlOpeningMode = "URL-Öffnungsmodus"
override val urlOpeningModeExternal = "Extern"
override val urlOpeningModeExternal = "Externer Browser"
override val urlOpeningModeCustomTabs = "Benutzerdefinierte Registerkarten"
override val urlOpeningModeInternal = "Interne Webansicht"
override val dialogErrorTitle = "Ups…"
override val messagePollVoteErrorBody =
"Leider bin ich nur ein mobiler Entwickler und kann keine fehlenden Backend-Methoden hinzufügen!\nSchauen Sie sich dieses Problem an und setzen Sie ein 👍, damit die Entwickler wissen, dass es sich lohnen könnte, es zu implementieren."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,8 +203,9 @@ internal open class DefaultStrings : Strings {
override val fontScaleLarger = "Larger"
override val fontScaleLargest = "Largest"
override val settingsItemUrlOpeningMode = "URL opening mode"
override val urlOpeningModeExternal = "External"
override val urlOpeningModeExternal = "External browser"
override val urlOpeningModeCustomTabs = "Custom tabs"
override val urlOpeningModeInternal = "Internal web view"
override val dialogErrorTitle = "Oops…"
override val messagePollVoteErrorBody =
"Unfortunately, I'm just a mobile dev and I can't add missing back-end methods!\nCheck out this issue and put a 👍 so that the devs know it may be worth implementing it."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,9 @@ internal val EsStrings =
override val fontScaleLarger = "Más grande"
override val fontScaleLargest = "Grandísimo"
override val settingsItemUrlOpeningMode = "Modo de apertura de URL"
override val urlOpeningModeExternal = "Externo"
override val urlOpeningModeExternal = "Navegador externo"
override val urlOpeningModeCustomTabs = "Pestañas personalizadas"
override val urlOpeningModeInternal = "Vista web interna"
override val dialogErrorTitle = "Ups…"
override val messagePollVoteErrorBody =
"Por desgracia, sólo soy un desarrollador móvil y no puedo añadir los métodos back-end que faltan."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,8 +203,9 @@ internal val FrStrings =
override val fontScaleLarger = "Plus grand"
override val fontScaleLargest = "Le plus grand"
override val settingsItemUrlOpeningMode = "Mode d'ouverture de l'URL"
override val urlOpeningModeExternal = "Externe"
override val urlOpeningModeExternal = "Navigateur externe"
override val urlOpeningModeCustomTabs = "Onglets personnalisés"
override val urlOpeningModeInternal = "Vue interne du web"
override val dialogErrorTitle = "Oups..."
override val messagePollVoteErrorBody =
"Malheureusement, je ne suis qu'un développeur mobile et je ne peux pas ajouter les méthodes back-end manquantes !\nJetez un œil à ce problème et mettez un 👍 pour que les développeurs sachent que cela peut valoir la peine de l'implémenter."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ internal val ItStrings =
override val fontScaleLargest = "Grandissimo"
override val settingsItemUrlOpeningMode = "Modalità apertura URL"
override val urlOpeningModeExternal = "Browser esterno"
override val urlOpeningModeInternal = "Web view interna"
override val urlOpeningModeCustomTabs = "Schede personalizzate"
override val dialogErrorTitle = "Ops…"
override val messagePollVoteErrorBody =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,9 @@ internal val PlStrings =
override val fontScaleLarger = "Większa"
override val fontScaleLargest = "Największa"
override val settingsItemUrlOpeningMode = "Tryb otwierania adresu URL"
override val urlOpeningModeExternal = "Zewnętrzny"
override val urlOpeningModeExternal = "Zewnętrzna przeglądarka"
override val urlOpeningModeCustomTabs = "Zakładki niestandardowe"
override val urlOpeningModeInternal = "Wewnętrzny widok sieciowy"
override val dialogErrorTitle = "Ups…"
override val messagePollVoteErrorBody =
"Niestety, jestem tylko deweloperem mobilnym i nie mogę dodać brakujących metod back-endowych!\nSprawdź tę kwestię i umieść 👍, aby deweloperzy wiedzieli, że warto ją wdrożyć."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,9 @@ internal val PtStrings =
override val fontScaleLarger = "Maior"
override val fontScaleLargest = "Maior"
override val settingsItemUrlOpeningMode = "Modo de abertura do URL"
override val urlOpeningModeExternal = "Externo"
override val urlOpeningModeCustomTabs = "Separadores personalizados"
override val urlOpeningModeExternal = "Navegador externo"
override val urlOpeningModeCustomTabs = "Guias personalizadas"
override val urlOpeningModeInternal = "Vista interna da Web"
override val dialogErrorTitle = "Oops…"
override val messagePollVoteErrorBody =
"Infelizmente, sou apenas um programador móvel e não posso adicionar métodos back-end em falta!"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ interface Strings {
val settingsItemUrlOpeningMode: String
val urlOpeningModeExternal: String
val urlOpeningModeCustomTabs: String
val urlOpeningModeInternal: String
val dialogErrorTitle: String
val messagePollVoteErrorBody: String
val buttonPollErrorOpenIssue: String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,6 @@ interface DetailOpener {
fun openEvent(event: EventModel)

fun openLicences()

fun openInternalWebView(url: String)
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,32 @@ import androidx.compose.runtime.Composable
import com.livefast.eattrash.raccoonforfriendica.core.l10n.messages.LocalStrings

sealed interface UrlOpeningMode {
data object Internal : UrlOpeningMode

data object External : UrlOpeningMode

data object CustomTabs : UrlOpeningMode
}

fun Int.toUrlOpeningMode(): UrlOpeningMode =
when (this) {
2 -> UrlOpeningMode.Internal
1 -> UrlOpeningMode.CustomTabs
else -> UrlOpeningMode.External
}

fun UrlOpeningMode.toInt(): Int =
when (this) {
UrlOpeningMode.Internal -> 2
UrlOpeningMode.CustomTabs -> 1
UrlOpeningMode.External -> 0
}

@Composable
fun UrlOpeningMode.toReadableName(): String =
when (this) {
// TODO: l10n
UrlOpeningMode.Internal -> LocalStrings.current.urlOpeningModeInternal
UrlOpeningMode.CustomTabs -> LocalStrings.current.urlOpeningModeCustomTabs
UrlOpeningMode.External -> LocalStrings.current.urlOpeningModeExternal
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.livefast.eattrash.raccoonforfriendica.domain.urlhandler

import androidx.compose.ui.platform.UriHandler
import com.livefast.eattrash.raccoonforfriendica.core.navigation.DetailOpener
import com.livefast.eattrash.raccoonforfriendica.core.utils.url.CustomTabsHelper
import com.livefast.eattrash.raccoonforfriendica.domain.identity.data.UrlOpeningMode
import com.livefast.eattrash.raccoonforfriendica.domain.identity.repository.SettingsRepository
Expand Down Expand Up @@ -31,6 +32,7 @@ internal class DefaultCustomUriHandler(
private val guppeProcessor: GuppeProcessor,
private val peertubeProcessor: PeertubeProcessor,
private val pixelfedProcessor: PixelfedProcessor,
private val detailOpener: DetailOpener,
dispatcher: CoroutineDispatcher = Dispatchers.IO,
) : CustomUriHandler {
private val scope = CoroutineScope(SupervisorJob() + dispatcher)
Expand Down Expand Up @@ -63,6 +65,9 @@ internal class DefaultCustomUriHandler(
mode: UrlOpeningMode,
) {
when {
mode == UrlOpeningMode.Internal ->
detailOpener.openInternalWebView(url)

customTabsHelper.isSupported && mode == UrlOpeningMode.CustomTabs ->
customTabsHelper.handle(url)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ val domainUrlHandlerModule =
guppeProcessor = get(),
peertubeProcessor = get(),
pixelfedProcessor = get(),
detailOpener = get(),
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ class DefaultCustomUriHandlerTest {
guppeProcessor = guppeProcessor,
peertubeProcessor = peertubeProcessor,
pixelfedProcessor = pixelfedProcessor,
detailOpener = detailOpener,
dispatcher = UnconfinedTestDispatcher(),
)

Expand Down Expand Up @@ -129,6 +130,20 @@ class DefaultCustomUriHandlerTest {
}
}

@Test
fun `given URL and internal mode when openUri then interactions are as expected`() {
every {
settingsRepository.current
} returns MutableStateFlow(SettingsModel(urlOpeningMode = UrlOpeningMode.Internal))
val url = "https://www.example.com"

sut.openUri(url)

verify {
detailOpener.openInternalWebView(url)
}
}

@Test
fun `given hashtag URL when openUri then interactions are as expected`() {
every {
Expand Down
Loading

0 comments on commit 88eb352

Please sign in to comment.