Add GroupOptionView
This commit is contained in:
parent
590470137a
commit
1ba149368b
|
@ -69,6 +69,7 @@ class App : Application(), Configuration.Provider {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
applicationScope.launch(dispatcherDefault) {
|
applicationScope.launch(dispatcherDefault) {
|
||||||
accountInit()
|
accountInit()
|
||||||
|
dataStoreInit()
|
||||||
workerInit()
|
workerInit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,6 +82,9 @@ class App : Application(), Configuration.Provider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun dataStoreInit() {
|
||||||
|
}
|
||||||
|
|
||||||
private fun workerInit() {
|
private fun workerInit() {
|
||||||
rssRepository.get().doSync()
|
rssRepository.get().doSync()
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,20 @@ interface ArticleDao {
|
||||||
)
|
)
|
||||||
suspend fun deleteByFeedId(accountId: Int, feedId: String)
|
suspend fun deleteByFeedId(accountId: Int, feedId: String)
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"""
|
||||||
|
DELETE FROM article
|
||||||
|
WHERE id IN (
|
||||||
|
SELECT a.id FROM article AS a, feed AS b, `group` AS c
|
||||||
|
WHERE a.accountId = :accountId
|
||||||
|
AND a.feedId = b.id
|
||||||
|
AND b.groupId = c.id
|
||||||
|
AND c.id = :groupId
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
suspend fun deleteByGroupId(accountId: Int, groupId: String)
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
@Query(
|
@Query(
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -5,6 +5,41 @@ import me.ash.reader.data.entity.Feed
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface FeedDao {
|
interface FeedDao {
|
||||||
|
@Query(
|
||||||
|
"""
|
||||||
|
UPDATE feed SET isFullContent = :isFullContent
|
||||||
|
WHERE accountId = :accountId
|
||||||
|
AND groupId = :groupId
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
suspend fun updateIsFullContentByGroupId(
|
||||||
|
accountId: Int,
|
||||||
|
groupId: String,
|
||||||
|
isFullContent: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"""
|
||||||
|
UPDATE feed SET isNotification = :isNotification
|
||||||
|
WHERE accountId = :accountId
|
||||||
|
AND groupId = :groupId
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
suspend fun updateIsNotificationByGroupId(
|
||||||
|
accountId: Int,
|
||||||
|
groupId: String,
|
||||||
|
isNotification: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"""
|
||||||
|
DELETE FROM feed
|
||||||
|
WHERE groupId = :groupId
|
||||||
|
AND accountId = :accountId
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
suspend fun deleteByGroupId(accountId: Int, groupId: String)
|
||||||
|
|
||||||
@Query(
|
@Query(
|
||||||
"""
|
"""
|
||||||
SELECT * FROM feed
|
SELECT * FROM feed
|
||||||
|
|
|
@ -13,7 +13,7 @@ interface GroupDao {
|
||||||
WHERE id = :id
|
WHERE id = :id
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
fun queryById(id: String): Group?
|
suspend fun queryById(id: String): Group?
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
@Query(
|
@Query(
|
||||||
|
@ -31,7 +31,7 @@ interface GroupDao {
|
||||||
WHERE accountId = :accountId
|
WHERE accountId = :accountId
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
fun queryAllGroupWithFeed(accountId: Int): List<GroupWithFeed>
|
suspend fun queryAllGroupWithFeed(accountId: Int): List<GroupWithFeed>
|
||||||
|
|
||||||
@Query(
|
@Query(
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -112,6 +112,10 @@ abstract class AbstractRssRepository constructor(
|
||||||
return feedDao.queryById(id)
|
return feedDao.queryById(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun findGroupById(id: String): Group? {
|
||||||
|
return groupDao.queryById(id)
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun findArticleById(id: String): ArticleWithFeed? {
|
suspend fun findArticleById(id: String): ArticleWithFeed? {
|
||||||
return articleDao.queryById(id)
|
return articleDao.queryById(id)
|
||||||
}
|
}
|
||||||
|
@ -133,13 +137,23 @@ abstract class AbstractRssRepository constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun deleteGroup(group: Group) {
|
suspend fun deleteGroup(group: Group) {
|
||||||
groupDao.update(group)
|
articleDao.deleteByGroupId(context.currentAccountId, group.id)
|
||||||
|
feedDao.deleteByGroupId(context.currentAccountId, group.id)
|
||||||
|
groupDao.delete(group)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun deleteFeed(feed: Feed) {
|
suspend fun deleteFeed(feed: Feed) {
|
||||||
articleDao.deleteByFeedId(context.currentAccountId, feed.id)
|
articleDao.deleteByFeedId(context.currentAccountId, feed.id)
|
||||||
feedDao.delete(feed)
|
feedDao.delete(feed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun groupParseFullContent(group: Group, isFullContent: Boolean) {
|
||||||
|
feedDao.updateIsFullContentByGroupId(context.currentAccountId, group.id, isFullContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun groupAllowNotification(group: Group, isNotification: Boolean) {
|
||||||
|
feedDao.updateIsNotificationByGroupId(context.currentAccountId, group.id, isNotification)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@HiltWorker
|
@HiltWorker
|
||||||
|
|
|
@ -8,7 +8,7 @@ import me.ash.reader.data.dao.GroupDao
|
||||||
import me.ash.reader.data.entity.Account
|
import me.ash.reader.data.entity.Account
|
||||||
import me.ash.reader.data.entity.Group
|
import me.ash.reader.data.entity.Group
|
||||||
import me.ash.reader.ui.ext.currentAccountId
|
import me.ash.reader.ui.ext.currentAccountId
|
||||||
import me.ash.reader.ui.ext.spacerDollar
|
import me.ash.reader.ui.ext.getDefaultGroupId
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class AccountRepository @Inject constructor(
|
class AccountRepository @Inject constructor(
|
||||||
|
@ -38,7 +38,7 @@ class AccountRepository @Inject constructor(
|
||||||
if (groupDao.queryAll(it.id!!).isEmpty()) {
|
if (groupDao.queryAll(it.id!!).isEmpty()) {
|
||||||
groupDao.insert(
|
groupDao.insert(
|
||||||
Group(
|
Group(
|
||||||
id = it.id!!.spacerDollar(readYouString + defaultString),
|
id = it.id!!.getDefaultGroupId(),
|
||||||
name = defaultString,
|
name = defaultString,
|
||||||
accountId = it.id!!,
|
accountId = it.id!!,
|
||||||
)
|
)
|
||||||
|
|
|
@ -31,6 +31,7 @@ import me.ash.reader.data.module.DispatcherIO
|
||||||
import me.ash.reader.data.repository.SyncWorker.Companion.setIsSyncing
|
import me.ash.reader.data.repository.SyncWorker.Companion.setIsSyncing
|
||||||
import me.ash.reader.data.source.RssNetworkDataSource
|
import me.ash.reader.data.source.RssNetworkDataSource
|
||||||
import me.ash.reader.ui.ext.currentAccountId
|
import me.ash.reader.ui.ext.currentAccountId
|
||||||
|
import me.ash.reader.ui.ext.spacerDollar
|
||||||
import me.ash.reader.ui.page.common.ExtraName
|
import me.ash.reader.ui.page.common.ExtraName
|
||||||
import me.ash.reader.ui.page.common.NotificationGroupName
|
import me.ash.reader.ui.page.common.NotificationGroupName
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
@ -81,14 +82,16 @@ class LocalRssRepository @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun addGroup(name: String): String {
|
override suspend fun addGroup(name: String): String {
|
||||||
return UUID.randomUUID().toString().also {
|
context.currentAccountId.let { accountId ->
|
||||||
groupDao.insert(
|
return accountId.spacerDollar(UUID.randomUUID().toString()).also {
|
||||||
Group(
|
groupDao.insert(
|
||||||
id = it,
|
Group(
|
||||||
name = name,
|
id = it,
|
||||||
accountId = context.currentAccountId
|
name = name,
|
||||||
|
accountId = accountId
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,14 +7,13 @@ import be.ceau.opml.entity.Head
|
||||||
import be.ceau.opml.entity.Opml
|
import be.ceau.opml.entity.Opml
|
||||||
import be.ceau.opml.entity.Outline
|
import be.ceau.opml.entity.Outline
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import me.ash.reader.R
|
|
||||||
import me.ash.reader.data.dao.AccountDao
|
import me.ash.reader.data.dao.AccountDao
|
||||||
import me.ash.reader.data.dao.FeedDao
|
import me.ash.reader.data.dao.FeedDao
|
||||||
import me.ash.reader.data.dao.GroupDao
|
import me.ash.reader.data.dao.GroupDao
|
||||||
import me.ash.reader.data.entity.Feed
|
import me.ash.reader.data.entity.Feed
|
||||||
import me.ash.reader.data.source.OpmlLocalDataSource
|
import me.ash.reader.data.source.OpmlLocalDataSource
|
||||||
import me.ash.reader.ui.ext.currentAccountId
|
import me.ash.reader.ui.ext.currentAccountId
|
||||||
import me.ash.reader.ui.ext.spacerDollar
|
import me.ash.reader.ui.ext.getDefaultGroupId
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -27,7 +26,6 @@ class OpmlRepository @Inject constructor(
|
||||||
private val accountDao: AccountDao,
|
private val accountDao: AccountDao,
|
||||||
private val rssRepository: RssRepository,
|
private val rssRepository: RssRepository,
|
||||||
private val opmlLocalDataSource: OpmlLocalDataSource,
|
private val opmlLocalDataSource: OpmlLocalDataSource,
|
||||||
private val stringsRepository: StringsRepository,
|
|
||||||
) {
|
) {
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
suspend fun saveToDatabase(inputStream: InputStream) {
|
suspend fun saveToDatabase(inputStream: InputStream) {
|
||||||
|
@ -88,8 +86,6 @@ class OpmlRepository @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getDefaultGroupId(): String {
|
private fun getDefaultGroupId(): String {
|
||||||
val readYouString = stringsRepository.getString(R.string.read_you)
|
return context.currentAccountId.getDefaultGroupId()
|
||||||
val defaultString = stringsRepository.getString(R.string.defaults)
|
|
||||||
return context.currentAccountId.spacerDollar(readYouString + defaultString)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,3 +1,5 @@
|
||||||
package me.ash.reader.ui.ext
|
package me.ash.reader.ui.ext
|
||||||
|
|
||||||
fun Int.spacerDollar(str: Any): String = "$this$$str"
|
fun Int.spacerDollar(str: Any): String = "$this$$str"
|
||||||
|
|
||||||
|
fun Int.getDefaultGroupId() = this.spacerDollar("read_you_app_default_group")
|
|
@ -17,6 +17,7 @@ import me.ash.reader.ui.page.common.ExtraName
|
||||||
import me.ash.reader.ui.page.home.drawer.feed.FeedOptionDrawer
|
import me.ash.reader.ui.page.home.drawer.feed.FeedOptionDrawer
|
||||||
import me.ash.reader.ui.page.home.drawer.feed.FeedOptionViewAction
|
import me.ash.reader.ui.page.home.drawer.feed.FeedOptionViewAction
|
||||||
import me.ash.reader.ui.page.home.drawer.feed.FeedOptionViewModel
|
import me.ash.reader.ui.page.home.drawer.feed.FeedOptionViewModel
|
||||||
|
import me.ash.reader.ui.page.home.drawer.group.GroupOptionDrawer
|
||||||
import me.ash.reader.ui.page.home.feeds.FeedsPage
|
import me.ash.reader.ui.page.home.feeds.FeedsPage
|
||||||
import me.ash.reader.ui.page.home.flow.FlowPage
|
import me.ash.reader.ui.page.home.flow.FlowPage
|
||||||
import me.ash.reader.ui.page.home.read.ReadPage
|
import me.ash.reader.ui.page.home.read.ReadPage
|
||||||
|
@ -165,4 +166,5 @@ fun HomePage(
|
||||||
}
|
}
|
||||||
|
|
||||||
FeedOptionDrawer()
|
FeedOptionDrawer()
|
||||||
|
GroupOptionDrawer()
|
||||||
}
|
}
|
|
@ -27,7 +27,7 @@ fun DeleteFeedDialog(
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val viewState = viewModel.viewState.collectAsStateValue()
|
val viewState = viewModel.viewState.collectAsStateValue()
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val deletedTip = stringResource(R.string.has_been_deleted, feedName)
|
val toastString = stringResource(R.string.delete_toast, feedName)
|
||||||
|
|
||||||
Dialog(
|
Dialog(
|
||||||
visible = viewState.deleteDialogVisible,
|
visible = viewState.deleteDialogVisible,
|
||||||
|
@ -37,7 +37,7 @@ fun DeleteFeedDialog(
|
||||||
icon = {
|
icon = {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Outlined.DeleteForever,
|
imageVector = Icons.Outlined.DeleteForever,
|
||||||
contentDescription = stringResource(R.string.subscribe),
|
contentDescription = stringResource(R.string.unsubscribe),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
title = {
|
title = {
|
||||||
|
@ -52,7 +52,7 @@ fun DeleteFeedDialog(
|
||||||
viewModel.dispatch(FeedOptionViewAction.Delete {
|
viewModel.dispatch(FeedOptionViewAction.Delete {
|
||||||
viewModel.dispatch(FeedOptionViewAction.HideDeleteDialog)
|
viewModel.dispatch(FeedOptionViewAction.HideDeleteDialog)
|
||||||
viewModel.dispatch(FeedOptionViewAction.Hide(scope))
|
viewModel.dispatch(FeedOptionViewAction.Hide(scope))
|
||||||
Toast.makeText(context, deletedTip, Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, toastString, Toast.LENGTH_SHORT).show()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
package me.ash.reader.ui.page.home.drawer.group
|
||||||
|
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.Notifications
|
||||||
|
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.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
|
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||||
|
import me.ash.reader.R
|
||||||
|
import me.ash.reader.ui.component.Dialog
|
||||||
|
import me.ash.reader.ui.ext.collectAsStateValue
|
||||||
|
|
||||||
|
@OptIn(ExperimentalPagerApi::class)
|
||||||
|
@Composable
|
||||||
|
fun AllAllowNotificationDialog(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
groupName: String,
|
||||||
|
viewModel: GroupOptionViewModel = hiltViewModel(),
|
||||||
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val viewState = viewModel.viewState.collectAsStateValue()
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val allowToastString = stringResource(R.string.all_allow_notification_toast, groupName)
|
||||||
|
val denyToastString = stringResource(R.string.all_deny_notification_toast, groupName)
|
||||||
|
|
||||||
|
Dialog(
|
||||||
|
visible = viewState.allAllowNotificationDialogVisible,
|
||||||
|
onDismissRequest = {
|
||||||
|
viewModel.dispatch(GroupOptionViewAction.HideAllAllowNotificationDialog)
|
||||||
|
},
|
||||||
|
icon = {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.Notifications,
|
||||||
|
contentDescription = stringResource(R.string.allow_notification),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
title = {
|
||||||
|
Text(text = stringResource(R.string.allow_notification))
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Text(text = stringResource(R.string.all_allow_notification_tip, groupName))
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
viewModel.dispatch(GroupOptionViewAction.AllAllowNotification(true) {
|
||||||
|
viewModel.dispatch(GroupOptionViewAction.HideAllAllowNotificationDialog)
|
||||||
|
viewModel.dispatch(GroupOptionViewAction.Hide(scope))
|
||||||
|
Toast.makeText(context, allowToastString, Toast.LENGTH_SHORT).show()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.allow),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
viewModel.dispatch(GroupOptionViewAction.AllAllowNotification(false) {
|
||||||
|
viewModel.dispatch(GroupOptionViewAction.HideAllAllowNotificationDialog)
|
||||||
|
viewModel.dispatch(GroupOptionViewAction.Hide(scope))
|
||||||
|
Toast.makeText(context, denyToastString, Toast.LENGTH_SHORT).show()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.deny),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
package me.ash.reader.ui.page.home.drawer.group
|
||||||
|
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.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.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
|
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||||
|
import me.ash.reader.R
|
||||||
|
import me.ash.reader.ui.component.Dialog
|
||||||
|
import me.ash.reader.ui.ext.collectAsStateValue
|
||||||
|
|
||||||
|
@OptIn(ExperimentalPagerApi::class)
|
||||||
|
@Composable
|
||||||
|
fun AllParseFullContentDialog(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
groupName: String,
|
||||||
|
viewModel: GroupOptionViewModel = hiltViewModel(),
|
||||||
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val viewState = viewModel.viewState.collectAsStateValue()
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val allowToastString = stringResource(R.string.all_parse_full_content_toast, groupName)
|
||||||
|
val denyToastString = stringResource(R.string.all_deny_parse_full_content_toast, groupName)
|
||||||
|
|
||||||
|
Dialog(
|
||||||
|
visible = viewState.allParseFullContentDialogVisible,
|
||||||
|
onDismissRequest = {
|
||||||
|
viewModel.dispatch(GroupOptionViewAction.HideAllParseFullContentDialog)
|
||||||
|
},
|
||||||
|
icon = {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.Article,
|
||||||
|
contentDescription = stringResource(R.string.parse_full_content),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
title = {
|
||||||
|
Text(text = stringResource(R.string.parse_full_content))
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Text(text = stringResource(R.string.all_parse_full_content_tip, groupName))
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
viewModel.dispatch(GroupOptionViewAction.AllParseFullContent(true) {
|
||||||
|
viewModel.dispatch(GroupOptionViewAction.HideAllParseFullContentDialog)
|
||||||
|
viewModel.dispatch(GroupOptionViewAction.Hide(scope))
|
||||||
|
Toast.makeText(context, allowToastString, Toast.LENGTH_SHORT).show()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.allow),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
viewModel.dispatch(GroupOptionViewAction.AllParseFullContent(false) {
|
||||||
|
viewModel.dispatch(GroupOptionViewAction.HideAllParseFullContentDialog)
|
||||||
|
viewModel.dispatch(GroupOptionViewAction.Hide(scope))
|
||||||
|
Toast.makeText(context, denyToastString, Toast.LENGTH_SHORT).show()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.deny),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
package me.ash.reader.ui.page.home.drawer.group
|
||||||
|
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.DeleteForever
|
||||||
|
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.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
|
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||||
|
import me.ash.reader.R
|
||||||
|
import me.ash.reader.ui.component.Dialog
|
||||||
|
import me.ash.reader.ui.ext.collectAsStateValue
|
||||||
|
|
||||||
|
@OptIn(ExperimentalPagerApi::class)
|
||||||
|
@Composable
|
||||||
|
fun DeleteGroupDialog(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
groupName: String,
|
||||||
|
viewModel: GroupOptionViewModel = hiltViewModel(),
|
||||||
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val viewState = viewModel.viewState.collectAsStateValue()
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val toastString = stringResource(R.string.delete_toast, groupName)
|
||||||
|
|
||||||
|
Dialog(
|
||||||
|
visible = viewState.deleteDialogVisible,
|
||||||
|
onDismissRequest = {
|
||||||
|
viewModel.dispatch(GroupOptionViewAction.HideDeleteDialog)
|
||||||
|
},
|
||||||
|
icon = {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.DeleteForever,
|
||||||
|
contentDescription = stringResource(R.string.delete_group),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
title = {
|
||||||
|
Text(text = stringResource(R.string.delete_group))
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Text(text = stringResource(R.string.delete_group_tip, groupName))
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
viewModel.dispatch(GroupOptionViewAction.Delete {
|
||||||
|
viewModel.dispatch(GroupOptionViewAction.HideDeleteDialog)
|
||||||
|
viewModel.dispatch(GroupOptionViewAction.Hide(scope))
|
||||||
|
Toast.makeText(context, toastString, Toast.LENGTH_SHORT).show()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.delete),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
viewModel.dispatch(GroupOptionViewAction.HideDeleteDialog)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.cancel),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,155 @@
|
||||||
|
package me.ash.reader.ui.page.home.drawer.group
|
||||||
|
|
||||||
|
import androidx.compose.animation.animateContentSize
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material.ExperimentalMaterialApi
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.Article
|
||||||
|
import androidx.compose.material.icons.outlined.Folder
|
||||||
|
import androidx.compose.material.icons.outlined.Notifications
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
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.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
|
import com.google.accompanist.flowlayout.FlowRow
|
||||||
|
import com.google.accompanist.flowlayout.MainAxisAlignment
|
||||||
|
import me.ash.reader.R
|
||||||
|
import me.ash.reader.ui.component.BottomDrawer
|
||||||
|
import me.ash.reader.ui.component.SelectionChip
|
||||||
|
import me.ash.reader.ui.component.Subtitle
|
||||||
|
import me.ash.reader.ui.ext.collectAsStateValue
|
||||||
|
import me.ash.reader.ui.ext.currentAccountId
|
||||||
|
import me.ash.reader.ui.ext.getDefaultGroupId
|
||||||
|
import me.ash.reader.ui.ext.roundClick
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterialApi::class)
|
||||||
|
@Composable
|
||||||
|
fun GroupOptionDrawer(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
GroupOptionViewModel: GroupOptionViewModel = hiltViewModel(),
|
||||||
|
content: @Composable () -> Unit = {},
|
||||||
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val viewState = GroupOptionViewModel.viewState.collectAsStateValue()
|
||||||
|
val group = viewState.group
|
||||||
|
|
||||||
|
BottomDrawer(
|
||||||
|
drawerState = viewState.drawerState,
|
||||||
|
sheetContent = {
|
||||||
|
Column {
|
||||||
|
Icon(
|
||||||
|
modifier = modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.align(Alignment.CenterHorizontally),
|
||||||
|
imageVector = Icons.Outlined.Folder,
|
||||||
|
contentDescription = group?.name ?: stringResource(R.string.unknown),
|
||||||
|
tint = MaterialTheme.colorScheme.onSurface
|
||||||
|
)
|
||||||
|
Spacer(modifier = modifier.height(16.dp))
|
||||||
|
Text(
|
||||||
|
modifier = Modifier
|
||||||
|
.roundClick {}
|
||||||
|
.fillMaxWidth(),
|
||||||
|
text = group?.name ?: stringResource(R.string.unknown),
|
||||||
|
style = MaterialTheme.typography.headlineSmall,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
)
|
||||||
|
Spacer(modifier = modifier.height(16.dp))
|
||||||
|
Column(
|
||||||
|
modifier = modifier.verticalScroll(rememberScrollState())
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.group_option_tip),
|
||||||
|
color = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f),
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.height(26.dp))
|
||||||
|
|
||||||
|
Subtitle(text = stringResource(R.string.preset))
|
||||||
|
Spacer(modifier = Modifier.height(10.dp))
|
||||||
|
FlowRow(
|
||||||
|
mainAxisAlignment = MainAxisAlignment.Start,
|
||||||
|
crossAxisSpacing = 10.dp,
|
||||||
|
mainAxisSpacing = 10.dp,
|
||||||
|
) {
|
||||||
|
SelectionChip(
|
||||||
|
modifier = Modifier.animateContentSize(),
|
||||||
|
content = stringResource(R.string.allow_notification),
|
||||||
|
selected = false,
|
||||||
|
selectedIcon = {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.Notifications,
|
||||||
|
contentDescription = stringResource(R.string.allow_notification),
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(start = 8.dp)
|
||||||
|
.size(20.dp),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
GroupOptionViewModel.dispatch(GroupOptionViewAction.ShowAllAllowNotificationDialog)
|
||||||
|
}
|
||||||
|
SelectionChip(
|
||||||
|
modifier = Modifier.animateContentSize(),
|
||||||
|
content = stringResource(R.string.parse_full_content),
|
||||||
|
selected = false,
|
||||||
|
selectedIcon = {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.Article,
|
||||||
|
contentDescription = stringResource(R.string.parse_full_content),
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(start = 8.dp)
|
||||||
|
.size(20.dp),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
GroupOptionViewModel.dispatch(GroupOptionViewAction.ShowAllParseFullContentDialog)
|
||||||
|
}
|
||||||
|
if (group?.id != context.currentAccountId.getDefaultGroupId()) {
|
||||||
|
SelectionChip(
|
||||||
|
modifier = Modifier.animateContentSize(),
|
||||||
|
content = stringResource(R.string.delete_group),
|
||||||
|
selected = false,
|
||||||
|
) {
|
||||||
|
GroupOptionViewModel.dispatch(GroupOptionViewAction.ShowDeleteDialog)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.height(26.dp))
|
||||||
|
|
||||||
|
// AddToGroup(
|
||||||
|
// groups = groups,
|
||||||
|
// selectedGroupId = selectedGroupId,
|
||||||
|
// onGroupClick = onGroupClick,
|
||||||
|
// onAddNewGroup = onAddNewGroup,
|
||||||
|
// )
|
||||||
|
Spacer(modifier = Modifier.height(6.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
content()
|
||||||
|
}
|
||||||
|
|
||||||
|
DeleteGroupDialog(groupName = group?.name ?: "")
|
||||||
|
AllAllowNotificationDialog(groupName = group?.name ?: "")
|
||||||
|
AllParseFullContentDialog(groupName = group?.name ?: "")
|
||||||
|
}
|
|
@ -0,0 +1,189 @@
|
||||||
|
package me.ash.reader.ui.page.home.drawer.group
|
||||||
|
|
||||||
|
import androidx.compose.material.ExperimentalMaterialApi
|
||||||
|
import androidx.compose.material.ModalBottomSheetState
|
||||||
|
import androidx.compose.material.ModalBottomSheetValue
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import me.ash.reader.data.entity.Group
|
||||||
|
import me.ash.reader.data.repository.RssRepository
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@OptIn(
|
||||||
|
ExperimentalPagerApi::class,
|
||||||
|
ExperimentalMaterialApi::class
|
||||||
|
)
|
||||||
|
@HiltViewModel
|
||||||
|
class GroupOptionViewModel @Inject constructor(
|
||||||
|
private val rssRepository: RssRepository,
|
||||||
|
) : ViewModel() {
|
||||||
|
private val _viewState = MutableStateFlow(GroupOptionViewState())
|
||||||
|
val viewState: StateFlow<GroupOptionViewState> = _viewState.asStateFlow()
|
||||||
|
|
||||||
|
init {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
rssRepository.get().pullGroups().collect { groups ->
|
||||||
|
_viewState.update {
|
||||||
|
it.copy(
|
||||||
|
groups = groups
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun dispatch(action: GroupOptionViewAction) {
|
||||||
|
when (action) {
|
||||||
|
is GroupOptionViewAction.Show -> show(action.scope, action.groupId)
|
||||||
|
is GroupOptionViewAction.Hide -> hide(action.scope)
|
||||||
|
is GroupOptionViewAction.ShowDeleteDialog -> changeDeleteDialogVisible(true)
|
||||||
|
is GroupOptionViewAction.HideDeleteDialog -> changeDeleteDialogVisible(false)
|
||||||
|
is GroupOptionViewAction.Delete -> delete(action.callback)
|
||||||
|
is GroupOptionViewAction.ShowAllAllowNotificationDialog ->
|
||||||
|
changeAllAllowNotificationDialogVisible(true)
|
||||||
|
is GroupOptionViewAction.HideAllAllowNotificationDialog ->
|
||||||
|
changeAllAllowNotificationDialogVisible(false)
|
||||||
|
is GroupOptionViewAction.AllAllowNotification ->
|
||||||
|
allAllowNotification(action.isNotification, action.callback)
|
||||||
|
is GroupOptionViewAction.ShowAllParseFullContentDialog ->
|
||||||
|
changeAllParseFullContentDialogVisible(true)
|
||||||
|
is GroupOptionViewAction.HideAllParseFullContentDialog ->
|
||||||
|
changeAllParseFullContentDialogVisible(false)
|
||||||
|
is GroupOptionViewAction.AllParseFullContent ->
|
||||||
|
allParseFullContent(action.isFullContent, action.callback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun fetchGroup(groupId: String) {
|
||||||
|
val group = rssRepository.get().findGroupById(groupId)
|
||||||
|
_viewState.update {
|
||||||
|
it.copy(
|
||||||
|
group = group,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun show(scope: CoroutineScope, groupId: String) {
|
||||||
|
scope.launch {
|
||||||
|
fetchGroup(groupId)
|
||||||
|
_viewState.value.drawerState.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hide(scope: CoroutineScope) {
|
||||||
|
scope.launch {
|
||||||
|
_viewState.value.drawerState.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun allAllowNotification(isNotification: Boolean, callback: () -> Unit = {}) {
|
||||||
|
_viewState.value.group?.let {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
rssRepository.get().groupAllowNotification(it, isNotification)
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun changeAllAllowNotificationDialogVisible(visible: Boolean) {
|
||||||
|
_viewState.update {
|
||||||
|
it.copy(
|
||||||
|
allAllowNotificationDialogVisible = visible,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun allParseFullContent(isFullContent: Boolean, callback: () -> Unit = {}) {
|
||||||
|
_viewState.value.group?.let {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
rssRepository.get().groupParseFullContent(it, isFullContent)
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun changeAllParseFullContentDialogVisible(visible: Boolean) {
|
||||||
|
_viewState.update {
|
||||||
|
it.copy(
|
||||||
|
allParseFullContentDialogVisible = visible,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun delete(callback: () -> Unit = {}) {
|
||||||
|
_viewState.value.group?.let {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
rssRepository.get().deleteGroup(it)
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun changeDeleteDialogVisible(visible: Boolean) {
|
||||||
|
_viewState.update {
|
||||||
|
it.copy(
|
||||||
|
deleteDialogVisible = visible,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterialApi::class)
|
||||||
|
data class GroupOptionViewState(
|
||||||
|
var drawerState: ModalBottomSheetState = ModalBottomSheetState(ModalBottomSheetValue.Hidden),
|
||||||
|
val group: Group? = null,
|
||||||
|
val groups: List<Group> = emptyList(),
|
||||||
|
val allAllowNotificationDialogVisible: Boolean = false,
|
||||||
|
val allParseFullContentDialogVisible: Boolean = false,
|
||||||
|
val deleteDialogVisible: Boolean = false,
|
||||||
|
)
|
||||||
|
|
||||||
|
sealed class GroupOptionViewAction {
|
||||||
|
data class Show(
|
||||||
|
val scope: CoroutineScope,
|
||||||
|
val groupId: String
|
||||||
|
) : GroupOptionViewAction()
|
||||||
|
|
||||||
|
data class Hide(
|
||||||
|
val scope: CoroutineScope,
|
||||||
|
) : GroupOptionViewAction()
|
||||||
|
|
||||||
|
data class Delete(
|
||||||
|
val callback: () -> Unit = {}
|
||||||
|
) : GroupOptionViewAction()
|
||||||
|
|
||||||
|
object ShowDeleteDialog : GroupOptionViewAction()
|
||||||
|
object HideDeleteDialog : GroupOptionViewAction()
|
||||||
|
|
||||||
|
data class AllParseFullContent(
|
||||||
|
val isFullContent: Boolean,
|
||||||
|
val callback: () -> Unit = {}
|
||||||
|
) : GroupOptionViewAction()
|
||||||
|
|
||||||
|
object ShowAllParseFullContentDialog : GroupOptionViewAction()
|
||||||
|
object HideAllParseFullContentDialog : GroupOptionViewAction()
|
||||||
|
|
||||||
|
data class AllAllowNotification(
|
||||||
|
val isNotification: Boolean,
|
||||||
|
val callback: () -> Unit = {}
|
||||||
|
) : GroupOptionViewAction()
|
||||||
|
|
||||||
|
object ShowAllAllowNotificationDialog : GroupOptionViewAction()
|
||||||
|
object HideAllAllowNotificationDialog : GroupOptionViewAction()
|
||||||
|
}
|
|
@ -186,7 +186,7 @@ fun FeedsPage(
|
||||||
// Crossfade(targetState = groupWithFeed) { groupWithFeed ->
|
// Crossfade(targetState = groupWithFeed) { groupWithFeed ->
|
||||||
Column {
|
Column {
|
||||||
GroupItem(
|
GroupItem(
|
||||||
text = groupWithFeed.group.name,
|
group = groupWithFeed.group,
|
||||||
feeds = groupWithFeed.feeds,
|
feeds = groupWithFeed.feeds,
|
||||||
groupOnClick = {
|
groupOnClick = {
|
||||||
onFilterChange(
|
onFilterChange(
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package me.ash.reader.ui.page.home.feeds
|
package me.ash.reader.ui.page.home.feeds
|
||||||
|
|
||||||
|
import android.view.HapticFeedbackConstants
|
||||||
import androidx.compose.animation.*
|
import androidx.compose.animation.*
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
|
@ -18,22 +19,30 @@ import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
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.platform.LocalView
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import me.ash.reader.R
|
import me.ash.reader.R
|
||||||
import me.ash.reader.data.entity.Feed
|
import me.ash.reader.data.entity.Feed
|
||||||
|
import me.ash.reader.data.entity.Group
|
||||||
|
import me.ash.reader.ui.page.home.drawer.group.GroupOptionViewAction
|
||||||
|
import me.ash.reader.ui.page.home.drawer.group.GroupOptionViewModel
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterialApi::class, androidx.compose.foundation.ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalMaterialApi::class, androidx.compose.foundation.ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun GroupItem(
|
fun GroupItem(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
text: String,
|
group: Group,
|
||||||
feeds: List<Feed>,
|
feeds: List<Feed>,
|
||||||
isExpanded: Boolean = true,
|
isExpanded: Boolean = true,
|
||||||
|
groupOptionViewModel: GroupOptionViewModel = hiltViewModel(),
|
||||||
groupOnClick: () -> Unit = {},
|
groupOnClick: () -> Unit = {},
|
||||||
feedOnClick: (feed: Feed) -> Unit = {},
|
feedOnClick: (feed: Feed) -> Unit = {},
|
||||||
) {
|
) {
|
||||||
|
val view = LocalView.current
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
var expanded by remember { mutableStateOf(isExpanded) }
|
var expanded by remember { mutableStateOf(isExpanded) }
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
|
@ -47,6 +56,8 @@ fun GroupItem(
|
||||||
groupOnClick()
|
groupOnClick()
|
||||||
},
|
},
|
||||||
onLongClick = {
|
onLongClick = {
|
||||||
|
view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
|
||||||
|
groupOptionViewModel.dispatch(GroupOptionViewAction.Show(scope, group.id))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.padding(top = 22.dp)
|
.padding(top = 22.dp)
|
||||||
|
@ -60,7 +71,7 @@ fun GroupItem(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.weight(1f)
|
.weight(1f)
|
||||||
.padding(start = 28.dp),
|
.padding(start = 28.dp),
|
||||||
text = text,
|
text = group.name,
|
||||||
style = MaterialTheme.typography.titleMedium,
|
style = MaterialTheme.typography.titleMedium,
|
||||||
color = MaterialTheme.colorScheme.onSecondaryContainer,
|
color = MaterialTheme.colorScheme.onSecondaryContainer,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
<string name="expand_more">展开</string>
|
<string name="expand_more">展开</string>
|
||||||
<string name="confirm">确认</string>
|
<string name="confirm">确认</string>
|
||||||
<string name="cancel">取消</string>
|
<string name="cancel">取消</string>
|
||||||
|
<string name="allow">允许</string>
|
||||||
|
<string name="deny">拒绝</string>
|
||||||
<string name="defaults">默认</string>
|
<string name="defaults">默认</string>
|
||||||
<string name="unknown">未知</string>
|
<string name="unknown">未知</string>
|
||||||
<string name="back">返回</string>
|
<string name="back">返回</string>
|
||||||
|
@ -29,16 +31,25 @@
|
||||||
<string name="preset">预设</string>
|
<string name="preset">预设</string>
|
||||||
<string name="selected">已选择</string>
|
<string name="selected">已选择</string>
|
||||||
<string name="allow_notification">允许通知</string>
|
<string name="allow_notification">允许通知</string>
|
||||||
|
<string name="all_allow_notification_tip">允许 \"%1$s\" 分组中的所有订阅源发出通知。</string>
|
||||||
|
<string name="all_allow_notification_toast">已全部允许 \"%1$s\" 分组中的通知</string>
|
||||||
|
<string name="all_deny_notification_toast">已全部拒绝 \"%1$s\" 分组中的通知</string>
|
||||||
<string name="parse_full_content">全文解析</string>
|
<string name="parse_full_content">全文解析</string>
|
||||||
|
<string name="all_parse_full_content_tip">对 \"%1$s\" 分组中的所有文章进行全文解析。</string>
|
||||||
|
<string name="all_parse_full_content_toast">全文解析 \"%1$s\" 分组中的文章</string>
|
||||||
|
<string name="all_deny_parse_full_content_toast">不再全文解析 \"%1$s\" 分组中的文章</string>
|
||||||
<string name="add_to_group">添加到组</string>
|
<string name="add_to_group">添加到组</string>
|
||||||
<string name="create_new_group">新建分组</string>
|
<string name="create_new_group">新建分组</string>
|
||||||
<string name="name">名称</string>
|
<string name="name">名称</string>
|
||||||
<string name="open_with">打开 %1$s</string>
|
<string name="open_with">打开 %1$s</string>
|
||||||
<string name="options">选项</string>
|
<string name="options">选项</string>
|
||||||
<string name="delete">删除</string>
|
<string name="delete">删除</string>
|
||||||
<string name="has_been_deleted">\"%1$s\" 已被删除</string>
|
<string name="delete_toast">\"%1$s\" 已被删除</string>
|
||||||
<string name="unsubscribe">取消订阅</string>
|
<string name="unsubscribe">取消订阅</string>
|
||||||
<string name="unsubscribe_tip">不再订阅 \"%1$s\",同时删除其所有已归档的文章。</string>
|
<string name="unsubscribe_tip">不再订阅 \"%1$s\",同时删除其中所有已归档的文章。</string>
|
||||||
|
<string name="delete_group">删除分组</string>
|
||||||
|
<string name="delete_group_tip">删除 \"%1$s\" 分组,同时删除其中所有订阅源和已归档的文章。</string>
|
||||||
|
<string name="group_option_tip">以下选项将应用到该分组中的所有订阅源。</string>
|
||||||
<string name="today">今天</string>
|
<string name="today">今天</string>
|
||||||
<string name="yesterday">昨天</string>
|
<string name="yesterday">昨天</string>
|
||||||
<string name="date_at_time">%1$s %2$s</string>
|
<string name="date_at_time">%1$s %2$s</string>
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
<string name="expand_more">Expand More</string>
|
<string name="expand_more">Expand More</string>
|
||||||
<string name="confirm">Confirm</string>
|
<string name="confirm">Confirm</string>
|
||||||
<string name="cancel">Cancel</string>
|
<string name="cancel">Cancel</string>
|
||||||
|
<string name="allow">Allow</string>
|
||||||
|
<string name="deny">Deny</string>
|
||||||
<string name="defaults">Default</string>
|
<string name="defaults">Default</string>
|
||||||
<string name="unknown">Unknown</string>
|
<string name="unknown">Unknown</string>
|
||||||
<string name="back">Back</string>
|
<string name="back">Back</string>
|
||||||
|
@ -29,16 +31,25 @@
|
||||||
<string name="preset">Preset</string>
|
<string name="preset">Preset</string>
|
||||||
<string name="selected">Selected</string>
|
<string name="selected">Selected</string>
|
||||||
<string name="allow_notification">Allow Notification</string>
|
<string name="allow_notification">Allow Notification</string>
|
||||||
|
<string name="all_allow_notification_tip">Allow all feeds in the \"%1$s\" group to send notifications.</string>
|
||||||
|
<string name="all_allow_notification_toast">All notifications in the \"%1$s\" group are allowed</string>
|
||||||
|
<string name="all_deny_notification_toast">All notifications in the \"%1$s\" group are denied</string>
|
||||||
<string name="parse_full_content">Parse Full Content</string>
|
<string name="parse_full_content">Parse Full Content</string>
|
||||||
|
<string name="all_parse_full_content_tip">Full content parsing of all articles in the \"%1$s\" group.</string>
|
||||||
|
<string name="all_parse_full_content_toast">Full content parsing of all articles in the \"%1$s\" group</string>
|
||||||
|
<string name="all_deny_parse_full_content_toast">No more full content parsing of all articles in the \"%1$s\" group</string>
|
||||||
<string name="add_to_group">Add to Group</string>
|
<string name="add_to_group">Add to Group</string>
|
||||||
<string name="create_new_group">Create New Group</string>
|
<string name="create_new_group">Create New Group</string>
|
||||||
<string name="name">Name</string>
|
<string name="name">Name</string>
|
||||||
<string name="open_with">Open %1$s</string>
|
<string name="open_with">Open %1$s</string>
|
||||||
<string name="options">Options</string>
|
<string name="options">Options</string>
|
||||||
<string name="delete">Delete</string>
|
<string name="delete">Delete</string>
|
||||||
<string name="has_been_deleted">\"%1$s\" has been deleted</string>
|
<string name="delete_toast">\"%1$s\" has been deleted</string>
|
||||||
<string name="unsubscribe">Unsubscribe</string>
|
<string name="unsubscribe">Unsubscribe</string>
|
||||||
<string name="unsubscribe_tip">Unsubscribe \"%1$s\" and delete all its archived articles.</string>
|
<string name="unsubscribe_tip">Unsubscribe \"%1$s\" and delete all archived articles in it.</string>
|
||||||
|
<string name="delete_group">Delete Group</string>
|
||||||
|
<string name="delete_group_tip">Delete the \"%1$s\" group, and delete all feeds and archived articles in it.</string>
|
||||||
|
<string name="group_option_tip">The following options will be applied to all feeds in this group.</string>
|
||||||
<string name="today">Today</string>
|
<string name="today">Today</string>
|
||||||
<string name="yesterday">Yesterday</string>
|
<string name="yesterday">Yesterday</string>
|
||||||
<string name="date_at_time">%1$s At %2$s</string>
|
<string name="date_at_time">%1$s At %2$s</string>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user