Skip to content

Open items in the browser by default #917

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 20, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
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 @@ -2,7 +2,7 @@
"formatVersion": 1,
"database": {
"version": 5,
"identityHash": "2b86f20200ed2c56f5ae8d0565cf0f26",
"identityHash": "dd4d3c434aed697c688aa692d4778b63",
"entities": [
{
"tableName": "account",
Expand Down Expand Up @@ -99,7 +99,7 @@
},
{
"tableName": "feed",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `icon` TEXT, `url` TEXT NOT NULL, `groupId` TEXT NOT NULL, `accountId` INTEGER NOT NULL, `isNotification` INTEGER NOT NULL, `isFullContent` INTEGER NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`groupId`) REFERENCES `group`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `icon` TEXT, `url` TEXT NOT NULL, `groupId` TEXT NOT NULL, `accountId` INTEGER NOT NULL, `isNotification` INTEGER NOT NULL, `isFullContent` INTEGER NOT NULL, `isBrowser` INTEGER NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`groupId`) REFERENCES `group`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "id",
Expand Down Expand Up @@ -148,6 +148,12 @@
"columnName": "isFullContent",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "isBrowser",
"columnName": "isBrowser",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
Expand Down Expand Up @@ -371,7 +377,7 @@
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '2b86f20200ed2c56f5ae8d0565cf0f26')"
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'dd4d3c434aed697c688aa692d4778b63')"
]
}
}
4 changes: 4 additions & 0 deletions app/src/main/java/me/ash/reader/domain/model/feed/Feed.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ data class Feed(
var isNotification: Boolean = false,
@ColumnInfo
var isFullContent: Boolean = false,
@ColumnInfo
var isBrowser: Boolean = false,
) {

@Ignore
Expand All @@ -52,6 +54,7 @@ data class Feed(
if (accountId != other.accountId) return false
if (isNotification != other.isNotification) return false
if (isFullContent != other.isFullContent) return false
if (isBrowser != other.isBrowser) return false
if (important != other.important) return false

return true
Expand All @@ -66,6 +69,7 @@ data class Feed(
result = 31 * result + accountId
result = 31 * result + isNotification.hashCode()
result = 31 * result + isFullContent.hashCode()
result = 31 * result + isBrowser.hashCode()
result = 31 * result + (important ?: 0)
return result
}
Expand Down
14 changes: 14 additions & 0 deletions app/src/main/java/me/ash/reader/domain/repository/FeedDao.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,19 @@ interface FeedDao {
groupId: String,
isFullContent: Boolean,
)

@Query(
"""
UPDATE feed SET isBrowser = :isBrowser
WHERE accountId = :accountId
AND groupId = :groupId
"""
)
suspend fun updateIsBrowserByGroupId(
accountId: Int,
groupId: String,
isBrowser: Boolean,
)

@Query(
"""
Expand Down Expand Up @@ -130,6 +143,7 @@ interface FeedDao {
// TODO: Consider migrating the fields to be nullable.
it.isNotification = feed.isNotification
it.isFullContent = feed.isFullContent
it.isBrowser = feed.isBrowser
update(it)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ abstract class AbstractRssRepository(

open suspend fun subscribe(
feedLink: String, searchedFeed: SyndFeed, groupId: String,
isNotification: Boolean, isFullContent: Boolean
isNotification: Boolean, isFullContent: Boolean, isBrowser: Boolean,
) {
val accountId = context.currentAccountId
val feed = Feed(
Expand Down Expand Up @@ -357,6 +357,10 @@ abstract class AbstractRssRepository(
feedDao.updateIsFullContentByGroupId(context.currentAccountId, group.id, isFullContent)
}

suspend fun groupOpenInBrowser(group: Group, isBrowser: Boolean) {
feedDao.updateIsBrowserByGroupId(context.currentAccountId, group.id, isBrowser)
}

suspend fun groupAllowNotification(group: Group, isNotification: Boolean) {
feedDao.updateIsNotificationByGroupId(context.currentAccountId, group.id, isNotification)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ class FeverRssService @Inject constructor(

override suspend fun subscribe(
feedLink: String, searchedFeed: SyndFeed, groupId: String,
isNotification: Boolean, isFullContent: Boolean,
isNotification: Boolean, isFullContent: Boolean, isBrowser: Boolean,
) {
throw FeverAPIException("Unsupported")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ class GoogleReaderRssService @Inject constructor(

override suspend fun subscribe(
feedLink: String, searchedFeed: SyndFeed, groupId: String,
isNotification: Boolean, isFullContent: Boolean,
isNotification: Boolean, isFullContent: Boolean, isBrowser: Boolean,
) {
val accountId = context.currentAccountId
val quickAdd = getGoogleReaderAPI().subscriptionQuickAdd(feedLink)
Expand All @@ -125,6 +125,7 @@ class GoogleReaderRssService @Inject constructor(
accountId = accountId,
isNotification = isNotification,
isFullContent = isFullContent,
isBrowser = isBrowser,
)
)
// TODO: When users need to subscribe to multiple feeds continuously, this makes them uncomfortable.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ class OpmlService @Inject constructor(
if (attachInfo) {
put("isNotification", feed.isNotification.toString())
put("isFullContent", feed.isFullContent.toString())
put("isBrowser", feed.isBrowser.toString())
}
},
listOf()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class OPMLDataSource @Inject constructor(
accountId = targetAccountId,
isNotification = outline.extractPresetNotification(),
isFullContent = outline.extractPresetFullContent(),
isBrowser = outline.extractPresetBrowser(),
)
)
}
Expand All @@ -83,6 +84,7 @@ class OPMLDataSource @Inject constructor(
accountId = targetAccountId,
isNotification = subOutline.extractPresetNotification(),
isFullContent = subOutline.extractPresetFullContent(),
isBrowser = subOutline.extractPresetBrowser(),
)
)
}
Expand Down Expand Up @@ -127,6 +129,9 @@ class OPMLDataSource @Inject constructor(
private fun Outline?.extractPresetFullContent(): Boolean =
this?.attributes?.getOrDefault("isFullContent", null).toBoolean()

private fun Outline?.extractPresetBrowser(): Boolean =
this?.attributes?.getOrDefault("isBrowser", null).toBoolean()

private fun Outline?.isDefaultGroup(): Boolean =
this?.attributes?.getOrDefault("isDefault", null).toBoolean()
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.Article
import androidx.compose.material.icons.outlined.Add
import androidx.compose.material.icons.outlined.Notifications
import androidx.compose.material.icons.outlined.OpenInBrowser
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
Expand All @@ -35,13 +36,15 @@ fun FeedOptionView(
groups: List<Group> = emptyList(),
selectedAllowNotificationPreset: Boolean = false,
selectedParseFullContentPreset: Boolean = false,
selectedOpenInBrowserPreset: Boolean = false,
isMoveToGroup: Boolean = false,
showGroup: Boolean = true,
showUnsubscribe: Boolean = true,
notSubscribeMode: Boolean = false,
selectedGroupId: String = "",
allowNotificationPresetOnClick: () -> Unit = {},
parseFullContentPresetOnClick: () -> Unit = {},
openInBrowserPresetOnClick: () -> Unit = {},
clearArticlesOnClick: () -> Unit = {},
unsubscribeOnClick: () -> Unit = {},
onGroupClick: (groupId: String) -> Unit = {},
Expand All @@ -66,10 +69,12 @@ fun FeedOptionView(
Preset(
selectedAllowNotificationPreset = selectedAllowNotificationPreset,
selectedParseFullContentPreset = selectedParseFullContentPreset,
selectedOpenInBrowserPreset = selectedOpenInBrowserPreset,
showUnsubscribe = showUnsubscribe,
notSubscribeMode = notSubscribeMode,
allowNotificationPresetOnClick = allowNotificationPresetOnClick,
parseFullContentPresetOnClick = parseFullContentPresetOnClick,
openInBrowserPresetOnClick = openInBrowserPresetOnClick,
clearArticlesOnClick = clearArticlesOnClick,
unsubscribeOnClick = unsubscribeOnClick,
)
Expand Down Expand Up @@ -121,10 +126,12 @@ private fun EditableUrl(
private fun Preset(
selectedAllowNotificationPreset: Boolean = false,
selectedParseFullContentPreset: Boolean = false,
selectedOpenInBrowserPreset: Boolean = false,
showUnsubscribe: Boolean = true,
notSubscribeMode: Boolean = false,
allowNotificationPresetOnClick: () -> Unit = {},
parseFullContentPresetOnClick: () -> Unit = {},
openInBrowserPresetOnClick: () -> Unit = {},
clearArticlesOnClick: () -> Unit = {},
unsubscribeOnClick: () -> Unit = {},
) {
Expand Down Expand Up @@ -168,6 +175,23 @@ private fun Preset(
) {
parseFullContentPresetOnClick()
}
RYSelectionChip(
modifier = Modifier.animateContentSize(),
content = stringResource(R.string.open_in_browser),
selected = selectedOpenInBrowserPreset,
selectedIcon = {
Icon(
modifier = Modifier
.padding(start = 8.dp)
.size(20.dp),
imageVector = Icons.Outlined.OpenInBrowser,
contentDescription = stringResource(R.string.open_in_browser),
tint = MaterialTheme.colorScheme.onSurface alwaysLight true,
)
},
) {
openInBrowserPresetOnClick()
}
if (notSubscribeMode) {
RYSelectionChip(
modifier = Modifier.animateContentSize(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ fun FeedOptionDrawer(
selectedAllowNotificationPreset = feedOptionUiState.feed?.isNotification
?: false,
selectedParseFullContentPreset = feedOptionUiState.feed?.isFullContent ?: false,
selectedOpenInBrowserPreset = feedOptionUiState.feed?.isBrowser ?: false,
isMoveToGroup = true,
showGroup = feedOptionViewModel.rssService.get().moveSubscription,
showUnsubscribe = feedOptionViewModel.rssService.get().deleteSubscription,
Expand All @@ -108,6 +109,9 @@ fun FeedOptionDrawer(
parseFullContentPresetOnClick = {
feedOptionViewModel.changeParseFullContentPreset()
},
openInBrowserPresetOnClick = {
feedOptionViewModel.changeOpenInBrowserPreset()
},
clearArticlesOnClick = {
feedOptionViewModel.showClearDialog()
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,15 @@ class FeedOptionViewModel @Inject constructor(
}
}
}

fun changeOpenInBrowserPreset() {
viewModelScope.launch(ioDispatcher) {
_feedOptionUiState.value.feed?.let {
rssService.get().updateFeed(it.copy(isBrowser = !it.isBrowser))
fetchFeed(it.id)
}
}
}

fun changeAllowNotificationPreset() {
viewModelScope.launch(ioDispatcher) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package me.ash.reader.ui.page.home.feeds.drawer.group

import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.Article
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel
import me.ash.reader.R
import me.ash.reader.ui.component.base.RYDialog
import me.ash.reader.ui.ext.collectAsStateValue
import me.ash.reader.ui.ext.showToast

@Composable
fun AllOpenInBrowserDialog(
groupName: String,
groupOptionViewModel: GroupOptionViewModel = hiltViewModel(),
onConfirm: () -> Unit,
) {
val context = LocalContext.current
val groupOptionUiState = groupOptionViewModel.groupOptionUiState.collectAsStateValue()
val scope = rememberCoroutineScope()
val allowToastString = stringResource(R.string.all_open_in_browser_toast, groupName)
val denyToastString = stringResource(R.string.all_deny_open_in_browser_toast, groupName)

RYDialog(
visible = groupOptionUiState.allOpenInBrowserDialogVisible,
onDismissRequest = {
groupOptionViewModel.hideAllOpenInBrowserDialog()
},
icon = {
Icon(
imageVector = Icons.AutoMirrored.Outlined.Article,
contentDescription = stringResource(R.string.open_in_browser),
)
},
title = {
Text(text = stringResource(R.string.open_in_browser))
},
text = {
Text(text = stringResource(R.string.all_open_in_browser_tips, groupName))
},
confirmButton = {
TextButton(
onClick = {
groupOptionViewModel.allOpenInBrowser(true) {
groupOptionViewModel.hideAllOpenInBrowserDialog()
onConfirm()
context.showToast(allowToastString)
}
}
) {
Text(
text = stringResource(R.string.allow),
)
}
},
dismissButton = {
TextButton(
onClick = {
groupOptionViewModel.allOpenInBrowser(false) {
groupOptionViewModel.hideAllOpenInBrowserDialog()
onConfirm()
context.showToast(denyToastString)
}
}
) {
Text(
text = stringResource(R.string.deny),
)
}
},
)
}
Loading
Loading