mirror of
https://github.com/msfjarvis/compose-lobsters
synced 2025-08-17 22:37:03 +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.AvatarContentDescription -> R.string.avatar_content_description
|
||||||
Strings.ChangeSortingOrder -> R.string.change_sorting_order
|
Strings.ChangeSortingOrder -> R.string.change_sorting_order
|
||||||
Strings.HottestPosts -> R.string.hottest_posts
|
Strings.HottestPosts -> R.string.hottest_posts
|
||||||
|
Strings.NewestPosts -> R.string.newest_posts
|
||||||
Strings.NoSavedPost -> R.string.no_saved_posts
|
Strings.NoSavedPost -> R.string.no_saved_posts
|
||||||
Strings.OpenComments -> R.string.open_comments
|
Strings.OpenComments -> R.string.open_comments
|
||||||
Strings.RefreshPostsContentDescription -> R.string.refresh_posts_content_description
|
Strings.RefreshPostsContentDescription -> R.string.refresh_posts_content_description
|
||||||
Strings.RemoveFromSavedPosts -> R.string.remove_from_saved_posts
|
Strings.RemoveFromSavedPosts -> R.string.remove_from_saved_posts
|
||||||
Strings.SavedPosts -> R.string.saved_posts
|
Strings.SavedPosts -> R.string.saved_posts
|
||||||
Strings.SubmittedBy -> R.string.submitted_by
|
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="open_comments">Open comments</string>
|
||||||
<string name="change_sorting_order">Change sort order</string>
|
<string name="change_sorting_order">Change sort order</string>
|
||||||
<string name="newest_posts">Newest</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>
|
</resources>
|
||||||
|
|
|
@ -13,4 +13,9 @@ enum class Strings {
|
||||||
SavedPosts,
|
SavedPosts,
|
||||||
SubmittedBy,
|
SubmittedBy,
|
||||||
NewestPosts,
|
NewestPosts,
|
||||||
|
Settings,
|
||||||
|
SettingsBackup,
|
||||||
|
SettingsBackupDescription,
|
||||||
|
SettingsRestore,
|
||||||
|
SettingsRestoreDescription,
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,13 @@ private fun stringEnumMapper(stringEnum: Strings): String {
|
||||||
Strings.SavedPosts -> "Saved"
|
Strings.SavedPosts -> "Saved"
|
||||||
Strings.SubmittedBy -> "submitted by %1s"
|
Strings.SubmittedBy -> "submitted by %1s"
|
||||||
Strings.NewestPosts -> "Newest"
|
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