mirror of
https://github.com/msfjarvis/compose-lobsters
synced 2025-08-15 07:37:03 +05:30
feat(android): wire up import/export settings to UI
This commit is contained in:
parent
fb0e372917
commit
3738d94789
5 changed files with 142 additions and 1 deletions
|
@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Backup and restore options for saved posts
|
||||||
|
|
||||||
## [1.28.0] - 2023-06-03
|
## [1.28.0] - 2023-06-03
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
|
@ -18,6 +18,7 @@ import androidx.compose.material.icons.filled.Favorite
|
||||||
import androidx.compose.material.icons.filled.NewReleases
|
import androidx.compose.material.icons.filled.NewReleases
|
||||||
import androidx.compose.material.icons.filled.Whatshot
|
import androidx.compose.material.icons.filled.Whatshot
|
||||||
import androidx.compose.material.icons.outlined.FavoriteBorder
|
import androidx.compose.material.icons.outlined.FavoriteBorder
|
||||||
|
import androidx.compose.material.icons.outlined.ImportExport
|
||||||
import androidx.compose.material.icons.outlined.NavigateBefore
|
import androidx.compose.material.icons.outlined.NavigateBefore
|
||||||
import androidx.compose.material.icons.outlined.NewReleases
|
import androidx.compose.material.icons.outlined.NewReleases
|
||||||
import androidx.compose.material.icons.outlined.Whatshot
|
import androidx.compose.material.icons.outlined.Whatshot
|
||||||
|
@ -49,6 +50,7 @@ import androidx.navigation.navDeepLink
|
||||||
import androidx.paging.compose.collectAsLazyPagingItems
|
import androidx.paging.compose.collectAsLazyPagingItems
|
||||||
import com.deliveryhero.whetstone.compose.injectedViewModel
|
import com.deliveryhero.whetstone.compose.injectedViewModel
|
||||||
import dev.msfjarvis.claw.android.R
|
import dev.msfjarvis.claw.android.R
|
||||||
|
import dev.msfjarvis.claw.android.ui.datatransfer.DataTransferScreen
|
||||||
import dev.msfjarvis.claw.android.ui.decorations.ClawNavigationBar
|
import dev.msfjarvis.claw.android.ui.decorations.ClawNavigationBar
|
||||||
import dev.msfjarvis.claw.android.ui.decorations.ClawNavigationRail
|
import dev.msfjarvis.claw.android.ui.decorations.ClawNavigationRail
|
||||||
import dev.msfjarvis.claw.android.ui.decorations.NavigationItem
|
import dev.msfjarvis.claw.android.ui.decorations.NavigationItem
|
||||||
|
@ -149,6 +151,14 @@ fun LobstersApp(
|
||||||
Text(text = stringResource(R.string.app_name), fontWeight = FontWeight.Bold)
|
Text(text = stringResource(R.string.app_name), fontWeight = FontWeight.Bold)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
actions = {
|
||||||
|
IconButton(onClick = { navController.navigate(Destinations.DataTransfer.route) }) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.ImportExport,
|
||||||
|
contentDescription = "Data transfer options"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
bottomBar = {
|
bottomBar = {
|
||||||
|
@ -237,6 +247,12 @@ fun LobstersApp(
|
||||||
getProfile = viewModel::getUserProfile,
|
getProfile = viewModel::getUserProfile,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
composable(route = Destinations.DataTransfer.route) {
|
||||||
|
DataTransferScreen(
|
||||||
|
context = context,
|
||||||
|
dataTransferRepository = viewModel.dataTransferRepository,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,116 @@
|
||||||
|
/*
|
||||||
|
* Copyright © 2023 Harsh Shandilya.
|
||||||
|
* Use of this source code is governed by an MIT-style
|
||||||
|
* license that can be found in the LICENSE file or at
|
||||||
|
* https://opensource.org/licenses/MIT.
|
||||||
|
*/
|
||||||
|
package dev.msfjarvis.claw.android.ui.datatransfer
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts.CreateDocument
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts.GetContent
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.NorthEast
|
||||||
|
import androidx.compose.material.icons.outlined.SouthWest
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.ListItem
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import dev.msfjarvis.claw.android.viewmodel.DataTransferRepository
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
private const val MIME_TYPE = "application/json"
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun DataTransferScreen(
|
||||||
|
context: Context,
|
||||||
|
dataTransferRepository: DataTransferRepository,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
Column(modifier = modifier) {
|
||||||
|
ImportOption(context, coroutineScope, dataTransferRepository)
|
||||||
|
ExportOption(context, coroutineScope, dataTransferRepository)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ImportOption(
|
||||||
|
context: Context,
|
||||||
|
coroutineScope: CoroutineScope,
|
||||||
|
dataTransferRepository: DataTransferRepository,
|
||||||
|
) {
|
||||||
|
val importAction =
|
||||||
|
rememberLauncherForActivityResult(GetContent()) { uri ->
|
||||||
|
if (uri == null) return@rememberLauncherForActivityResult
|
||||||
|
coroutineScope.launch {
|
||||||
|
context.contentResolver.openInputStream(uri)?.use { stream ->
|
||||||
|
dataTransferRepository.importPosts(stream)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SettingsActionItem(
|
||||||
|
title = "Import saved posts",
|
||||||
|
description = "Import saved posts from a previously generated export",
|
||||||
|
icon = Icons.Outlined.SouthWest,
|
||||||
|
) {
|
||||||
|
importAction.launch(MIME_TYPE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ExportOption(
|
||||||
|
context: Context,
|
||||||
|
coroutineScope: CoroutineScope,
|
||||||
|
dataTransferRepository: DataTransferRepository,
|
||||||
|
) {
|
||||||
|
val exportAction =
|
||||||
|
rememberLauncherForActivityResult(CreateDocument(MIME_TYPE)) { uri ->
|
||||||
|
if (uri == null) return@rememberLauncherForActivityResult
|
||||||
|
coroutineScope.launch {
|
||||||
|
context.contentResolver.openOutputStream(uri)?.use { stream ->
|
||||||
|
dataTransferRepository.exportPosts(stream)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SettingsActionItem(
|
||||||
|
title = "Export posts to file",
|
||||||
|
description = "Write all saved posts into a JSON file that can be imported at a later date",
|
||||||
|
icon = Icons.Outlined.NorthEast,
|
||||||
|
) {
|
||||||
|
exportAction.launch("claw-export.json")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun SettingsActionItem(
|
||||||
|
title: String,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
description: String? = null,
|
||||||
|
icon: ImageVector? = null,
|
||||||
|
onClick: (() -> Unit)? = null,
|
||||||
|
) {
|
||||||
|
ListItem(
|
||||||
|
headlineContent = { Text(title) },
|
||||||
|
supportingContent = { description?.let { Text(it) } },
|
||||||
|
leadingContent = {
|
||||||
|
icon?.let {
|
||||||
|
Icon(
|
||||||
|
imageVector = icon,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.height(32.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = modifier.clickable { onClick?.invoke() },
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2021-2022 Harsh Shandilya.
|
* Copyright © 2021-2023 Harsh Shandilya.
|
||||||
* Use of this source code is governed by an MIT-style
|
* Use of this source code is governed by an MIT-style
|
||||||
* license that can be found in the LICENSE file or at
|
* license that can be found in the LICENSE file or at
|
||||||
* https://opensource.org/licenses/MIT.
|
* https://opensource.org/licenses/MIT.
|
||||||
|
@ -31,6 +31,10 @@ sealed class Destinations {
|
||||||
override val route = "user/$placeholder"
|
override val route = "user/$placeholder"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object DataTransfer : Destinations() {
|
||||||
|
override val route: String = "datatransfer"
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val startDestination
|
val startDestination
|
||||||
get() = Hottest
|
get() = Hottest
|
||||||
|
|
|
@ -41,6 +41,7 @@ constructor(
|
||||||
private val savedPostsRepository: SavedPostsRepository,
|
private val savedPostsRepository: SavedPostsRepository,
|
||||||
private val commentsRepository: CommentsRepository,
|
private val commentsRepository: CommentsRepository,
|
||||||
private val linkMetadataRepository: LinkMetadataRepository,
|
private val linkMetadataRepository: LinkMetadataRepository,
|
||||||
|
val dataTransferRepository: DataTransferRepository,
|
||||||
private val pagingSourceFactory: LobstersPagingSource.Factory,
|
private val pagingSourceFactory: LobstersPagingSource.Factory,
|
||||||
@IODispatcher private val ioDispatcher: CoroutineDispatcher,
|
@IODispatcher private val ioDispatcher: CoroutineDispatcher,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue