add url preview settings

This commit is contained in:
greenart7c3
2023-07-07 06:39:28 -03:00
parent 38bb9f457d
commit 66952c9212
11 changed files with 98 additions and 46 deletions
@@ -72,6 +72,7 @@ private object PrefKeys {
const val AUTOMATICALLY_START_PLAYBACK = "automatically_start_playback" const val AUTOMATICALLY_START_PLAYBACK = "automatically_start_playback"
const val THEME = "theme" const val THEME = "theme"
const val PREFERRED_LANGUAGE = "preferred_Language" const val PREFERRED_LANGUAGE = "preferred_Language"
const val AUTOMATICALLY_LOAD_URL_PREVIEW = "automatically_load_url_preview"
val LAST_READ: (String) -> String = { route -> "last_read_route_$route" } val LAST_READ: (String) -> String = { route -> "last_read_route_$route" }
} }
@@ -255,6 +256,11 @@ object LocalPreferences {
} else { } else {
putBoolean(PrefKeys.AUTOMATICALLY_START_PLAYBACK, account.settings.automaticallyStartPlayback!!) putBoolean(PrefKeys.AUTOMATICALLY_START_PLAYBACK, account.settings.automaticallyStartPlayback!!)
} }
if (account.settings.automaticallyShowUrlPreview == null) {
remove(PrefKeys.AUTOMATICALLY_LOAD_URL_PREVIEW)
} else {
putBoolean(PrefKeys.AUTOMATICALLY_LOAD_URL_PREVIEW, account.settings.automaticallyShowUrlPreview!!)
}
putString(PrefKeys.PREFERRED_LANGUAGE, account.settings.preferredLanguage ?: "") putString(PrefKeys.PREFERRED_LANGUAGE, account.settings.preferredLanguage ?: "")
}.apply() }.apply()
} }
@@ -396,6 +402,11 @@ object LocalPreferences {
} else { } else {
null null
} }
settings.automaticallyShowUrlPreview = if (contains(PrefKeys.AUTOMATICALLY_LOAD_URL_PREVIEW)) {
getBoolean(PrefKeys.AUTOMATICALLY_LOAD_URL_PREVIEW, false)
} else {
null
}
settings.preferredLanguage = getString(PrefKeys.PREFERRED_LANGUAGE, "") settings.preferredLanguage = getString(PrefKeys.PREFERRED_LANGUAGE, "")
} }
@@ -86,9 +86,14 @@ class Account(
var userProfileCache: User? = null var userProfileCache: User? = null
fun updateGlobalSettings(automaticallyShowImages: Boolean?, automaticallyStartPlayback: Boolean?) { fun updateGlobalSettings(
automaticallyShowImages: Boolean?,
automaticallyStartPlayback: Boolean?,
automaticallyShowUrlPreview: Boolean?
) {
settings.automaticallyStartPlayback = automaticallyStartPlayback settings.automaticallyStartPlayback = automaticallyStartPlayback
settings.automaticallyShowImages = automaticallyShowImages settings.automaticallyShowImages = automaticallyShowImages
settings.automaticallyShowUrlPreview = automaticallyShowUrlPreview
live.invalidateData() live.invalidateData()
saveable.invalidateData() saveable.invalidateData()
} }
@@ -6,5 +6,6 @@ import androidx.compose.runtime.Stable
class Settings( class Settings(
var automaticallyShowImages: Boolean? = null, var automaticallyShowImages: Boolean? = null,
var automaticallyStartPlayback: Boolean? = null, var automaticallyStartPlayback: Boolean? = null,
var preferredLanguage: String? = null var preferredLanguage: String? = null,
var automaticallyShowUrlPreview: Boolean? = null
) )
@@ -332,7 +332,7 @@ fun NewPostView(onClose: () -> Unit, baseReplyTo: Note? = null, quote: Note? = n
} else if (videoExtensions.any { removedParamsFromUrl.endsWith(it) }) { } else if (videoExtensions.any { removedParamsFromUrl.endsWith(it) }) {
VideoView(myUrlPreview, automaticallyStartPlayback = remember { mutableStateOf(true) }) VideoView(myUrlPreview, automaticallyStartPlayback = remember { mutableStateOf(true) })
} else { } else {
UrlPreview(myUrlPreview, myUrlPreview) UrlPreview(myUrlPreview, myUrlPreview, remember { mutableStateOf(true) })
} }
} else if (startsWithNIP19Scheme(myUrlPreview)) { } else if (startsWithNIP19Scheme(myUrlPreview)) {
val bgColor = MaterialTheme.colors.background val bgColor = MaterialTheme.colors.background
@@ -348,7 +348,7 @@ fun NewPostView(onClose: () -> Unit, baseReplyTo: Note? = null, quote: Note? = n
nav nav
) )
} else if (noProtocolUrlValidator.matcher(myUrlPreview).matches()) { } else if (noProtocolUrlValidator.matcher(myUrlPreview).matches()) {
UrlPreview("https://$myUrlPreview", myUrlPreview) UrlPreview("https://$myUrlPreview", myUrlPreview, remember { mutableStateOf(true) })
} }
} }
} }
@@ -68,6 +68,16 @@ fun ExpandableRichTextViewer(
) )
} }
val automaticallyShowUrlPreview = remember {
mutableStateOf(
when (settings?.automaticallyShowUrlPreview) {
true -> !isMobile
false -> false
else -> true
}
)
}
var showFullText by remember { mutableStateOf(false) } var showFullText by remember { mutableStateOf(false) }
val whereToCut = remember(content) { val whereToCut = remember(content) {
@@ -98,6 +108,7 @@ fun ExpandableRichTextViewer(
accountViewModel, accountViewModel,
showImage, showImage,
automaticallyStartPlayback, automaticallyStartPlayback,
automaticallyShowUrlPreview,
nav nav
) )
@@ -110,13 +110,14 @@ fun RichTextViewer(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
showImage: MutableState<Boolean>, showImage: MutableState<Boolean>,
automaticallyStartPlayback: MutableState<Boolean>, automaticallyStartPlayback: MutableState<Boolean>,
automaticallyShowUrlPreview: MutableState<Boolean>,
nav: (String) -> Unit nav: (String) -> Unit
) { ) {
Column(modifier = modifier) { Column(modifier = modifier) {
if (remember(content) { isMarkdown(content) }) { if (remember(content) { isMarkdown(content) }) {
RenderContentAsMarkdown(content, tags, nav) RenderContentAsMarkdown(content, tags, nav)
} else { } else {
RenderRegular(content, tags, canPreview, backgroundColor, accountViewModel, showImage, automaticallyStartPlayback, nav) RenderRegular(content, tags, canPreview, backgroundColor, accountViewModel, showImage, automaticallyStartPlayback, automaticallyShowUrlPreview, nav)
} }
} }
} }
@@ -131,6 +132,7 @@ private fun RenderRegular(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
showImage: MutableState<Boolean>, showImage: MutableState<Boolean>,
automaticallyStartPlayback: MutableState<Boolean>, automaticallyStartPlayback: MutableState<Boolean>,
automaticallyShowUrlPreview: MutableState<Boolean>,
nav: (String) -> Unit nav: (String) -> Unit
) { ) {
val state by remember(content) { val state by remember(content) {
@@ -162,6 +164,7 @@ private fun RenderRegular(
accountViewModel, accountViewModel,
showImage, showImage,
automaticallyStartPlayback, automaticallyStartPlayback,
automaticallyShowUrlPreview,
nav nav
) )
} }
@@ -243,11 +246,12 @@ private fun RenderWordWithPreview(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
showImage: MutableState<Boolean>, showImage: MutableState<Boolean>,
automaticallyStartPlayback: MutableState<Boolean>, automaticallyStartPlayback: MutableState<Boolean>,
automaticallyShowUrlPreview: MutableState<Boolean>,
nav: (String) -> Unit nav: (String) -> Unit
) { ) {
when (word) { when (word) {
is ImageSegment -> ZoomableContentView(word.segmentText, state, showImage, automaticallyStartPlayback) is ImageSegment -> ZoomableContentView(word.segmentText, state, showImage, automaticallyStartPlayback)
is LinkSegment -> UrlPreview(word.segmentText, word.segmentText) is LinkSegment -> UrlPreview(word.segmentText, word.segmentText, automaticallyShowUrlPreview)
is EmojiSegment -> RenderCustomEmoji(word.segmentText, state) is EmojiSegment -> RenderCustomEmoji(word.segmentText, state)
is InvoiceSegment -> MayBeInvoicePreview(word.segmentText) is InvoiceSegment -> MayBeInvoicePreview(word.segmentText)
is WithdrawSegment -> MayBeWithdrawal(word.segmentText) is WithdrawSegment -> MayBeWithdrawal(word.segmentText)
@@ -4,6 +4,7 @@ import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@@ -13,33 +14,41 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@Composable @Composable
fun UrlPreview(url: String, urlText: String) { fun UrlPreview(url: String, urlText: String, automaticallyShowUrlPreview: MutableState<Boolean>) {
var urlPreviewState by remember(url) { if (!automaticallyShowUrlPreview.value) {
mutableStateOf( ClickableUrl(urlText, url)
UrlCachedPreviewer.cache.get(url)?.let { it } ?: UrlPreviewState.Loading } else {
) var urlPreviewState by remember(url) {
} mutableStateOf(
UrlCachedPreviewer.cache.get(url)?.let { it } ?: UrlPreviewState.Loading
)
}
// Doesn't use a viewModel because of viewModel reusing issues (too many UrlPreview are created). // Doesn't use a viewModel because of viewModel reusing issues (too many UrlPreview are created).
if (urlPreviewState == UrlPreviewState.Loading) { if (urlPreviewState == UrlPreviewState.Loading) {
LaunchedEffect(url) { LaunchedEffect(url) {
launch(Dispatchers.IO) { launch(Dispatchers.IO) {
UrlCachedPreviewer.previewInfo(url) { UrlCachedPreviewer.previewInfo(url) {
launch(Dispatchers.Main) { launch(Dispatchers.Main) {
urlPreviewState = it urlPreviewState = it
}
} }
} }
} }
} }
}
Crossfade(targetState = urlPreviewState, animationSpec = tween(durationMillis = 100)) { state -> Crossfade(
when (state) { targetState = urlPreviewState,
is UrlPreviewState.Loaded -> { animationSpec = tween(durationMillis = 100)
UrlPreviewCard(url, state.previewInfo) ) { state ->
} when (state) {
else -> { is UrlPreviewState.Loaded -> {
ClickableUrl(urlText, url) UrlPreviewCard(url, state.previewInfo, automaticallyShowUrlPreview)
}
else -> {
ClickableUrl(urlText, url)
}
} }
} }
} }
@@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
@@ -26,8 +27,12 @@ import com.vitorpamplona.amethyst.ui.theme.subtleBorder
@Composable @Composable
fun UrlPreviewCard( fun UrlPreviewCard(
url: String, url: String,
previewInfo: UrlInfoItem previewInfo: UrlInfoItem,
automaticallyShowUrlPreview: MutableState<Boolean>
) { ) {
if (!automaticallyShowUrlPreview.value) {
return ClickableUrl(url, url)
}
val uri = LocalUriHandler.current val uri = LocalUriHandler.current
Row( Row(
@@ -49,8 +49,12 @@ class AccountViewModel(val account: Account, private val themeViewModel: ThemeVi
return themeViewModel.theme.value ?: "System" return themeViewModel.theme.value ?: "System"
} }
fun updateGlobalSettings(automaticallyShowImages: Boolean?, automaticallyStartPlayback: Boolean?) { fun updateGlobalSettings(
account.updateGlobalSettings(automaticallyShowImages, automaticallyStartPlayback) automaticallyShowImages: Boolean?,
automaticallyStartPlayback: Boolean?,
automaticallyShowUrlPreview: Boolean?
) {
account.updateGlobalSettings(automaticallyShowImages, automaticallyStartPlayback, automaticallyShowUrlPreview)
} }
fun isWriteable(): Boolean { fun isWriteable(): Boolean {
@@ -57,7 +57,6 @@ import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.NostrUserProfileDataSource import com.vitorpamplona.amethyst.service.NostrUserProfileDataSource
import com.vitorpamplona.amethyst.service.connectivitystatus.ConnectivityStatus
import com.vitorpamplona.amethyst.service.model.AppDefinitionEvent import com.vitorpamplona.amethyst.service.model.AppDefinitionEvent
import com.vitorpamplona.amethyst.service.model.BadgeDefinitionEvent import com.vitorpamplona.amethyst.service.model.BadgeDefinitionEvent
import com.vitorpamplona.amethyst.service.model.BadgeProfilesEvent import com.vitorpamplona.amethyst.service.model.BadgeProfilesEvent
@@ -599,20 +598,6 @@ private fun ProfileHeader(
var popupExpanded by remember { mutableStateOf(false) } var popupExpanded by remember { mutableStateOf(false) }
var zoomImageDialogOpen by remember { mutableStateOf(false) } var zoomImageDialogOpen by remember { mutableStateOf(false) }
val accountState by accountViewModel.accountLiveData.observeAsState()
val settings = accountState?.account?.settings
val isMobile = ConnectivityStatus.isOnMobileData.value
val showImage = remember {
mutableStateOf(
when (settings?.automaticallyShowImages) {
true -> !isMobile
false -> false
else -> true
}
)
}
Box { Box {
DrawBanner(baseUser) DrawBanner(baseUser)
@@ -116,6 +116,12 @@ fun SettingsScreen(
val selectedVideoItem = remember { val selectedVideoItem = remember {
mutableStateOf(selectedItens[videoIndex]) mutableStateOf(selectedItens[videoIndex])
} }
val linkIndex = if (settings.automaticallyShowUrlPreview == null) { 0 } else {
if (settings.automaticallyShowUrlPreview == true) 1 else 2
}
val selectedLinkItem = remember {
mutableStateOf(selectedItens[linkIndex])
}
val themeItens = arrayOf("System", "Light", "Dark") val themeItens = arrayOf("System", "Light", "Dark")
val themeIndex = themeItens.indexOf(accountViewModel.currentTheme()) val themeIndex = themeItens.indexOf(accountViewModel.currentTheme())
@@ -216,6 +222,12 @@ fun SettingsScreen(
title = "Automatically play videos" title = "Automatically play videos"
) )
DropDownSettings(
selectedItem = selectedLinkItem,
listItems = selectedItens,
title = "Automatically show url preview"
)
Row( Row(
Modifier.fillMaxWidth(), Modifier.fillMaxWidth(),
Arrangement.Center Arrangement.Center
@@ -232,10 +244,15 @@ fun SettingsScreen(
2 -> false 2 -> false
else -> null else -> null
} }
val automaticallyShowUrlPreview = when (selectedItens.indexOf(selectedLinkItem.value)) {
1 -> true
2 -> false
else -> null
}
accountViewModel.changeTheme(selectedTheme.value) accountViewModel.changeTheme(selectedTheme.value)
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
accountViewModel.updateGlobalSettings(automaticallyShowImages, automaticallyStartPlayback) accountViewModel.updateGlobalSettings(automaticallyShowImages, automaticallyStartPlayback, automaticallyShowUrlPreview)
LocalPreferences.saveToEncryptedStorage(accountViewModel.account) LocalPreferences.saveToEncryptedStorage(accountViewModel.account)
LocalPreferences.updateTheme(selectedTheme.value) LocalPreferences.updateTheme(selectedTheme.value)
ServiceManager.pause() ServiceManager.pause()