mirror of
https://github.com/msfjarvis/compose-lobsters
synced 2025-08-17 06:17:02 +05:30
common/app: add Settings UI composables
Co-authored-by: Harsh Shandilya <me@msfjarvis.dev> Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
This commit is contained in:
parent
a08109d745
commit
ef09b241ea
6 changed files with 176 additions and 1 deletions
|
@ -0,0 +1,58 @@
|
|||
package dev.msfjarvis.lobsters.ui.settings
|
||||
|
||||
import android.content.Context
|
||||
import androidx.activity.compose.registerForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.runtime.Composable
|
||||
import dev.msfjarvis.lobsters.data.backup.BackupHandler
|
||||
import dev.msfjarvis.lobsters.utils.Strings
|
||||
import dev.msfjarvis.lobsters.utils.get
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
private const val JSON_MINE = "application/json"
|
||||
|
||||
@Composable
|
||||
fun BackupOption(
|
||||
context: Context,
|
||||
backupHandler: BackupHandler,
|
||||
coroutineScope: CoroutineScope,
|
||||
) {
|
||||
val result =
|
||||
registerForActivityResult(ActivityResultContracts.CreateDocument()) { uri ->
|
||||
if (uri == null) return@registerForActivityResult
|
||||
context.contentResolver.openOutputStream(uri)?.use {
|
||||
coroutineScope.launch(Dispatchers.IO) {
|
||||
it.write(backupHandler.exportSavedPosts().toByteArray(Charsets.UTF_8))
|
||||
}
|
||||
}
|
||||
}
|
||||
SettingsActionItem(
|
||||
Strings.SettingsBackup.get(),
|
||||
Strings.SettingsBackupDescription.get(),
|
||||
onClick = { result.launch("Claw-export.json") }
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RestoreOption(
|
||||
context: Context,
|
||||
backupHandler: BackupHandler,
|
||||
coroutineScope: CoroutineScope,
|
||||
) {
|
||||
val result =
|
||||
registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
|
||||
if (uri == null) return@registerForActivityResult
|
||||
context.contentResolver.openInputStream(uri)?.use {
|
||||
coroutineScope.launch(Dispatchers.IO) {
|
||||
backupHandler.importSavedPosts(it.readBytes().toString(Charsets.UTF_8))
|
||||
}
|
||||
}
|
||||
}
|
||||
SettingsActionItem(
|
||||
title = Strings.SettingsRestore.get(),
|
||||
description = Strings.SettingsRestoreDescription.get(),
|
||||
onClick = { result.launch(JSON_MINE) }
|
||||
)
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
package dev.msfjarvis.lobsters.ui.settings
|
||||
|
||||
import android.content.Context
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.ListItem
|
||||
import androidx.compose.material.Scaffold
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TopAppBar
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
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.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.msfjarvis.lobsters.data.backup.BackupHandler
|
||||
import dev.msfjarvis.lobsters.utils.Strings
|
||||
import dev.msfjarvis.lobsters.utils.get
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
||||
@Composable
|
||||
fun LobstersSettings(
|
||||
backupHandler: BackupHandler,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
Scaffold(
|
||||
topBar = { SettingsTopBar(context) },
|
||||
content = { SettingsBody(context, backupHandler, scope) },
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SettingsTopBar(
|
||||
context: Context,
|
||||
) {
|
||||
TopAppBar(
|
||||
title = { Text(Strings.Settings.get()) },
|
||||
navigationIcon = {
|
||||
Icon(
|
||||
Icons.Default.ArrowBack,
|
||||
contentDescription = Strings.Settings.get(),
|
||||
modifier =
|
||||
Modifier.padding(start = 16.dp).clickable { (context as ComponentActivity).finish() },
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SettingsBody(
|
||||
context: Context,
|
||||
backupHandler: BackupHandler,
|
||||
scope: CoroutineScope,
|
||||
) {
|
||||
LazyColumn {
|
||||
item {
|
||||
BackupOption(
|
||||
context,
|
||||
backupHandler,
|
||||
scope,
|
||||
)
|
||||
}
|
||||
item {
|
||||
RestoreOption(
|
||||
context,
|
||||
backupHandler,
|
||||
scope,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SettingsActionItem(
|
||||
title: String,
|
||||
description: String? = null,
|
||||
singleLineDescription: Boolean = true,
|
||||
icon: ImageVector? = null,
|
||||
onClick: (() -> Unit)? = null,
|
||||
) {
|
||||
ListItem(
|
||||
text = { Text(title) },
|
||||
secondaryText = { description?.let { Text(it) } },
|
||||
icon = { icon?.let { Icon(icon, null, Modifier.height(32.dp)) } },
|
||||
singleLineSecondaryText = singleLineDescription,
|
||||
modifier = Modifier.clickable { onClick?.invoke() },
|
||||
)
|
||||
}
|
|
@ -11,13 +11,18 @@ private fun stringEnumMapper(stringEnum: Strings): Int {
|
|||
Strings.AvatarContentDescription -> R.string.avatar_content_description
|
||||
Strings.ChangeSortingOrder -> R.string.change_sorting_order
|
||||
Strings.HottestPosts -> R.string.hottest_posts
|
||||
Strings.NewestPosts -> R.string.newest_posts
|
||||
Strings.NoSavedPost -> R.string.no_saved_posts
|
||||
Strings.OpenComments -> R.string.open_comments
|
||||
Strings.RefreshPostsContentDescription -> R.string.refresh_posts_content_description
|
||||
Strings.RemoveFromSavedPosts -> R.string.remove_from_saved_posts
|
||||
Strings.SavedPosts -> R.string.saved_posts
|
||||
Strings.SubmittedBy -> R.string.submitted_by
|
||||
Strings.NewestPosts -> R.string.newest_posts
|
||||
Strings.Settings -> R.string.settings
|
||||
Strings.SettingsBackup -> R.string.settings_backup
|
||||
Strings.SettingsBackupDescription -> R.string.settings_backup_desc
|
||||
Strings.SettingsRestore -> R.string.settings_restore
|
||||
Strings.SettingsRestoreDescription -> R.string.settings_restore_desc
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,4 +11,9 @@
|
|||
<string name="open_comments">Open comments</string>
|
||||
<string name="change_sorting_order">Change sort order</string>
|
||||
<string name="newest_posts">Newest</string>
|
||||
<string name="settings">Settings</string>
|
||||
<string name="settings_backup">Backup saved posts</string>
|
||||
<string name="settings_backup_desc">Export saved posts in a JSON file that can be restored later</string>
|
||||
<string name="settings_restore">Restore saved posts</string>
|
||||
<string name="settings_restore_desc">Import a previously exported copy of saved posts. Existing saved posts are not cleared</string>
|
||||
</resources>
|
||||
|
|
|
@ -13,4 +13,9 @@ enum class Strings {
|
|||
SavedPosts,
|
||||
SubmittedBy,
|
||||
NewestPosts,
|
||||
Settings,
|
||||
SettingsBackup,
|
||||
SettingsBackupDescription,
|
||||
SettingsRestore,
|
||||
SettingsRestoreDescription,
|
||||
}
|
||||
|
|
|
@ -16,6 +16,13 @@ private fun stringEnumMapper(stringEnum: Strings): String {
|
|||
Strings.SavedPosts -> "Saved"
|
||||
Strings.SubmittedBy -> "submitted by %1s"
|
||||
Strings.NewestPosts -> "Newest"
|
||||
Strings.Settings -> "Settings"
|
||||
Strings.SettingsBackup -> "Backup saved posts"
|
||||
Strings.SettingsBackupDescription ->
|
||||
"Export saved posts in a JSON file that can be restored later"
|
||||
Strings.SettingsRestore -> "Restore saved posts"
|
||||
Strings.SettingsRestoreDescription ->
|
||||
"Import a previously exported copy of saved posts. Existing saved posts are not cleared"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue