Add Open In feed parameter

This commit is contained in:
Shinokuni 2024-12-07 17:57:17 +01:00
parent cf75fc914e
commit ef9111c604
18 changed files with 355 additions and 202 deletions

View File

@ -11,6 +11,7 @@ import com.readrops.app.util.components.dialog.TextFieldDialogState
import com.readrops.db.Database
import com.readrops.db.entities.Feed
import com.readrops.db.entities.Folder
import com.readrops.db.entities.OpenIn
import com.readrops.db.filters.MainFilter
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
@ -69,12 +70,22 @@ class FeedScreenModel(
}
.collect { foldersAndFeeds ->
_feedState.update { state ->
val dialog = if (state.dialog is DialogState.FeedSheet) {
val feed = foldersAndFeeds.values.flatten()
.first { it.id == state.dialog.feed.id }
state.dialog.copy(feed = feed)
} else {
state.dialog
val dialog = when (state.dialog) {
is DialogState.FeedSheet -> {
val feed = foldersAndFeeds.values.flatten()
.first { it.id == state.dialog.feed.id }
state.dialog.copy(feed = feed)
}
is DialogState.UpdateFeedOpenInSetting -> {
val feed = foldersAndFeeds.values.flatten()
.first { it.id == state.dialog.feed.id }
state.dialog.copy(feed = feed)
}
else -> {
state.dialog
}
}
state.copy(
@ -137,7 +148,19 @@ class FeedScreenModel(
else -> {}
}
_feedState.update { it.copy(dialog = null) }
if (dialog is DialogState.UpdateFeedOpenInSetting) {
_feedState.update {
it.copy(
dialog = DialogState.FeedSheet(
feed = dialog.feed,
folder = null,
config = currentAccount!!.config
)
)
}
} else {
_feedState.update { it.copy(dialog = null) }
}
}
fun openDialog(state: DialogState) {
@ -342,4 +365,10 @@ class FeedScreenModel(
database.feedDao().updateFeedNotificationState(feedId, isEnabled)
}
}
fun updateFeedOpenInSetting(feedId: Int, openIn: OpenIn) {
screenModelScope.launch(dispatcher) {
database.feedDao().updateOpenInSetting(feedId, openIn)
}
}
}

View File

@ -24,6 +24,7 @@ sealed interface DialogState {
class DeleteFolder(val folder: Folder) : DialogState
class UpdateFeed(val feed: Feed, val folder: Folder?) : DialogState
class UpdateFolder(val folder: Folder) : DialogState
data class UpdateFeedOpenInSetting(val feed: Feed) : DialogState
data class FeedSheet(
val feed: Feed,

View File

@ -10,7 +10,6 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
@ -29,7 +28,6 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
@ -46,14 +44,13 @@ import cafe.adriel.voyager.navigator.tab.Tab
import cafe.adriel.voyager.navigator.tab.TabOptions
import com.readrops.app.MainActivity
import com.readrops.app.R
import com.readrops.app.feeds.dialogs.FeedModalBottomSheet
import com.readrops.app.feeds.dialogs.UpdateFeedDialog
import com.readrops.app.feeds.components.FeedItem
import com.readrops.app.feeds.components.FolderExpandableItem
import com.readrops.app.feeds.dialogs.FeedDialogs
import com.readrops.app.feeds.newfeed.NewFeedScreen
import com.readrops.app.util.components.CenteredProgressIndicator
import com.readrops.app.util.components.ErrorMessage
import com.readrops.app.util.components.Placeholder
import com.readrops.app.util.components.dialog.TextFieldDialog
import com.readrops.app.util.components.dialog.TwoChoicesDialog
import com.readrops.app.util.theme.spacing
import com.readrops.db.entities.Feed
import kotlinx.coroutines.channels.Channel
@ -260,98 +257,6 @@ object FeedTab : Tab {
}
}
@Composable
private fun FeedDialogs(state: FeedState, screenModel: FeedScreenModel) {
val uriHandler = LocalUriHandler.current
val folderState by screenModel.folderState.collectAsStateWithLifecycle()
when (val dialog = state.dialog) {
is DialogState.DeleteFeed -> {
TwoChoicesDialog(
title = stringResource(R.string.delete_feed),
text = stringResource(R.string.delete_feed_question, dialog.feed.name!!),
icon = rememberVectorPainter(image = Icons.Default.Delete),
confirmText = stringResource(R.string.delete),
dismissText = stringResource(R.string.cancel),
onDismiss = { screenModel.closeDialog() },
onConfirm = {
screenModel.deleteFeed(dialog.feed)
screenModel.closeDialog()
}
)
}
is DialogState.FeedSheet -> {
FeedModalBottomSheet(
feed = dialog.feed,
accountNotificationsEnabled = state.isAccountNotificationsEnabled,
onDismissRequest = { screenModel.closeDialog() },
onOpen = {
uriHandler.openUri(dialog.feed.siteUrl!!)
screenModel.closeDialog()
},
onUpdate = {
screenModel.openDialog(DialogState.UpdateFeed(dialog.feed, dialog.folder))
},
onDelete = { screenModel.openDialog(DialogState.DeleteFeed(dialog.feed)) },
onUpdateNotifications = {
screenModel.updateFeedNotifications(dialog.feed.id, it)
},
canUpdateFeed = dialog.config.canUpdateFeed,
canDeleteFeed = dialog.config.canDeleteFeed
)
}
is DialogState.UpdateFeed -> {
UpdateFeedDialog(
viewModel = screenModel,
onDismissRequest = { screenModel.closeDialog(dialog) }
)
}
DialogState.AddFolder -> {
TextFieldDialog(
title = stringResource(id = R.string.add_folder),
icon = painterResource(id = R.drawable.ic_new_folder),
label = stringResource(id = R.string.name),
state = folderState,
onValueChange = { screenModel.setFolderName(it) },
onValidate = { screenModel.folderValidate() },
onDismiss = { screenModel.closeDialog(DialogState.AddFolder) }
)
}
is DialogState.DeleteFolder -> {
TwoChoicesDialog(
title = stringResource(R.string.delete_folder),
text = stringResource(R.string.delete_folder_question, dialog.folder.name!!),
icon = rememberVectorPainter(image = Icons.Default.Delete),
confirmText = stringResource(R.string.delete),
dismissText = stringResource(R.string.cancel),
onDismiss = { screenModel.closeDialog() },
onConfirm = {
screenModel.deleteFolder(dialog.folder)
screenModel.closeDialog()
}
)
}
is DialogState.UpdateFolder -> {
TextFieldDialog(
title = stringResource(id = R.string.edit_folder),
icon = painterResource(id = R.drawable.ic_folder_grey),
label = stringResource(id = R.string.name),
state = folderState,
onValueChange = { screenModel.setFolderName(it) },
onValidate = { screenModel.folderValidate(updateFolder = true) },
onDismiss = { screenModel.closeDialog(DialogState.UpdateFolder(dialog.folder)) }
)
}
null -> {}
}
}
suspend fun openAddFeedDialog(url: String) {
addFeedDialogChannel.send(url)

View File

@ -0,0 +1,111 @@
package com.readrops.app.feeds.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.HorizontalDivider
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.blur
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage
import com.readrops.app.R
import com.readrops.app.util.theme.MediumSpacer
import com.readrops.app.util.theme.VeryShortSpacer
import com.readrops.app.util.theme.spacing
import com.readrops.db.entities.Feed
@Composable
fun FeedBanner(feed: Feed) {
Column {
Box(
modifier = Modifier.fillMaxWidth()
) {
if (feed.imageUrl != null) {
AsyncImage(
model = feed.imageUrl,
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier
.matchParentSize()
.drawWithContent {
drawContent()
drawRect(
color = Color.Black.copy(alpha = 0.65f)
)
}
.blur(2.5.dp)
)
}
Row(
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(
top = MaterialTheme.spacing.largeSpacing,
start = MaterialTheme.spacing.largeSpacing,
end = MaterialTheme.spacing.largeSpacing,
bottom = MaterialTheme.spacing.mediumSpacing
)
) {
AsyncImage(
model = feed.iconUrl,
contentDescription = feed.name!!,
placeholder = painterResource(id = R.drawable.ic_rss_feed_grey),
error = painterResource(id = R.drawable.ic_rss_feed_grey),
modifier = Modifier.size(MaterialTheme.spacing.veryLargeSpacing)
)
MediumSpacer()
Column {
Text(
text = feed.name!!,
style = MaterialTheme.typography.titleLarge,
color = if (feed.imageUrl != null) {
Color.White
} else {
MaterialTheme.colorScheme.onBackground
},
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
if (feed.description != null) {
VeryShortSpacer()
Text(
text = feed.description!!,
style = MaterialTheme.typography.bodyMedium,
color = if (feed.imageUrl != null) {
Color.White
} else {
MaterialTheme.colorScheme.onSurfaceVariant
},
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
}
}
}
}
if (feed.imageUrl == null) {
HorizontalDivider(
modifier = Modifier.padding(horizontal = MaterialTheme.spacing.mediumSpacing)
)
}
}
}

View File

@ -1,4 +1,4 @@
package com.readrops.app.feeds
package com.readrops.app.feeds.components
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.combinedClickable

View File

@ -1,4 +1,4 @@
package com.readrops.app.feeds
package com.readrops.app.feeds.components
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.LinearOutSlowInEasing

View File

@ -1,18 +1,16 @@
package com.readrops.app.feeds.dialogs
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Create
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet
@ -20,36 +18,31 @@ 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.blur
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage
import com.readrops.app.R
import com.readrops.app.feeds.components.FeedBanner
import com.readrops.app.more.preferences.components.BasePreference
import com.readrops.app.util.components.SwitchText
import com.readrops.app.util.theme.LargeSpacer
import com.readrops.app.util.theme.MediumSpacer
import com.readrops.app.util.theme.VeryShortSpacer
import com.readrops.app.util.theme.spacing
import com.readrops.db.entities.Feed
import com.readrops.db.entities.OpenIn
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun FeedModalBottomSheet(
feed: Feed,
accountNotificationsEnabled: Boolean,
onDismissRequest: () -> Unit,
onOpen: () -> Unit,
onUpdate: () -> Unit,
//onUpdateColor: () -> Unit,
onUpdateNotifications: (Boolean) -> Unit,
onOpenInClick: () -> Unit,
onDelete: () -> Unit,
accountNotificationsEnabled: Boolean,
canUpdateFeed: Boolean,
canDeleteFeed: Boolean
) {
@ -58,83 +51,7 @@ fun FeedModalBottomSheet(
onDismissRequest = { onDismissRequest() }
) {
Column {
Box(
modifier = Modifier.fillMaxWidth()
) {
if (feed.imageUrl != null) {
AsyncImage(
model = feed.imageUrl,
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier
.matchParentSize()
.drawWithContent {
drawContent()
drawRect(
color = Color.Black.copy(alpha = 0.65f)
)
}
.blur(2.5.dp)
)
}
Row(
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(
top = MaterialTheme.spacing.largeSpacing,
start = MaterialTheme.spacing.largeSpacing,
end = MaterialTheme.spacing.largeSpacing,
bottom = MaterialTheme.spacing.mediumSpacing
)
) {
AsyncImage(
model = feed.iconUrl,
contentDescription = feed.name!!,
placeholder = painterResource(id = R.drawable.ic_rss_feed_grey),
error = painterResource(id = R.drawable.ic_rss_feed_grey),
modifier = Modifier.size(MaterialTheme.spacing.veryLargeSpacing)
)
MediumSpacer()
Column {
Text(
text = feed.name!!,
style = MaterialTheme.typography.titleLarge,
color = if (feed.imageUrl != null) {
Color.White
} else {
MaterialTheme.colorScheme.onBackground
},
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
if (feed.description != null) {
VeryShortSpacer()
Text(
text = feed.description!!,
style = MaterialTheme.typography.bodyMedium,
color = if (feed.imageUrl != null) {
Color.White
} else {
MaterialTheme.colorScheme.onSurfaceVariant
},
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
}
}
}
}
if (feed.imageUrl == null) {
HorizontalDivider(
modifier = Modifier.padding(horizontal = MaterialTheme.spacing.mediumSpacing)
)
}
FeedBanner(feed)
SwitchText(
title = stringResource(R.string.enable_notifications),
@ -145,6 +62,20 @@ fun FeedModalBottomSheet(
onCheckedChange = onUpdateNotifications
)
BasePreference(
title = stringResource(R.string.open_feed_in),
subtitle = if (feed.openIn == OpenIn.LOCAL_VIEW) {
stringResource(R.string.local_view)
} else {
stringResource(R.string.external_view)
},
onClick = onOpenInClick,
paddingValues = PaddingValues(
horizontal = MaterialTheme.spacing.mediumSpacing,
vertical = MaterialTheme.spacing.shortSpacing
)
)
BottomSheetOption(
text = stringResource(R.string.open),
icon = ImageVector.vectorResource(id = R.drawable.ic_open_in_browser),

View File

@ -0,0 +1,136 @@
package com.readrops.app.feeds.dialogs
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.readrops.app.R
import com.readrops.app.feeds.DialogState
import com.readrops.app.feeds.FeedScreenModel
import com.readrops.app.feeds.FeedState
import com.readrops.app.more.preferences.components.RadioButtonPreferenceDialog
import com.readrops.app.more.preferences.components.ToggleableInfo
import com.readrops.app.util.components.dialog.TextFieldDialog
import com.readrops.app.util.components.dialog.TwoChoicesDialog
import com.readrops.db.entities.OpenIn
@Composable
fun FeedDialogs(state: FeedState, screenModel: FeedScreenModel) {
val uriHandler = LocalUriHandler.current
val folderState by screenModel.folderState.collectAsStateWithLifecycle()
when (val dialog = state.dialog) {
is DialogState.DeleteFeed -> {
TwoChoicesDialog(
title = stringResource(R.string.delete_feed),
text = stringResource(R.string.delete_feed_question, dialog.feed.name!!),
icon = rememberVectorPainter(image = Icons.Default.Delete),
confirmText = stringResource(R.string.delete),
dismissText = stringResource(R.string.cancel),
onDismiss = { screenModel.closeDialog() },
onConfirm = {
screenModel.deleteFeed(dialog.feed)
screenModel.closeDialog()
}
)
}
is DialogState.FeedSheet -> {
FeedModalBottomSheet(
feed = dialog.feed,
accountNotificationsEnabled = state.isAccountNotificationsEnabled,
onDismissRequest = { screenModel.closeDialog() },
onOpen = {
uriHandler.openUri(dialog.feed.siteUrl!!)
screenModel.closeDialog()
},
onUpdate = {
screenModel.openDialog(DialogState.UpdateFeed(dialog.feed, dialog.folder))
},
onDelete = { screenModel.openDialog(DialogState.DeleteFeed(dialog.feed)) },
onUpdateNotifications = {
screenModel.updateFeedNotifications(dialog.feed.id, it)
},
onOpenInClick = {
screenModel.openDialog(DialogState.UpdateFeedOpenInSetting(dialog.feed))
},
canUpdateFeed = dialog.config.canUpdateFeed,
canDeleteFeed = dialog.config.canDeleteFeed
)
}
is DialogState.UpdateFeed -> {
UpdateFeedDialog(
viewModel = screenModel,
onDismissRequest = { screenModel.closeDialog(dialog) }
)
}
DialogState.AddFolder -> {
TextFieldDialog(
title = stringResource(id = R.string.add_folder),
icon = painterResource(id = R.drawable.ic_new_folder),
label = stringResource(id = R.string.name),
state = folderState,
onValueChange = { screenModel.setFolderName(it) },
onValidate = { screenModel.folderValidate() },
onDismiss = { screenModel.closeDialog(DialogState.AddFolder) }
)
}
is DialogState.DeleteFolder -> {
TwoChoicesDialog(
title = stringResource(R.string.delete_folder),
text = stringResource(R.string.delete_folder_question, dialog.folder.name!!),
icon = rememberVectorPainter(image = Icons.Default.Delete),
confirmText = stringResource(R.string.delete),
dismissText = stringResource(R.string.cancel),
onDismiss = { screenModel.closeDialog() },
onConfirm = {
screenModel.deleteFolder(dialog.folder)
screenModel.closeDialog()
}
)
}
is DialogState.UpdateFolder -> {
TextFieldDialog(
title = stringResource(id = R.string.edit_folder),
icon = painterResource(id = R.drawable.ic_folder_grey),
label = stringResource(id = R.string.name),
state = folderState,
onValueChange = { screenModel.setFolderName(it) },
onValidate = { screenModel.folderValidate(updateFolder = true) },
onDismiss = { screenModel.closeDialog(DialogState.UpdateFolder(dialog.folder)) }
)
}
is DialogState.UpdateFeedOpenInSetting -> {
RadioButtonPreferenceDialog(
title = stringResource(R.string.open_feed_in),
entries = listOf(
ToggleableInfo(
key = OpenIn.LOCAL_VIEW,
text = stringResource(R.string.local_view),
isSelected = dialog.feed.openIn == OpenIn.LOCAL_VIEW
),
ToggleableInfo(
key = OpenIn.EXTERNAL_VIEW,
text = stringResource(R.string.external_view),
isSelected = dialog.feed.openIn == OpenIn.EXTERNAL_VIEW
)
),
onCheckChange = { screenModel.updateFeedOpenInSetting(dialog.feed.id, it) },
onDismiss = { screenModel.closeDialog(dialog) },
)
}
null -> {}
}
}

View File

@ -4,6 +4,7 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
@ -22,7 +23,8 @@ fun BasePreference(
onClick: () -> Unit,
modifier: Modifier = Modifier,
subtitle: String? = null,
rightComponent: (@Composable () -> Unit)? = null
rightComponent: (@Composable () -> Unit)? = null,
paddingValues: PaddingValues = PaddingValues(MaterialTheme.spacing.mediumSpacing)
) {
Box(
modifier = modifier.clickable { onClick() }
@ -30,7 +32,7 @@ fun BasePreference(
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier.padding(MaterialTheme.spacing.mediumSpacing)
modifier = Modifier.padding(paddingValues)
) {
Column(
modifier = Modifier.weight(1f)

View File

@ -3,6 +3,7 @@ package com.readrops.app.repositories
import com.readrops.db.Database
import com.readrops.db.entities.Feed
import com.readrops.db.entities.Folder
import com.readrops.db.entities.OpenIn
import com.readrops.db.filters.MainFilter
import com.readrops.db.queries.FeedUnreadCountQueryBuilder
import com.readrops.db.queries.FoldersAndFeedsQueryBuilder
@ -49,6 +50,11 @@ class GetFoldersWithFeeds(
siteUrl = it.feedSiteUrl,
description = it.feedDescription,
isNotificationEnabled = it.feedNotificationsEnabled,
openIn = if (it.feedOpenIn != null) {
it.feedOpenIn!!
} else {
OpenIn.LOCAL_VIEW
},
remoteId = it.feedRemoteId,
unreadCount = itemCounts[it.feedId] ?: 0
)

View File

@ -188,4 +188,7 @@
<string name="error_image_download">Une erreur s\'est produite lors du téléchargement de l\'image</string>
<string name="enable_notifications">Activer les notifications</string>
<string name="account_notifications_disabled">Les notifications du compte sont actuellement désactivées</string>
<string name="open_feed_in">Ouvrir le flux dans</string>
<string name="local_view">Vue locale</string>
<string name="external_view">Vue externe</string>
</resources>

View File

@ -197,4 +197,7 @@
<string name="error_image_download">An error occurred while downloading the image</string>
<string name="enable_notifications">Enable notifications</string>
<string name="account_notifications_disabled">Account notifications are currently disabled</string>
<string name="open_feed_in">Open Feed in</string>
<string name="local_view">Local view</string>
<string name="external_view">External view</string>
</resources>

View File

@ -2,11 +2,11 @@
"formatVersion": 1,
"database": {
"version": 5,
"identityHash": "63de09bfed367e5705a0889d928f056d",
"identityHash": "4f9ecfcf1febf90be9bd9c0cedf0f8a1",
"entities": [
{
"tableName": "Feed",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `description` TEXT, `url` TEXT, `image_url` TEXT, `siteUrl` TEXT, `last_updated` TEXT, `color` INTEGER NOT NULL, `icon_url` TEXT, `etag` TEXT, `last_modified` TEXT, `folder_id` INTEGER, `remote_id` TEXT, `account_id` INTEGER NOT NULL, `notification_enabled` INTEGER NOT NULL DEFAULT 1, FOREIGN KEY(`folder_id`) REFERENCES `Folder`(`id`) ON UPDATE NO ACTION ON DELETE SET NULL , FOREIGN KEY(`account_id`) REFERENCES `Account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `description` TEXT, `url` TEXT, `image_url` TEXT, `siteUrl` TEXT, `last_updated` TEXT, `color` INTEGER NOT NULL, `icon_url` TEXT, `etag` TEXT, `last_modified` TEXT, `folder_id` INTEGER, `remote_id` TEXT, `account_id` INTEGER NOT NULL, `notification_enabled` INTEGER NOT NULL DEFAULT 1, `open_in` TEXT NOT NULL DEFAULT 'LOCAL_VIEW', FOREIGN KEY(`folder_id`) REFERENCES `Folder`(`id`) ON UPDATE NO ACTION ON DELETE SET NULL , FOREIGN KEY(`account_id`) REFERENCES `Account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "id",
@ -98,6 +98,13 @@
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "1"
},
{
"fieldPath": "openIn",
"columnName": "open_in",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'LOCAL_VIEW'"
}
],
"primaryKey": {
@ -526,7 +533,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, '63de09bfed367e5705a0889d928f056d')"
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '4f9ecfcf1febf90be9bd9c0cedf0f8a1')"
]
}
}

View File

@ -124,5 +124,8 @@ object MigrationFrom4To5 : Migration(4, 5) {
// add image_url field
db.execSQL("""ALTER TABLE `Feed` ADD `image_url` TEXT DEFAULT NULL""")
// add open_in field
db.execSQL("""ALTER TABLE `Feed` ADD `open_in` TEXT DEFAULT "LOCAL_VIEW" NOT NULL""")
}
}

View File

@ -7,6 +7,7 @@ import androidx.room.Transaction
import androidx.sqlite.db.SupportSQLiteQuery
import com.readrops.db.entities.Feed
import com.readrops.db.entities.Item
import com.readrops.db.entities.OpenIn
import com.readrops.db.entities.account.Account
import com.readrops.db.pojo.FeedWithCount
import com.readrops.db.pojo.FeedWithFolder
@ -73,6 +74,9 @@ interface FeedDao : BaseDao<Feed> {
@Query("Update Feed set icon_url = :iconUrl Where id = :feedId")
suspend fun updateFeedIconUrl(feedId: Int, iconUrl: String)
@Query("Update Feed set open_in = :openIn Where id = :feedId")
suspend fun updateOpenInSetting(feedId: Int, openIn: OpenIn)
/**
* Insert, update and delete feeds by account
*

View File

@ -8,6 +8,11 @@ import androidx.room.Ignore
import androidx.room.PrimaryKey
import com.readrops.db.entities.account.Account
enum class OpenIn {
LOCAL_VIEW,
EXTERNAL_VIEW
}
@Entity(
foreignKeys = [
ForeignKey(
@ -42,6 +47,10 @@ data class Feed(
name = "notification_enabled",
defaultValue = "1"
) var isNotificationEnabled: Boolean = true,
@ColumnInfo(
name = "open_in",
defaultValue = "LOCAL_VIEW"
) var openIn: OpenIn = OpenIn.LOCAL_VIEW,
@Ignore var unreadCount: Int = 0,
@Ignore var remoteFolderId: String? = null,
)

View File

@ -3,6 +3,7 @@ package com.readrops.db.pojo
import androidx.room.ColumnInfo
import androidx.room.Embedded
import com.readrops.db.entities.Feed
import com.readrops.db.entities.OpenIn
data class FeedWithFolder(
@Embedded val feed: Feed,
@ -21,6 +22,7 @@ data class FolderWithFeed(
val feedDescription: String? = null,
val feedSiteUrl: String? = null,
val feedNotificationsEnabled: Boolean = true,
val feedOpenIn: OpenIn? = null,
val feedRemoteId: String? = null,
val accountId: Int = 0
)

View File

@ -15,6 +15,7 @@ object FoldersAndFeedsQueryBuilder {
"Feed.siteUrl As feedSiteUrl",
"Feed.description as feedDescription",
"Feed.notification_enabled as feedNotificationsEnabled",
"Feed.open_in as feedOpenIn",
"Feed.remote_id as feedRemoteId",
"Folder.id As folderId",
"Folder.name As folderName",