Skip to content

Commit

Permalink
Add preference to control following redirects
Browse files Browse the repository at this point in the history
- Add new preference to model
- Add new preference to `UserSharedPreferences`
- Add new preference to `UserRepository`/`UserDataSource`
- Update `UserPreferencesScreen` and `UserPreferencesViewModel` to show and edit the new preference value
- Update `GetUrlPreview` to run if either `autoFillDescription` or `followRedirects` is true
- Update `ShareReceiverViewModel` to wait until `getUrlPreview` before checking if the URL exists, otherwise it could check for the wrong URL
  • Loading branch information
fibelatti committed Sep 26, 2024
1 parent 9503e03 commit 1af07a6
Show file tree
Hide file tree
Showing 13 changed files with 129 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ const val KEY_ALWAYS_USE_SIDE_PANEL = "ALWAYS_USE_SIDE_PANEL"
@VisibleForTesting
const val KEY_MARK_AS_READ_ON_OPEN = "MARK_AS_READ_ON_OPEN"

@VisibleForTesting
const val KEY_FOLLOW_REDIRECTS = "FOLLOW_REDIRECTS"

@VisibleForTesting
const val KEY_AUTO_FILL_DESCRIPTION = "AUTO_FILL_DESCRIPTION"

Expand Down Expand Up @@ -112,6 +115,10 @@ class UserSharedPreferences @Inject constructor(private val sharedPreferences: S
get() = sharedPreferences.get(KEY_MARK_AS_READ_ON_OPEN, false)
set(value) = sharedPreferences.put(KEY_MARK_AS_READ_ON_OPEN, value)

var followRedirects: Boolean
get() = sharedPreferences.get(KEY_FOLLOW_REDIRECTS, true)
set(value) = sharedPreferences.put(KEY_FOLLOW_REDIRECTS, value)

var autoFillDescription: Boolean
get() = sharedPreferences.get(KEY_AUTO_FILL_DESCRIPTION, false)
set(value) = sharedPreferences.put(KEY_AUTO_FILL_DESCRIPTION, value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ class GetUrlPreview @Inject constructor(
) : UseCaseWithParams<GetUrlPreview.Params, Result<UrlPreview>> {

override suspend operator fun invoke(params: Params): Result<UrlPreview> = catching {
if (userRepository.autoFillDescription) {
fetchTitleAndDescription(params)
if (userRepository.autoFillDescription || userRepository.followRedirects) {
loadUrl(params)
} else {
createUrlPreview(params)
}
Expand All @@ -36,7 +36,7 @@ class GetUrlPreview @Inject constructor(
description = params.highlightedText,
)

private suspend fun fetchTitleAndDescription(params: Params): UrlPreview {
private suspend fun loadUrl(params: Params): UrlPreview {
val request = Request.Builder()
.url(params.url)
.apply {
Expand All @@ -50,18 +50,27 @@ class GetUrlPreview @Inject constructor(
}
.build()

val (url, document) = withContext(Dispatchers.IO) {
val previewUrl: String
val document: Document

withContext(Dispatchers.IO) {
okHttpClient.newCall(request).execute().use { response ->
response.request.url.toString() to Jsoup.parse(requireNotNull(response.body).string())
previewUrl = if (userRepository.followRedirects) response.request.url.toString() else params.url
document = Jsoup.parse(requireNotNull(response.body).string())
}
}

val title = document.getMetaProperty(property = "og:title")
?: document.title().ifBlank { params.title.ifNullOrBlank { url } }
val description = params.highlightedText
?: document.getMetaProperty(property = "og:description")
val previewTitle = (document.getMetaProperty(property = "og:title") ?: document.title())
.takeIf { userRepository.autoFillDescription }
.ifNullOrBlank { params.title.ifNullOrBlank { previewUrl } }
val previewDescription = params.highlightedText
?: document.getMetaProperty(property = "og:description").takeIf { userRepository.autoFillDescription }

return UrlPreview(url, title, description)
return UrlPreview(
url = previewUrl,
title = previewTitle,
description = previewDescription,
)
}

private fun Document.getMetaProperty(property: String): String? = select("meta[property=$property]")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,11 @@ import com.fibelatti.pinboard.features.posts.domain.usecase.GetUrlPreview
import com.fibelatti.pinboard.features.posts.domain.usecase.UrlPreview
import com.fibelatti.pinboard.features.user.domain.UserRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.async
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class ShareReceiverViewModel @Inject constructor(
Expand All @@ -45,19 +44,16 @@ class ShareReceiverViewModel @Inject constructor(
fun saveUrl(url: String, title: String?, skipEdit: Boolean = false) {
launch {
extractUrl(url).mapCatching { (extractedUrl, highlightedText) ->
val urlPreview = async {
getUrlPreview(
GetUrlPreview.Params(
url = extractedUrl,
title = title,
highlightedText = highlightedText,
),
)
}
val existingPost = async { postsRepository.getPost(id = "", url = extractedUrl) }
val preview = getUrlPreview(
GetUrlPreview.Params(
url = extractedUrl,
title = title,
highlightedText = highlightedText,
),
).getOrThrow()

val existingPost = postsRepository.getPost(id = "", url = preview.url).getOrNull()

urlPreview.await().getOrThrow() to existingPost.await().getOrNull()
}.onSuccess { (urlPreview, existingPost) ->
when {
existingPost != null && skipEdit -> {
_screenState.emitLoaded(SharingResult.Saved(message = R.string.posts_existing_feedback))
Expand All @@ -69,10 +65,10 @@ class ShareReceiverViewModel @Inject constructor(
}

skipEdit || userRepository.editAfterSharing is EditAfterSharing.AfterSaving -> {
addBookmark(urlPreview = urlPreview, skipEdit = skipEdit)
addBookmark(urlPreview = preview, skipEdit = skipEdit)
}

else -> editBookmark(urlPreview = urlPreview)
else -> editBookmark(urlPreview = preview)
}
}.onFailure(_screenState::emitError)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,13 @@ class UserDataSource @Inject constructor(
updateCurrentPreferences()
}

override var followRedirects: Boolean
get() = userSharedPreferences.followRedirects
set(value) {
userSharedPreferences.followRedirects = value
updateCurrentPreferences()
}

override var autoFillDescription: Boolean
get() = userSharedPreferences.autoFillDescription
set(value) {
Expand Down Expand Up @@ -188,6 +195,7 @@ class UserDataSource @Inject constructor(
preferredDateFormat = preferredDateFormat,
preferredDetailsView = preferredDetailsView,
alwaysUseSidePanel = alwaysUseSidePanel,
followRedirects = followRedirects,
autoFillDescription = autoFillDescription,
showDescriptionInLists = showDescriptionInLists,
defaultPrivate = defaultPrivate ?: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ data class UserPreferences(
val preferredDateFormat: PreferredDateFormat,
val preferredDetailsView: PreferredDetailsView,
val alwaysUseSidePanel: Boolean,
val followRedirects: Boolean,
val autoFillDescription: Boolean,
val showDescriptionInLists: Boolean,
val defaultPrivate: Boolean,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ interface UserRepository {

var markAsReadOnOpen: Boolean

var followRedirects: Boolean

var autoFillDescription: Boolean

var showDescriptionInLists: Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class UserPreferencesProvider : PreviewParameterProvider<UserPreferences> {
preferredDateFormat = PreferredDateFormat.YearMonthDayWithTime,
preferredDetailsView = PreferredDetailsView.Edit,
alwaysUseSidePanel = true,
followRedirects = true,
autoFillDescription = true,
showDescriptionInLists = true,
defaultPrivate = false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,7 @@ private fun BookmarkingPreferencesContent(
appMode = appMode,
userPreferences = userPreferences,
onEditAfterSharingChange = userPreferencesViewModel::saveEditAfterSharing,
onFollowRedirectsChange = userPreferencesViewModel::saveFollowRedirects,
onAutoFillDescriptionChange = userPreferencesViewModel::saveAutoFillDescription,
onPrivateByDefaultChange = userPreferencesViewModel::saveDefaultPrivate,
onReadLaterByDefaultChange = userPreferencesViewModel::saveDefaultReadLater,
Expand Down Expand Up @@ -473,6 +474,7 @@ private fun BookmarkingPreferencesContent(
appMode: AppMode,
userPreferences: UserPreferences,
onEditAfterSharingChange: (EditAfterSharing) -> Unit,
onFollowRedirectsChange: (Boolean) -> Unit,
onAutoFillDescriptionChange: (Boolean) -> Unit,
onPrivateByDefaultChange: (Boolean) -> Unit,
onReadLaterByDefaultChange: (Boolean) -> Unit,
Expand Down Expand Up @@ -521,6 +523,14 @@ private fun BookmarkingPreferencesContent(
color = MaterialTheme.colorScheme.onSurfaceVariant,
)

SettingToggle(
title = stringResource(id = R.string.user_preferences_follow_redirects),
description = stringResource(id = R.string.user_preferences_follow_redirects_description),
checked = userPreferences.followRedirects,
onCheckedChange = onFollowRedirectsChange,
modifier = Modifier.padding(top = 16.dp),
)

SettingToggle(
title = stringResource(id = R.string.user_preferences_description_auto_fill),
description = stringResource(id = R.string.user_preferences_description_auto_fill_description),
Expand Down Expand Up @@ -623,6 +633,7 @@ private fun BookmarkingPreferencesContentPreview(
appMode = AppMode.PINBOARD,
userPreferences = userPreferences,
onEditAfterSharingChange = {},
onFollowRedirectsChange = {},
onAutoFillDescriptionChange = {},
onPrivateByDefaultChange = {},
onReadLaterByDefaultChange = {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ class UserPreferencesViewModel @Inject constructor(
userRepository.markAsReadOnOpen = value
}

fun saveFollowRedirects(value: Boolean) {
userRepository.followRedirects = value
}

fun saveAutoFillDescription(value: Boolean) {
userRepository.autoFillDescription = value
}
Expand Down
6 changes: 4 additions & 2 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@
<string name="user_preferences_appearance_light">Light theme</string>
<string name="user_preferences_appearance_system_default">Follow system</string>
<string name="user_preferences_dynamic_colors">Use dynamic colors</string>
<string name="user_preferences_dynamic_colors_caveat">Change the colors of the app based on your wallpaper</string>
<string name="user_preferences_dynamic_colors_caveat">Change the colors of the app, matching your device style</string>
<string name="user_preferences_disable_screenshots">Disable screenshots and recording</string>
<string name="user_preferences_disable_screenshots_caveat">Prevent app contents from being displayed on screenshots and screen recordings</string>
<string name="user_preferences_date_format">Preferred date format</string>
Expand All @@ -241,8 +241,10 @@
<string name="user_preferences_preferred_details_view_caveat">Long press list items when browsing bookmarks to access share and edit shortcuts</string>
<string name="user_preferences_preferred_details_view_mark_as_read_on_open">Mark as read on open</string>
<string name="user_preferences_preferred_details_view_mark_as_read_on_open_caveat">Automatically update the bookmark to remove the read later flag</string>
<string name="user_preferences_follow_redirects">Follow redirects</string>
<string name="user_preferences_follow_redirects_description">Automatically follow redirects when sharing a bookmark to the app, saving the final URL. Increases the loading time when saving a bookmark via the share action</string>
<string name="user_preferences_description_auto_fill">Auto-fill title and description</string>
<string name="user_preferences_description_auto_fill_description">Automatically get the details from the page when sharing its URL to Pinkt. May increase the loading time when saving a bookmark via the share action</string>
<string name="user_preferences_description_auto_fill_description">Automatically get the page details when sharing a bookmark to the app. Increases the loading time when saving a bookmark via the share action</string>
<string name="user_preferences_description_visible_in_lists">Show description in lists</string>
<string name="user_preferences_description_visible_in_lists_description">Show the first 5 lines of the description when browsing bookmarks</string>
<string name="user_preferences_always_use_side_panel">Use side panel when available</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,26 @@ internal class UserSharedPreferencesTest {
assertThat(userSharedPreferences.markAsReadOnOpen).isFalse()
}

@Test
fun `WHEN followRedirects getter is called THEN its value is returned`() {
// GIVEN
val value = randomBoolean()
every { mockSharedPreferences.get(KEY_FOLLOW_REDIRECTS, true) } returns value

// THEN
assertThat(userSharedPreferences.followRedirects).isEqualTo(value)
}

@Test
fun `WHEN followRedirects setter is called THEN KEY_FOLLOW_REDIRECTS is set`() {
// WHEN
val value = randomBoolean()
userSharedPreferences.followRedirects = value

// THEN
verify { mockEditor.putBoolean(KEY_FOLLOW_REDIRECTS, value) }
}

@Test
fun `WHEN getDescriptionAutoFill is called THEN its value is returned`() {
// GIVEN
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ internal class UserDataSourceTest {
every { preferredDateFormat } returns ""
every { preferredDetailsView } returns ""
every { alwaysUseSidePanel } returns false
every { followRedirects } returns true
every { autoFillDescription } returns false
every { showDescriptionInLists } returns false
every { defaultPrivate } returns null
Expand All @@ -51,6 +52,7 @@ internal class UserDataSourceTest {
preferredDateFormat = PreferredDateFormat.DayMonthYearWithTime,
preferredDetailsView = PreferredDetailsView.InAppBrowser(markAsReadOnOpen = false),
alwaysUseSidePanel = false,
followRedirects = true,
autoFillDescription = false,
showDescriptionInLists = false,
defaultPrivate = false,
Expand Down Expand Up @@ -468,6 +470,32 @@ internal class UserDataSourceTest {
}
}

@Nested
inner class FollowRedirects {

@Test
fun `WHEN followRedirects getter is called THEN UserSharedPreferences is returned`() {
// GIVEN
val value = randomBoolean()
every { mockUserSharedPreferences.followRedirects } returns value

// THEN
assertThat(userDataSource.followRedirects).isEqualTo(value)
}

@Test
fun `WHEN followRedirects setter is called THEN UserSharedPreferences is set`() = runTest {
// GIVEN
val value = randomBoolean()

// WHEN
userDataSource.followRedirects = value

// THEN
verify { mockUserSharedPreferences.followRedirects = value }
}
}

@Nested
inner class AutoFill {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,18 @@ internal class UserPreferencesViewModelTest : BaseViewModelTest() {
verify { mockUserRepository.markAsReadOnOpen = value }
}

@Test
fun `WHEN saveFollowRedirects is called THEN repository is updated`() {
// GIVEN
val value = randomBoolean()

// WHEN
userPreferencesViewModel.saveFollowRedirects(value)

// THEN
verify { mockUserRepository.followRedirects = value }
}

@Test
fun `WHEN saveAutoFillDescription is called THEN repository is updated`() {
// GIVEN
Expand Down

0 comments on commit 1af07a6

Please sign in to comment.