mirror of
https://github.com/msfjarvis/compose-lobsters
synced 2025-08-14 22:17:03 +05:30
chore(deps): update dependency com.facebook:ktfmt to v0.47
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
This commit is contained in:
parent
050007ff1b
commit
a187752659
33 changed files with 118 additions and 290 deletions
|
@ -42,16 +42,8 @@ abstract class BaseActivity : ComponentActivity() {
|
||||||
preLaunch()
|
preLaunch()
|
||||||
Whetstone.inject(this)
|
Whetstone.inject(this)
|
||||||
enableEdgeToEdge(
|
enableEdgeToEdge(
|
||||||
statusBarStyle =
|
statusBarStyle = SystemBarStyle.light(Color.TRANSPARENT, Color.TRANSPARENT),
|
||||||
SystemBarStyle.light(
|
navigationBarStyle = SystemBarStyle.light(Color.TRANSPARENT, Color.TRANSPARENT),
|
||||||
Color.TRANSPARENT,
|
|
||||||
Color.TRANSPARENT,
|
|
||||||
),
|
|
||||||
navigationBarStyle =
|
|
||||||
SystemBarStyle.light(
|
|
||||||
Color.TRANSPARENT,
|
|
||||||
Color.TRANSPARENT,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
setContent {
|
setContent {
|
||||||
LobstersTheme(
|
LobstersTheme(
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2023 Harsh Shandilya.
|
* Copyright © 2023-2024 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.
|
||||||
|
@ -30,9 +30,7 @@ import dev.msfjarvis.claw.common.theme.DarkThemeColors
|
||||||
import dev.msfjarvis.claw.common.theme.LightThemeColors
|
import dev.msfjarvis.claw.common.theme.LightThemeColors
|
||||||
import dev.msfjarvis.claw.database.local.SavedPost
|
import dev.msfjarvis.claw.database.local.SavedPost
|
||||||
|
|
||||||
class SavedPostsWidget(
|
class SavedPostsWidget(private val posts: List<SavedPost>) : GlanceAppWidget() {
|
||||||
private val posts: List<SavedPost>,
|
|
||||||
) : GlanceAppWidget() {
|
|
||||||
override suspend fun provideGlance(context: Context, id: GlanceId) {
|
override suspend fun provideGlance(context: Context, id: GlanceId) {
|
||||||
provideContent {
|
provideContent {
|
||||||
GlanceTheme(
|
GlanceTheme(
|
||||||
|
@ -51,7 +49,7 @@ class SavedPostsWidget(
|
||||||
GlanceModifier.fillMaxSize()
|
GlanceModifier.fillMaxSize()
|
||||||
.background(GlanceTheme.colors.background)
|
.background(GlanceTheme.colors.background)
|
||||||
.appWidgetBackground(),
|
.appWidgetBackground(),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
) {
|
) {
|
||||||
item {
|
item {
|
||||||
val style = MaterialTheme.typography.titleLarge
|
val style = MaterialTheme.typography.titleLarge
|
||||||
|
|
|
@ -39,10 +39,7 @@ private val destinationKey = Key<String>(NAVIGATION_KEY)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@GlanceComposable
|
@GlanceComposable
|
||||||
fun WidgetListEntry(
|
fun WidgetListEntry(post: SavedPost, modifier: GlanceModifier = GlanceModifier) {
|
||||||
post: SavedPost,
|
|
||||||
modifier: GlanceModifier = GlanceModifier,
|
|
||||||
) {
|
|
||||||
val titleStyle = MaterialTheme.typography.titleMedium
|
val titleStyle = MaterialTheme.typography.titleMedium
|
||||||
val commentsAction =
|
val commentsAction =
|
||||||
actionStartActivity<MainActivity>(actionParametersOf(destinationKey to post.shortId))
|
actionStartActivity<MainActivity>(actionParametersOf(destinationKey to post.shortId))
|
||||||
|
@ -67,12 +64,12 @@ fun WidgetListEntry(
|
||||||
fontSize = titleStyle.fontSize,
|
fontSize = titleStyle.fontSize,
|
||||||
fontWeight = titleStyle.fontWeight.toGlance(),
|
fontWeight = titleStyle.fontWeight.toGlance(),
|
||||||
fontStyle = titleStyle.fontStyle.toGlance(),
|
fontStyle = titleStyle.fontStyle.toGlance(),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
Image(
|
Image(
|
||||||
provider = ImageProvider(R.drawable.ic_comment),
|
provider = ImageProvider(R.drawable.ic_comment),
|
||||||
contentDescription = "${post.commentCount ?: 0} comments",
|
contentDescription = "${post.commentCount ?: 0} comments",
|
||||||
modifier = GlanceModifier.padding(end = 4.dp).clickable(commentsAction)
|
modifier = GlanceModifier.padding(end = 4.dp).clickable(commentsAction),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2021-2023 Harsh Shandilya.
|
* Copyright © 2021-2024 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.
|
||||||
|
@ -32,13 +32,7 @@ object MetadataExtractorModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
fun provideUnfurler(
|
fun provideUnfurler(okHttpClient: OkHttpClient, logger: UnfurlLogger): Unfurler {
|
||||||
okHttpClient: OkHttpClient,
|
return Unfurler(httpClient = okHttpClient, logger = logger)
|
||||||
logger: UnfurlLogger,
|
|
||||||
): Unfurler {
|
|
||||||
return Unfurler(
|
|
||||||
httpClient = okHttpClient,
|
|
||||||
logger = logger,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2022 Harsh Shandilya.
|
* Copyright © 2022-2024 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.
|
||||||
|
@ -17,18 +17,14 @@ import dev.msfjarvis.claw.core.injection.AppPlugin
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ContributesMultibinding(ApplicationScope::class)
|
@ContributesMultibinding(ApplicationScope::class)
|
||||||
class WorkManagerPlugin
|
class WorkManagerPlugin @Inject constructor(private val workerFactory: WorkerFactory) : AppPlugin {
|
||||||
@Inject
|
|
||||||
constructor(
|
|
||||||
private val workerFactory: WorkerFactory,
|
|
||||||
) : AppPlugin {
|
|
||||||
override fun apply(application: Application) {
|
override fun apply(application: Application) {
|
||||||
WorkManager.initialize(
|
WorkManager.initialize(
|
||||||
application,
|
application,
|
||||||
Configuration.Builder()
|
Configuration.Builder()
|
||||||
.setWorkerFactory(workerFactory)
|
.setWorkerFactory(workerFactory)
|
||||||
.setMinimumLoggingLevel(Log.DEBUG)
|
.setMinimumLoggingLevel(Log.DEBUG)
|
||||||
.build()
|
.build(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2023 Harsh Shandilya.
|
* Copyright © 2023-2024 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.
|
||||||
|
@ -145,11 +145,7 @@ private fun GenericExportOption(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SettingsActionItem(
|
SettingsActionItem(title = title, description = description, icon = icon) {
|
||||||
title = title,
|
|
||||||
description = description,
|
|
||||||
icon = icon,
|
|
||||||
) {
|
|
||||||
exportAction.launch(fileName)
|
exportAction.launch(fileName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -167,11 +163,7 @@ private fun SettingsActionItem(
|
||||||
supportingContent = { description?.let { Text(it) } },
|
supportingContent = { description?.let { Text(it) } },
|
||||||
leadingContent = {
|
leadingContent = {
|
||||||
icon?.let {
|
icon?.let {
|
||||||
Icon(
|
Icon(imageVector = icon, contentDescription = null, modifier = Modifier.height(32.dp))
|
||||||
imageVector = icon,
|
|
||||||
contentDescription = null,
|
|
||||||
modifier = Modifier.height(32.dp),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
modifier = modifier.clickable { onClick?.invoke() },
|
modifier = modifier.clickable { onClick?.invoke() },
|
||||||
|
@ -179,9 +171,7 @@ private fun SettingsActionItem(
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Shows a Snackbar but dismisses any existing ones first. */
|
/** Shows a Snackbar but dismisses any existing ones first. */
|
||||||
private suspend fun SnackbarHostState.showSnackbarDismissing(
|
private suspend fun SnackbarHostState.showSnackbarDismissing(text: String) {
|
||||||
text: String,
|
|
||||||
) {
|
|
||||||
currentSnackbarData?.dismiss()
|
currentSnackbarData?.dismiss()
|
||||||
showSnackbar(text)
|
showSnackbar(text)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2021-2023 Harsh Shandilya.
|
* Copyright © 2021-2024 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.
|
||||||
|
@ -46,9 +46,5 @@ fun ClawAppBar(
|
||||||
@ThemePreviews
|
@ThemePreviews
|
||||||
@Composable
|
@Composable
|
||||||
fun ClawAppBarPreview() {
|
fun ClawAppBarPreview() {
|
||||||
LobstersTheme {
|
LobstersTheme { ClawAppBar(title = { Text("Claw", fontWeight = FontWeight.Bold) }) }
|
||||||
ClawAppBar(
|
|
||||||
title = { Text("Claw", fontWeight = FontWeight.Bold) },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,11 +53,7 @@ fun DatabasePosts(
|
||||||
LazyColumn(state = listState) {
|
LazyColumn(state = listState) {
|
||||||
items.forEach { (month, posts) ->
|
items.forEach { (month, posts) ->
|
||||||
stickyHeader(contentType = "month-header") { MonthHeader(label = month) }
|
stickyHeader(contentType = "month-header") { MonthHeader(label = month) }
|
||||||
items(
|
items(items = posts, key = { it.shortId }, contentType = { "LobstersItem" }) { item ->
|
||||||
items = posts,
|
|
||||||
key = { it.shortId },
|
|
||||||
contentType = { "LobstersItem" },
|
|
||||||
) { item ->
|
|
||||||
LobstersListItem(
|
LobstersListItem(
|
||||||
item = item,
|
item = item,
|
||||||
isSaved = { true },
|
isSaved = { true },
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2021-2023 Harsh Shandilya.
|
* Copyright © 2021-2024 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.
|
||||||
|
@ -35,9 +35,7 @@ fun LobstersListItem(
|
||||||
background = MaterialTheme.colorScheme.tertiary,
|
background = MaterialTheme.colorScheme.tertiary,
|
||||||
onSwipe = { postActions.viewCommentsPage(item.commentsUrl) },
|
onSwipe = { postActions.viewCommentsPage(item.commentsUrl) },
|
||||||
)
|
)
|
||||||
SwipeableActionsBox(
|
SwipeableActionsBox(endActions = listOf(commentsAction)) {
|
||||||
endActions = listOf(commentsAction),
|
|
||||||
) {
|
|
||||||
LobstersCard(
|
LobstersCard(
|
||||||
post = item,
|
post = item,
|
||||||
isSaved = isSaved(item),
|
isSaved = isSaved(item),
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2021-2023 Harsh Shandilya.
|
* Copyright © 2021-2024 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.
|
||||||
|
@ -56,9 +56,7 @@ fun NetworkPosts(
|
||||||
modifier = Modifier.align(Alignment.Center),
|
modifier = Modifier.align(Alignment.Center),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
LazyColumn(
|
LazyColumn(state = listState) {
|
||||||
state = listState,
|
|
||||||
) {
|
|
||||||
items(
|
items(
|
||||||
count = lazyPagingItems.itemCount,
|
count = lazyPagingItems.itemCount,
|
||||||
key = lazyPagingItems.itemKey { it.shortId },
|
key = lazyPagingItems.itemKey { it.shortId },
|
||||||
|
@ -83,7 +81,7 @@ fun NetworkPosts(
|
||||||
modifier =
|
modifier =
|
||||||
Modifier.fillMaxWidth()
|
Modifier.fillMaxWidth()
|
||||||
.wrapContentWidth(Alignment.CenterHorizontally)
|
.wrapContentWidth(Alignment.CenterHorizontally)
|
||||||
.padding(vertical = 16.dp),
|
.padding(vertical = 16.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2023 Harsh Shandilya.
|
* Copyright © 2023-2024 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.
|
||||||
|
@ -20,9 +20,7 @@ import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
|
|
||||||
@Suppress("Unused")
|
@Suppress("Unused")
|
||||||
@Composable
|
@Composable
|
||||||
fun LobstersLoginWebView(
|
fun LobstersLoginWebView(modifier: Modifier = Modifier) {
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
val state = rememberWebViewState(url = "https://lobste.rs/login")
|
val state = rememberWebViewState(url = "https://lobste.rs/login")
|
||||||
val webClient = remember {
|
val webClient = remember {
|
||||||
object : AccompanistWebViewClient() {
|
object : AccompanistWebViewClient() {
|
||||||
|
@ -43,10 +41,5 @@ fun LobstersLoginWebView(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WebView(
|
WebView(state = state, client = webClient, modifier = modifier, captureBackPresses = false)
|
||||||
state = state,
|
|
||||||
client = webClient,
|
|
||||||
modifier = modifier,
|
|
||||||
captureBackPresses = false,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2021-2023 Harsh Shandilya.
|
* Copyright © 2021-2024 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.
|
||||||
|
@ -162,7 +162,7 @@ fun LobstersPostsScreen(
|
||||||
navItems.none { it.route == currentDestination }
|
navItems.none { it.route == currentDestination }
|
||||||
) {
|
) {
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = { if (!navController.popBackStack()) context.getActivity()?.finish() },
|
onClick = { if (!navController.popBackStack()) context.getActivity()?.finish() }
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||||
|
@ -202,7 +202,7 @@ fun LobstersPostsScreen(
|
||||||
imageVector = Icons.Filled.ImportExport,
|
imageVector = Icons.Filled.ImportExport,
|
||||||
contentDescription = "Data transfer options",
|
contentDescription = "Data transfer options",
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = { Text("Search posts") },
|
text = { Text("Search posts") },
|
||||||
|
@ -211,11 +211,8 @@ fun LobstersPostsScreen(
|
||||||
expanded = false
|
expanded = false
|
||||||
},
|
},
|
||||||
leadingIcon = {
|
leadingIcon = {
|
||||||
Icon(
|
Icon(imageVector = Icons.Filled.Search, contentDescription = "Search posts")
|
||||||
imageVector = Icons.Filled.Search,
|
},
|
||||||
contentDescription = "Search posts",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -273,11 +270,7 @@ fun LobstersPostsScreen(
|
||||||
}
|
}
|
||||||
composable(route = Destinations.Saved.route) {
|
composable(route = Destinations.Saved.route) {
|
||||||
setWebUri(null)
|
setWebUri(null)
|
||||||
DatabasePosts(
|
DatabasePosts(items = savedPosts, listState = savedListState, postActions = postActions)
|
||||||
items = savedPosts,
|
|
||||||
listState = savedListState,
|
|
||||||
postActions = postActions,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
composable(
|
composable(
|
||||||
route = Destinations.Comments.route,
|
route = Destinations.Comments.route,
|
||||||
|
@ -304,18 +297,14 @@ fun LobstersPostsScreen(
|
||||||
composable(
|
composable(
|
||||||
route = Destinations.User.route,
|
route = Destinations.User.route,
|
||||||
arguments = listOf(navArgument("username") { type = NavType.StringType }),
|
arguments = listOf(navArgument("username") { type = NavType.StringType }),
|
||||||
deepLinks =
|
deepLinks = listOf(navDeepLink { uriPattern = "$uri/u/${Destinations.User.PLACEHOLDER}" }),
|
||||||
listOf(navDeepLink { uriPattern = "$uri/u/${Destinations.User.PLACEHOLDER}" }),
|
|
||||||
) { backStackEntry ->
|
) { backStackEntry ->
|
||||||
val username =
|
val username =
|
||||||
requireNotNull(backStackEntry.arguments?.getString("username")) {
|
requireNotNull(backStackEntry.arguments?.getString("username")) {
|
||||||
"Navigating to ${Destinations.User.route} without necessary 'username' argument"
|
"Navigating to ${Destinations.User.route} without necessary 'username' argument"
|
||||||
}
|
}
|
||||||
setWebUri("https://lobste.rs/u/$username")
|
setWebUri("https://lobste.rs/u/$username")
|
||||||
UserProfile(
|
UserProfile(username = username, getProfile = viewModel::getUserProfile)
|
||||||
username = username,
|
|
||||||
getProfile = viewModel::getUserProfile,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
composable(route = Destinations.DataTransfer.route) {
|
composable(route = Destinations.DataTransfer.route) {
|
||||||
DataTransferScreen(
|
DataTransferScreen(
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2023 Harsh Shandilya.
|
* Copyright © 2023-2024 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.
|
||||||
|
@ -37,12 +37,7 @@ constructor(
|
||||||
|
|
||||||
suspend fun importPosts(input: InputStream) {
|
suspend fun importPosts(input: InputStream) {
|
||||||
val posts: List<SavedPost> =
|
val posts: List<SavedPost> =
|
||||||
withContext(ioDispatcher) {
|
withContext(ioDispatcher) { json.decodeFromStream(serializer, input) }
|
||||||
json.decodeFromStream(
|
|
||||||
serializer,
|
|
||||||
input,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
withContext(dbDispatcher) {
|
withContext(dbDispatcher) {
|
||||||
savedPostQueries.transaction { posts.forEach(savedPostQueries::insertOrReplacePost) }
|
savedPostQueries.transaction { posts.forEach(savedPostQueries::insertOrReplacePost) }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2023 Harsh Shandilya.
|
* Copyright © 2023-2024 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.
|
||||||
|
@ -77,7 +77,7 @@ object SearchConverter : Converter<ResponseBody, List<LobstersPost>> {
|
||||||
override fun responseBodyConverter(
|
override fun responseBodyConverter(
|
||||||
type: Type,
|
type: Type,
|
||||||
annotations: Array<out Annotation>,
|
annotations: Array<out Annotation>,
|
||||||
retrofit: Retrofit
|
retrofit: Retrofit,
|
||||||
): Converter<ResponseBody, List<LobstersPost>> {
|
): Converter<ResponseBody, List<LobstersPost>> {
|
||||||
return SearchConverter
|
return SearchConverter
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2023 Harsh Shandilya.
|
* Copyright © 2023-2024 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.
|
||||||
|
@ -91,10 +91,7 @@ object RetrofitModule {
|
||||||
@Provides
|
@Provides
|
||||||
@SearchApi
|
@SearchApi
|
||||||
fun provideConverters(): List<@JvmSuppressWildcards Converter.Factory> =
|
fun provideConverters(): List<@JvmSuppressWildcards Converter.Factory> =
|
||||||
listOf(
|
listOf(ApiResultConverterFactory, SearchConverter.Factory)
|
||||||
ApiResultConverterFactory,
|
|
||||||
SearchConverter.Factory,
|
|
||||||
)
|
|
||||||
|
|
||||||
@Provides @Named("LobstersURL") fun provideLobstersUrl(): String = LobstersApi.BASE_URL
|
@Provides @Named("LobstersURL") fun provideLobstersUrl(): String = LobstersApi.BASE_URL
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2022-2023 Harsh Shandilya.
|
* Copyright © 2022-2024 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.
|
||||||
|
@ -43,10 +43,7 @@ class KotlinCommonPlugin : Plugin<Project> {
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val ADDITIONAL_COMPILER_ARGS =
|
private val ADDITIONAL_COMPILER_ARGS = listOf("-opt-in=kotlin.RequiresOptIn")
|
||||||
listOf(
|
|
||||||
"-opt-in=kotlin.RequiresOptIn",
|
|
||||||
)
|
|
||||||
|
|
||||||
val JVM_TOOLCHAIN_ACTION =
|
val JVM_TOOLCHAIN_ACTION =
|
||||||
Action<JavaToolchainSpec> { languageVersion.set(JavaLanguageVersion.of(17)) }
|
Action<JavaToolchainSpec> { languageVersion.set(JavaLanguageVersion.of(17)) }
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2022-2023 Harsh Shandilya.
|
* Copyright © 2022-2024 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.
|
||||||
|
@ -45,7 +45,7 @@ class SpotlessPlugin : Plugin<Project> {
|
||||||
endWithNewline()
|
endWithNewline()
|
||||||
licenseHeaderFile(
|
licenseHeaderFile(
|
||||||
project.file("spotless/license.xml"),
|
project.file("spotless/license.xml"),
|
||||||
"<(adaptive-icon|appwidget-provider|data-extraction-rules|full-backup-content|manifest|vector|resources)"
|
"<(adaptive-icon|appwidget-provider|data-extraction-rules|full-backup-content|manifest|vector|resources)",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,6 +55,6 @@ class SpotlessPlugin : Plugin<Project> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
private const val KTFMT_VERSION = "0.46"
|
private const val KTFMT_VERSION = "0.47"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2021-2023 Harsh Shandilya.
|
* Copyright © 2021-2024 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.
|
||||||
|
@ -60,9 +60,7 @@ internal fun CommentsHeader(
|
||||||
) {
|
) {
|
||||||
val uriHandler = LocalUriHandler.current
|
val uriHandler = LocalUriHandler.current
|
||||||
val linkMetadata by
|
val linkMetadata by
|
||||||
produceState(
|
produceState(initialValue = LinkMetadata(postDetails.url, null)) {
|
||||||
initialValue = LinkMetadata(postDetails.url, null),
|
|
||||||
) {
|
|
||||||
runSuspendCatching { postActions.getLinkMetadata(postDetails.url) }
|
runSuspendCatching { postActions.getLinkMetadata(postDetails.url) }
|
||||||
.onSuccess { metadata -> value = metadata }
|
.onSuccess { metadata -> value = metadata }
|
||||||
}
|
}
|
||||||
|
@ -105,10 +103,7 @@ internal fun CommentsHeader(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun PostLink(
|
private fun PostLink(linkMetadata: LinkMetadata, modifier: Modifier = Modifier) {
|
||||||
linkMetadata: LinkMetadata,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
Box(
|
Box(
|
||||||
modifier.background(
|
modifier.background(
|
||||||
color = MaterialTheme.colorScheme.secondary,
|
color = MaterialTheme.colorScheme.secondary,
|
||||||
|
@ -155,7 +150,7 @@ internal fun CommentEntry(
|
||||||
end = CommentEntryPadding,
|
end = CommentEntryPadding,
|
||||||
top = CommentEntryPadding,
|
top = CommentEntryPadding,
|
||||||
bottom = CommentEntryPadding,
|
bottom = CommentEntryPadding,
|
||||||
),
|
)
|
||||||
) {
|
) {
|
||||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||||
Submitter(
|
Submitter(
|
||||||
|
@ -175,7 +170,7 @@ internal fun CommentEntry(
|
||||||
if (commentNode.isExpanded) {
|
if (commentNode.isExpanded) {
|
||||||
ThemedRichText(
|
ThemedRichText(
|
||||||
text = htmlConverter.convertHTMLToMarkdown(comment.comment),
|
text = htmlConverter.convertHTMLToMarkdown(comment.comment),
|
||||||
modifier = Modifier.padding(top = 8.dp)
|
modifier = Modifier.padding(top = 8.dp),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -228,11 +223,7 @@ fun buildCommenterString(
|
||||||
if (isUnread) {
|
if (isUnread) {
|
||||||
append(' ')
|
append(' ')
|
||||||
withStyle(
|
withStyle(
|
||||||
style =
|
style = SpanStyle(fontWeight = FontWeight.Bold, color = MaterialTheme.colorScheme.error)
|
||||||
SpanStyle(
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
color = MaterialTheme.colorScheme.error,
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
append("(unread)")
|
append("(unread)")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2022-2023 Harsh Shandilya.
|
* Copyright © 2022-2024 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.
|
||||||
|
@ -17,7 +17,7 @@ internal data class CommentNode(
|
||||||
val children: MutableList<CommentNode> = mutableListOf(),
|
val children: MutableList<CommentNode> = mutableListOf(),
|
||||||
val isUnread: Boolean = false,
|
val isUnread: Boolean = false,
|
||||||
var isExpanded: Boolean = true,
|
var isExpanded: Boolean = true,
|
||||||
var indentLevel: Int
|
var indentLevel: Int,
|
||||||
) {
|
) {
|
||||||
fun addChild(child: CommentNode) {
|
fun addChild(child: CommentNode) {
|
||||||
if (comment.shortId == child.comment.parentComment) {
|
if (comment.shortId == child.comment.parentComment) {
|
||||||
|
@ -32,7 +32,7 @@ internal data class CommentNode(
|
||||||
|
|
||||||
internal fun createListNode(
|
internal fun createListNode(
|
||||||
comments: List<Comment>,
|
comments: List<Comment>,
|
||||||
commentState: PostComments?
|
commentState: PostComments?,
|
||||||
): MutableList<CommentNode> {
|
): MutableList<CommentNode> {
|
||||||
val commentNodes = mutableListOf<CommentNode>()
|
val commentNodes = mutableListOf<CommentNode>()
|
||||||
val isUnread = { id: String -> commentState?.commentIds?.contains(id) == false }
|
val isUnread = { id: String -> commentState?.commentIds?.contains(id) == false }
|
||||||
|
@ -43,7 +43,7 @@ internal fun createListNode(
|
||||||
CommentNode(
|
CommentNode(
|
||||||
comment = comments[i],
|
comment = comments[i],
|
||||||
isUnread = isUnread(comments[i].shortId),
|
isUnread = isUnread(comments[i].shortId),
|
||||||
indentLevel = 1
|
indentLevel = 1,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -52,7 +52,7 @@ internal fun createListNode(
|
||||||
CommentNode(
|
CommentNode(
|
||||||
comment = comments[i],
|
comment = comments[i],
|
||||||
isUnread = isUnread(comments[i].shortId),
|
isUnread = isUnread(comments[i].shortId),
|
||||||
indentLevel = it.indentLevel + 1
|
indentLevel = it.indentLevel + 1,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -86,11 +86,7 @@ internal fun LazyListScope.nodes(
|
||||||
toggleExpanded: (CommentNode) -> Unit,
|
toggleExpanded: (CommentNode) -> Unit,
|
||||||
) {
|
) {
|
||||||
nodes.forEach { node ->
|
nodes.forEach { node ->
|
||||||
node(
|
node(node = node, htmlConverter = htmlConverter, toggleExpanded = toggleExpanded)
|
||||||
node = node,
|
|
||||||
htmlConverter = htmlConverter,
|
|
||||||
toggleExpanded = toggleExpanded,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,18 +100,10 @@ private fun LazyListScope.node(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
item {
|
item {
|
||||||
CommentEntry(
|
CommentEntry(commentNode = node, htmlConverter = htmlConverter, toggleExpanded = toggleExpanded)
|
||||||
commentNode = node,
|
|
||||||
htmlConverter = htmlConverter,
|
|
||||||
toggleExpanded = toggleExpanded,
|
|
||||||
)
|
|
||||||
HorizontalDivider()
|
HorizontalDivider()
|
||||||
}
|
}
|
||||||
if (node.children.isNotEmpty()) {
|
if (node.children.isNotEmpty()) {
|
||||||
nodes(
|
nodes(node.children, htmlConverter = htmlConverter, toggleExpanded = toggleExpanded)
|
||||||
node.children,
|
|
||||||
htmlConverter = htmlConverter,
|
|
||||||
toggleExpanded = toggleExpanded,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2021-2023 Harsh Shandilya.
|
* Copyright © 2021-2024 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.
|
||||||
|
@ -73,17 +73,13 @@ fun LobstersCard(
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.clickable { postActions.viewPost(post.shortId, post.url, post.commentsUrl) }
|
.clickable { postActions.viewPost(post.shortId, post.url, post.commentsUrl) }
|
||||||
.background(MaterialTheme.colorScheme.background)
|
.background(MaterialTheme.colorScheme.background)
|
||||||
.padding(start = 16.dp, top = 16.dp, end = 4.dp, bottom = 16.dp),
|
.padding(start = 16.dp, top = 16.dp, end = 4.dp, bottom = 16.dp)
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
PostDetails(
|
PostDetails(modifier = Modifier.weight(1f), post = post, isRead = isRead)
|
||||||
modifier = Modifier.weight(1f),
|
|
||||||
post = post,
|
|
||||||
isRead = isRead,
|
|
||||||
)
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.wrapContentHeight(),
|
modifier = Modifier.wrapContentHeight(),
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
@ -126,11 +122,7 @@ fun PostDetails(post: SavedPost, isRead: Boolean, modifier: Modifier = Modifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun PostTitle(
|
internal fun PostTitle(title: String, isRead: Boolean, modifier: Modifier = Modifier) {
|
||||||
title: String,
|
|
||||||
isRead: Boolean,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
Text(
|
Text(
|
||||||
text = title,
|
text = title,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
|
@ -169,10 +161,7 @@ internal fun Submitter(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun SaveButton(
|
private fun SaveButton(isSaved: Boolean, modifier: Modifier = Modifier) {
|
||||||
isSaved: Boolean,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
Crossfade(targetState = isSaved, label = "save-button") { saved ->
|
Crossfade(targetState = isSaved, label = "save-button") { saved ->
|
||||||
Box(modifier = modifier.padding(12.dp)) {
|
Box(modifier = modifier.padding(12.dp)) {
|
||||||
Icon(
|
Icon(
|
||||||
|
@ -186,10 +175,7 @@ private fun SaveButton(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun CommentsButton(
|
private fun CommentsButton(commentCount: Int?, modifier: Modifier = Modifier) {
|
||||||
commentCount: Int?,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
BadgedBox(
|
BadgedBox(
|
||||||
modifier = modifier.padding(12.dp),
|
modifier = modifier.padding(12.dp),
|
||||||
badge = {
|
badge = {
|
||||||
|
@ -219,10 +205,7 @@ private fun CommentsButton(
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@OptIn(ExperimentalLayoutApi::class)
|
@OptIn(ExperimentalLayoutApi::class)
|
||||||
internal fun TagRow(
|
internal fun TagRow(tags: ImmutableList<String>, modifier: Modifier = Modifier) {
|
||||||
tags: ImmutableList<String>,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
FlowRow(
|
FlowRow(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
@ -233,10 +216,7 @@ internal fun TagRow(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun TagText(
|
private fun TagText(tag: String, modifier: Modifier = Modifier) {
|
||||||
tag: String,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
Text(
|
Text(
|
||||||
text = tag,
|
text = tag,
|
||||||
modifier =
|
modifier =
|
||||||
|
|
|
@ -23,10 +23,7 @@ import dev.msfjarvis.claw.common.theme.LobstersTheme
|
||||||
import dev.msfjarvis.claw.common.ui.preview.ThemePreviews
|
import dev.msfjarvis.claw.common.ui.preview.ThemePreviews
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun ThemedRichText(
|
internal fun ThemedRichText(text: String, modifier: Modifier = Modifier) {
|
||||||
text: String,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
val linkStyle =
|
val linkStyle =
|
||||||
SpanStyle(
|
SpanStyle(
|
||||||
background = MaterialTheme.colorScheme.surfaceVariant,
|
background = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
|
@ -39,10 +36,7 @@ internal fun ThemedRichText(
|
||||||
LocalTextStyle provides MaterialTheme.typography.bodyLarge,
|
LocalTextStyle provides MaterialTheme.typography.bodyLarge,
|
||||||
LocalContentColor provides MaterialTheme.colorScheme.onBackground,
|
LocalContentColor provides MaterialTheme.colorScheme.onBackground,
|
||||||
) {
|
) {
|
||||||
RichText(
|
RichText(modifier = modifier, style = RichTextStyle.Default.copy(stringStyle = stringStyle)) {
|
||||||
modifier = modifier,
|
|
||||||
style = RichTextStyle.Default.copy(stringStyle = stringStyle),
|
|
||||||
) {
|
|
||||||
Markdown(text)
|
Markdown(text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2022-2023 Harsh Shandilya.
|
* Copyright © 2022-2024 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.
|
||||||
|
@ -29,11 +29,7 @@ import dev.msfjarvis.claw.common.ui.preview.ThemePreviews
|
||||||
import io.github.aakira.napier.Napier
|
import io.github.aakira.napier.Napier
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun NetworkError(
|
fun NetworkError(label: String, error: Throwable, modifier: Modifier = Modifier) {
|
||||||
label: String,
|
|
||||||
error: Throwable,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
LaunchedEffect(true) { Napier.e(error, "NetworkError") { "Failed to load posts" } }
|
LaunchedEffect(true) { Napier.e(error, "NetworkError") { "Failed to load posts" } }
|
||||||
var showDialog by remember { mutableStateOf(false) }
|
var showDialog by remember { mutableStateOf(false) }
|
||||||
Column(verticalArrangement = Arrangement.spacedBy(4.dp), modifier = modifier) {
|
Column(verticalArrangement = Arrangement.spacedBy(4.dp), modifier = modifier) {
|
||||||
|
@ -60,15 +56,10 @@ fun NetworkError(
|
||||||
Modifier.clickable {
|
Modifier.clickable {
|
||||||
clipboard.setText(AnnotatedString(error.stackTraceToString()))
|
clipboard.setText(AnnotatedString(error.stackTraceToString()))
|
||||||
showDialog = false
|
showDialog = false
|
||||||
}
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
text = {
|
|
||||||
Text(
|
|
||||||
text = "${error.message}",
|
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
|
text = { Text(text = "${error.message}", style = MaterialTheme.typography.bodyLarge) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2023 Harsh Shandilya.
|
* Copyright © 2023-2024 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.
|
||||||
|
@ -45,7 +45,7 @@ fun PasswordField(
|
||||||
if (isPasswordVisible) rememberVectorPainter(Icons.Filled.VisibilityOff)
|
if (isPasswordVisible) rememberVectorPainter(Icons.Filled.VisibilityOff)
|
||||||
else rememberVectorPainter(Icons.Filled.Visibility),
|
else rememberVectorPainter(Icons.Filled.Visibility),
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
modifier = Modifier.clickable { isPasswordVisible = !isPasswordVisible }
|
modifier = Modifier.clickable { isPasswordVisible = !isPasswordVisible },
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
|
@ -57,10 +57,6 @@ fun PasswordField(
|
||||||
private fun PasswordFieldPreview() {
|
private fun PasswordFieldPreview() {
|
||||||
LobstersTheme {
|
LobstersTheme {
|
||||||
var value by remember { mutableStateOf("") }
|
var value by remember { mutableStateOf("") }
|
||||||
PasswordField(
|
PasswordField(value = value, label = "Password", onValueChange = { value = it })
|
||||||
value = value,
|
|
||||||
label = "Password",
|
|
||||||
onValueChange = { value = it },
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2023 Harsh Shandilya.
|
* Copyright © 2023-2024 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.
|
||||||
|
@ -59,10 +59,7 @@ fun SearchBar(
|
||||||
},
|
},
|
||||||
keyboardActions = KeyboardActions(onSearch = { onSearch(value) }),
|
keyboardActions = KeyboardActions(onSearch = { onSearch(value) }),
|
||||||
keyboardOptions =
|
keyboardOptions =
|
||||||
KeyboardOptions(
|
KeyboardOptions(keyboardType = KeyboardType.Text, imeAction = ImeAction.Search),
|
||||||
keyboardType = KeyboardType.Text,
|
|
||||||
imeAction = ImeAction.Search,
|
|
||||||
),
|
|
||||||
singleLine = true,
|
singleLine = true,
|
||||||
modifier = modifier.focusable().focusRequester(focusRequester),
|
modifier = modifier.focusable().focusRequester(focusRequester),
|
||||||
)
|
)
|
||||||
|
@ -81,7 +78,7 @@ fun SearchBarPreview() {
|
||||||
value = value,
|
value = value,
|
||||||
onValueChange = { value = it },
|
onValueChange = { value = it },
|
||||||
onSearch = {},
|
onSearch = {},
|
||||||
modifier = Modifier.align(Alignment.TopCenter).fillMaxWidth()
|
modifier = Modifier.align(Alignment.TopCenter).fillMaxWidth(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,10 +22,7 @@ import dev.msfjarvis.claw.common.ui.preview.ThemePreviews
|
||||||
import dev.msfjarvis.claw.common.ui.surfaceColorAtNavigationBarElevation
|
import dev.msfjarvis.claw.common.ui.surfaceColorAtNavigationBarElevation
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MonthHeader(
|
fun MonthHeader(label: String, modifier: Modifier = Modifier) {
|
||||||
label: String,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
Box(
|
Box(
|
||||||
modifier
|
modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2022-2023 Harsh Shandilya.
|
* Copyright © 2022-2024 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.
|
||||||
|
@ -50,12 +50,8 @@ fun UserProfile(
|
||||||
.fold(
|
.fold(
|
||||||
success = { profile -> value = Success(profile) },
|
success = { profile -> value = Success(profile) },
|
||||||
failure = {
|
failure = {
|
||||||
value =
|
value = Error(error = it, description = "Failed to load profile for $username")
|
||||||
Error(
|
},
|
||||||
error = it,
|
|
||||||
description = "Failed to load profile for $username",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
when (user) {
|
when (user) {
|
||||||
|
@ -81,10 +77,7 @@ fun UserProfile(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun UserProfileInternal(
|
private fun UserProfileInternal(user: User, modifier: Modifier = Modifier) {
|
||||||
user: User,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
Surface(modifier = modifier) {
|
Surface(modifier = modifier) {
|
||||||
Column(
|
Column(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
@ -97,17 +90,10 @@ private fun UserProfileInternal(
|
||||||
contentDescription = "Avatar of ${user.username}",
|
contentDescription = "Avatar of ${user.username}",
|
||||||
modifier = Modifier.requiredSize(120.dp).clip(CircleShape),
|
modifier = Modifier.requiredSize(120.dp).clip(CircleShape),
|
||||||
)
|
)
|
||||||
Text(
|
Text(text = user.username, style = MaterialTheme.typography.displaySmall)
|
||||||
text = user.username,
|
ThemedRichText(text = user.about)
|
||||||
style = MaterialTheme.typography.displaySmall,
|
|
||||||
)
|
|
||||||
ThemedRichText(
|
|
||||||
text = user.about,
|
|
||||||
)
|
|
||||||
user.invitedBy?.let { invitedBy ->
|
user.invitedBy?.let { invitedBy ->
|
||||||
ThemedRichText(
|
ThemedRichText(text = "Invited by [${invitedBy}](https://lobste.rs/u/${user.invitedBy})")
|
||||||
text = "Invited by [${invitedBy}](https://lobste.rs/u/${user.invitedBy})",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2022 Harsh Shandilya.
|
* Copyright © 2022-2024 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.
|
||||||
|
@ -29,7 +29,7 @@ open class DelegatingSocketFactory(private val delegate: SocketFactory) : Socket
|
||||||
host: String,
|
host: String,
|
||||||
port: Int,
|
port: Int,
|
||||||
localAddress: InetAddress,
|
localAddress: InetAddress,
|
||||||
localPort: Int
|
localPort: Int,
|
||||||
): Socket {
|
): Socket {
|
||||||
val socket = delegate.createSocket(host, port, localAddress, localPort)
|
val socket = delegate.createSocket(host, port, localAddress, localPort)
|
||||||
return configureSocket(socket)
|
return configureSocket(socket)
|
||||||
|
@ -44,7 +44,7 @@ open class DelegatingSocketFactory(private val delegate: SocketFactory) : Socket
|
||||||
host: InetAddress,
|
host: InetAddress,
|
||||||
port: Int,
|
port: Int,
|
||||||
localAddress: InetAddress,
|
localAddress: InetAddress,
|
||||||
localPort: Int
|
localPort: Int,
|
||||||
): Socket {
|
): Socket {
|
||||||
val socket = delegate.createSocket(host, port, localAddress, localPort)
|
val socket = delegate.createSocket(host, port, localAddress, localPort)
|
||||||
return configureSocket(socket)
|
return configureSocket(socket)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2023 Harsh Shandilya.
|
* Copyright © 2023-2024 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.
|
||||||
|
@ -32,9 +32,7 @@ object QueriesModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
fun provideReadPostsQueries(
|
fun provideReadPostsQueries(@InternalDatabaseApi database: LobstersDatabase): ReadPostsQueries {
|
||||||
@InternalDatabaseApi database: LobstersDatabase,
|
|
||||||
): ReadPostsQueries {
|
|
||||||
return database.readPostsQueries
|
return database.readPostsQueries
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2022-2023 Harsh Shandilya.
|
* Copyright © 2022-2024 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.
|
||||||
|
@ -10,8 +10,4 @@ package dev.msfjarvis.claw.model
|
||||||
|
|
||||||
import dev.drewhamilton.poko.Poko
|
import dev.drewhamilton.poko.Poko
|
||||||
|
|
||||||
@Poko
|
@Poko class LinkMetadata(val url: String, val faviconUrl: String?)
|
||||||
class LinkMetadata(
|
|
||||||
val url: String,
|
|
||||||
val faviconUrl: String?,
|
|
||||||
)
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2021-2023 Harsh Shandilya.
|
* Copyright © 2021-2024 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.
|
||||||
|
@ -21,14 +21,8 @@ import kotlinx.serialization.Serializable
|
||||||
value = SavedPost::class,
|
value = SavedPost::class,
|
||||||
mappings =
|
mappings =
|
||||||
[
|
[
|
||||||
Mapping(
|
Mapping(target = "submitterName", expression = "it.submitter.username"),
|
||||||
target = "submitterName",
|
Mapping(target = "submitterAvatarUrl", expression = "it.submitter.avatarUrl"),
|
||||||
expression = "it.submitter.username",
|
|
||||||
),
|
|
||||||
Mapping(
|
|
||||||
target = "submitterAvatarUrl",
|
|
||||||
expression = "it.submitter.avatarUrl",
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
class LobstersPost(
|
class LobstersPost(
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2021-2023 Harsh Shandilya.
|
* Copyright © 2021-2024 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.
|
||||||
|
@ -21,14 +21,8 @@ import kotlinx.serialization.Serializable
|
||||||
value = SavedPost::class,
|
value = SavedPost::class,
|
||||||
mappings =
|
mappings =
|
||||||
[
|
[
|
||||||
Mapping(
|
Mapping(target = "submitterName", expression = "it.submitter.username"),
|
||||||
target = "submitterName",
|
Mapping(target = "submitterAvatarUrl", expression = "it.submitter.avatarUrl"),
|
||||||
expression = "it.submitter.username",
|
|
||||||
),
|
|
||||||
Mapping(
|
|
||||||
target = "submitterAvatarUrl",
|
|
||||||
expression = "it.submitter.avatarUrl",
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
class LobstersPostDetails(
|
class LobstersPostDetails(
|
||||||
|
|
|
@ -28,7 +28,7 @@ pluginManagement {
|
||||||
includeModule("dev.iurysouza", "modulegraph")
|
includeModule("dev.iurysouza", "modulegraph")
|
||||||
includeModule(
|
includeModule(
|
||||||
"com.jraska.module.graph.assertion",
|
"com.jraska.module.graph.assertion",
|
||||||
"com.jraska.module.graph.assertion.gradle.plugin"
|
"com.jraska.module.graph.assertion.gradle.plugin",
|
||||||
)
|
)
|
||||||
includeModule("com.gradle", "gradle-enterprise-gradle-plugin")
|
includeModule("com.gradle", "gradle-enterprise-gradle-plugin")
|
||||||
includeModule("com.gradle.enterprise", "com.gradle.enterprise.gradle.plugin")
|
includeModule("com.gradle.enterprise", "com.gradle.enterprise.gradle.plugin")
|
||||||
|
|
|
@ -103,7 +103,7 @@ public fun WebView(
|
||||||
onDispose,
|
onDispose,
|
||||||
client,
|
client,
|
||||||
chromeClient,
|
chromeClient,
|
||||||
factory
|
factory,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -166,7 +166,7 @@ public fun WebView(
|
||||||
content.data,
|
content.data,
|
||||||
content.mimeType,
|
content.mimeType,
|
||||||
content.encoding,
|
content.encoding,
|
||||||
content.historyUrl
|
content.historyUrl,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is WebContent.Post -> {
|
is WebContent.Post -> {
|
||||||
|
@ -204,7 +204,7 @@ public fun WebView(
|
||||||
.also { state.webView = it }
|
.also { state.webView = it }
|
||||||
},
|
},
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
onRelease = { onDispose(it) }
|
onRelease = { onDispose(it) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,7 +248,7 @@ public open class AccompanistWebViewClient : WebViewClient() {
|
||||||
override fun onReceivedError(
|
override fun onReceivedError(
|
||||||
view: WebView,
|
view: WebView,
|
||||||
request: WebResourceRequest?,
|
request: WebResourceRequest?,
|
||||||
error: WebResourceError?
|
error: WebResourceError?,
|
||||||
) {
|
) {
|
||||||
super.onReceivedError(view, request, error)
|
super.onReceivedError(view, request, error)
|
||||||
|
|
||||||
|
@ -298,7 +298,7 @@ public sealed class WebContent {
|
||||||
val baseUrl: String? = null,
|
val baseUrl: String? = null,
|
||||||
val encoding: String = "utf-8",
|
val encoding: String = "utf-8",
|
||||||
val mimeType: String? = null,
|
val mimeType: String? = null,
|
||||||
val historyUrl: String? = null
|
val historyUrl: String? = null,
|
||||||
) : WebContent()
|
) : WebContent()
|
||||||
|
|
||||||
public data class Post(val url: String, val postData: ByteArray) : WebContent() {
|
public data class Post(val url: String, val postData: ByteArray) : WebContent() {
|
||||||
|
@ -417,7 +417,7 @@ public class WebViewNavigator(
|
||||||
|
|
||||||
data class LoadUrl(
|
data class LoadUrl(
|
||||||
val url: String,
|
val url: String,
|
||||||
val additionalHttpHeaders: Map<String, String> = emptyMap()
|
val additionalHttpHeaders: Map<String, String> = emptyMap(),
|
||||||
) : NavigationEvent
|
) : NavigationEvent
|
||||||
|
|
||||||
data class LoadHtml(
|
data class LoadHtml(
|
||||||
|
@ -425,7 +425,7 @@ public class WebViewNavigator(
|
||||||
val baseUrl: String? = null,
|
val baseUrl: String? = null,
|
||||||
val mimeType: String? = null,
|
val mimeType: String? = null,
|
||||||
val encoding: String? = "utf-8",
|
val encoding: String? = "utf-8",
|
||||||
val historyUrl: String? = null
|
val historyUrl: String? = null,
|
||||||
) : NavigationEvent
|
) : NavigationEvent
|
||||||
|
|
||||||
data class PostUrl(val url: String, val postData: ByteArray) : NavigationEvent {
|
data class PostUrl(val url: String, val postData: ByteArray) : NavigationEvent {
|
||||||
|
@ -466,7 +466,7 @@ public class WebViewNavigator(
|
||||||
event.html,
|
event.html,
|
||||||
event.mimeType,
|
event.mimeType,
|
||||||
event.encoding,
|
event.encoding,
|
||||||
event.historyUrl
|
event.historyUrl,
|
||||||
)
|
)
|
||||||
is NavigationEvent.LoadUrl -> {
|
is NavigationEvent.LoadUrl -> {
|
||||||
loadUrl(event.url, event.additionalHttpHeaders)
|
loadUrl(event.url, event.additionalHttpHeaders)
|
||||||
|
@ -497,7 +497,7 @@ public class WebViewNavigator(
|
||||||
baseUrl: String? = null,
|
baseUrl: String? = null,
|
||||||
mimeType: String? = null,
|
mimeType: String? = null,
|
||||||
encoding: String? = "utf-8",
|
encoding: String? = "utf-8",
|
||||||
historyUrl: String? = null
|
historyUrl: String? = null,
|
||||||
) {
|
) {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
navigationEvents.emit(NavigationEvent.LoadHtml(html, baseUrl, mimeType, encoding, historyUrl))
|
navigationEvents.emit(NavigationEvent.LoadHtml(html, baseUrl, mimeType, encoding, historyUrl))
|
||||||
|
@ -546,7 +546,7 @@ public data class WebViewError(
|
||||||
/** The request the error came from. */
|
/** The request the error came from. */
|
||||||
val request: WebResourceRequest?,
|
val request: WebResourceRequest?,
|
||||||
/** The error that was reported. */
|
/** The error that was reported. */
|
||||||
val error: WebResourceError
|
val error: WebResourceError,
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -559,7 +559,7 @@ public data class WebViewError(
|
||||||
@Composable
|
@Composable
|
||||||
public fun rememberWebViewState(
|
public fun rememberWebViewState(
|
||||||
url: String,
|
url: String,
|
||||||
additionalHttpHeaders: ImmutableMap<String, String> = persistentMapOf()
|
additionalHttpHeaders: ImmutableMap<String, String> = persistentMapOf(),
|
||||||
): WebViewState =
|
): WebViewState =
|
||||||
// Rather than using .apply {} here we will recreate the state, this prevents
|
// Rather than using .apply {} here we will recreate the state, this prevents
|
||||||
// a recomposition loop when the webview updates the url itself.
|
// a recomposition loop when the webview updates the url itself.
|
||||||
|
@ -581,7 +581,7 @@ public fun rememberWebViewStateWithHTMLData(
|
||||||
baseUrl: String? = null,
|
baseUrl: String? = null,
|
||||||
encoding: String = "utf-8",
|
encoding: String = "utf-8",
|
||||||
mimeType: String? = null,
|
mimeType: String? = null,
|
||||||
historyUrl: String? = null
|
historyUrl: String? = null,
|
||||||
): WebViewState =
|
): WebViewState =
|
||||||
remember { WebViewState(WebContent.Data(data, baseUrl, encoding, mimeType, historyUrl)) }
|
remember { WebViewState(WebContent.Data(data, baseUrl, encoding, mimeType, historyUrl)) }
|
||||||
.apply { this.content = WebContent.Data(data, baseUrl, encoding, mimeType, historyUrl) }
|
.apply { this.content = WebContent.Data(data, baseUrl, encoding, mimeType, historyUrl) }
|
||||||
|
@ -619,7 +619,7 @@ public val WebStateSaver: Saver<WebViewState, Any> = run {
|
||||||
mapOf(
|
mapOf(
|
||||||
pageTitleKey to it.pageTitle,
|
pageTitleKey to it.pageTitle,
|
||||||
lastLoadedUrlKey to it.lastLoadedUrl,
|
lastLoadedUrlKey to it.lastLoadedUrl,
|
||||||
stateBundle to viewState
|
stateBundle to viewState,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
restore = {
|
restore = {
|
||||||
|
@ -628,6 +628,6 @@ public val WebStateSaver: Saver<WebViewState, Any> = run {
|
||||||
this.lastLoadedUrl = it[lastLoadedUrlKey] as String?
|
this.lastLoadedUrl = it[lastLoadedUrlKey] as String?
|
||||||
this.viewState = it[stateBundle] as Bundle?
|
this.viewState = it[stateBundle] as Bundle?
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue