fix: adjust for new API changes

This commit is contained in:
Harsh Shandilya 2024-03-16 09:48:08 +05:30
parent 86a40d2e84
commit 85fc5cac21
20 changed files with 65 additions and 90 deletions

View File

@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
### Fixed
* Adapt to changes in lobste.rs API
## [1.41.0] - 2024-03-07 ## [1.41.0] - 2024-03-07
### Fixed ### Fixed

View File

@ -8,7 +8,6 @@ package dev.msfjarvis.claw.api.converters
import dev.msfjarvis.claw.api.LobstersApi import dev.msfjarvis.claw.api.LobstersApi
import dev.msfjarvis.claw.model.LobstersPost import dev.msfjarvis.claw.model.LobstersPost
import dev.msfjarvis.claw.model.User
import java.lang.reflect.Type import java.lang.reflect.Type
import okhttp3.ResponseBody import okhttp3.ResponseBody
import org.jsoup.Jsoup import org.jsoup.Jsoup
@ -32,8 +31,7 @@ object SearchConverter : Converter<ResponseBody, List<LobstersPost>> {
val url = titleElement.attr("href") val url = titleElement.attr("href")
val tags = elem.select("span.tags > a").map(Element::text) val tags = elem.select("span.tags > a").map(Element::text)
val (commentCount, commentsUrl) = getCommentsData(elem.select("span.comments_label")) val (commentCount, commentsUrl) = getCommentsData(elem.select("span.comments_label"))
val submitter = val submitter = elem.select("div.byline > a.u-author").text()
getSubmitter(elem.select("div.byline").first() ?: error("No byline element found"))
return LobstersPost( return LobstersPost(
shortId = shortId, shortId = shortId,
title = title, title = title,
@ -55,24 +53,6 @@ object SearchConverter : Converter<ResponseBody, List<LobstersPost>> {
return (countString.toIntOrNull() ?: 0) to commentsUrl return (countString.toIntOrNull() ?: 0) to commentsUrl
} }
/**
* Make a bare-bones [User] object given a byline [elem]. We only need this to be usable for
* displaying in a list.
*/
private fun getSubmitter(elem: Element): User {
val userElement = elem.select("a.u-author")
val avatarElement = elem.select("img.avatar")
val username = userElement.text()
val avatarUrl = avatarElement.attr("src")
return User(
username = username,
about = "",
invitedBy = null,
avatarUrl = avatarUrl,
createdAt = "",
)
}
object Factory : Converter.Factory() { object Factory : Converter.Factory() {
override fun responseBodyConverter( override fun responseBodyConverter(
type: Type, type: Type,

View File

@ -33,7 +33,7 @@ class ApiTest {
val posts = api.getHottestPosts(1) val posts = api.getHottestPosts(1)
assertIs<Success<List<LobstersPost>>>(posts) assertIs<Success<List<LobstersPost>>>(posts)
val commentsOnlyPosts = posts.value.asSequence().filter { it.url.isEmpty() }.toSet() val commentsOnlyPosts = posts.value.asSequence().filter { it.url.isEmpty() }.toSet()
assertThat(commentsOnlyPosts).hasSize(2) assertThat(commentsOnlyPosts).hasSize(1)
} }
@Test @Test

View File

@ -10,7 +10,6 @@ import com.google.common.truth.Truth.assertThat
import com.slack.eithernet.ApiResult import com.slack.eithernet.ApiResult
import com.slack.eithernet.test.newEitherNetController import com.slack.eithernet.test.newEitherNetController
import dev.msfjarvis.claw.model.LobstersPost import dev.msfjarvis.claw.model.LobstersPost
import dev.msfjarvis.claw.model.User
import dev.msfjarvis.claw.util.TestUtils.assertIs import dev.msfjarvis.claw.util.TestUtils.assertIs
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
@ -38,7 +37,7 @@ class SearchApiTest {
createdAt = "", createdAt = "",
commentCount = 3, commentCount = 3,
commentsUrl = "https://lobste.rs/s/gjlsdg/chatgpt_visits_emacs_doctor", commentsUrl = "https://lobste.rs/s/gjlsdg/chatgpt_visits_emacs_doctor",
submitter = User("xenodium", "", null, "/avatars/xenodium-16.png", ""), submitter = "xenodium",
tags = listOf("ai", "emacs"), tags = listOf("ai", "emacs"),
description = "", description = "",
), ),
@ -50,7 +49,7 @@ class SearchApiTest {
createdAt = "", createdAt = "",
commentCount = 0, commentCount = 0,
commentsUrl = "https://lobste.rs/s/astcqf/implementing_question_answering_system", commentsUrl = "https://lobste.rs/s/astcqf/implementing_question_answering_system",
submitter = User("asteroid", "", null, "/avatars/asteroid-16.png", ""), submitter = "asteroid",
tags = listOf("ai"), tags = listOf("ai"),
description = "", description = "",
), ),

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
{"username":"msfjarvis","created_at":"2020-04-24T11:41:56.000-05:00","is_admin":false,"about":"Android and Kotlin developer\r\n","is_moderator":false,"karma":574,"avatar_url":"/avatars/msfjarvis-100.png","invited_by_user":"Amolith","github_username":"msfjarvis","twitter_username":"msfjarvis"} {"username":"msfjarvis","created_at":"2020-04-24T11:41:56.000-05:00","is_admin":false,"about":"Android and Kotlin developer, currently working for [Dyte](https://dyte.io/)","is_moderator":false,"karma":1343,"avatar_url":"/avatars/msfjarvis-100.png","invited_by_user":"Amolith","github_username":"msfjarvis"}

File diff suppressed because one or more lines are too long

View File

@ -90,13 +90,11 @@ internal fun CommentsHeader(
Spacer(Modifier.height(4.dp)) Spacer(Modifier.height(4.dp))
} }
Submitter( Submitter(
text = AnnotatedString("Submitted by ${post.submitter.username}"), text = AnnotatedString("Submitted by ${post.submitter}"),
avatarUrl = "https://lobste.rs/${post.submitter.avatarUrl}", avatarUrl = "https://lobste.rs/avatars/${post.submitter}-100.png",
contentDescription = "User avatar for ${post.submitter.username}", contentDescription = "User avatar for ${post.submitter}",
modifier = modifier =
Modifier.clickable { Modifier.clickable { uriHandler.openUri("https://lobste.rs/u/${post.submitter}") },
uriHandler.openUri("https://lobste.rs/u/${post.submitter.username}")
},
) )
} }
} }
@ -156,16 +154,15 @@ internal fun CommentEntry(
Submitter( Submitter(
text = text =
buildCommenterString( buildCommenterString(
commenterName = comment.user.username, commenterName = comment.user,
score = comment.score, score = comment.score,
isUnread = commentNode.isUnread, isUnread = commentNode.isUnread,
createdAt = comment.createdAt, createdAt = comment.createdAt,
updatedAt = comment.updatedAt, updatedAt = comment.updatedAt,
), ),
avatarUrl = "https://lobste.rs/${comment.user.avatarUrl}", avatarUrl = "https://lobste.rs/avatars/${comment.user}-100.png",
contentDescription = "User avatar for ${comment.user.username}", contentDescription = "User avatar for ${comment.user}",
modifier = modifier = Modifier.clickable { uriHandler.openUri("https://lobste.rs/u/${comment.user}") },
Modifier.clickable { uriHandler.openUri("https://lobste.rs/u/${comment.user.username}") },
) )
if (commentNode.isExpanded) { if (commentNode.isExpanded) {
ThemedRichText( ThemedRichText(

View File

@ -53,7 +53,6 @@ import dev.msfjarvis.claw.common.ui.NetworkImage
import dev.msfjarvis.claw.common.ui.preview.ThemePreviews import dev.msfjarvis.claw.common.ui.preview.ThemePreviews
import dev.msfjarvis.claw.model.LinkMetadata import dev.msfjarvis.claw.model.LinkMetadata
import dev.msfjarvis.claw.model.UIPost import dev.msfjarvis.claw.model.UIPost
import dev.msfjarvis.claw.model.User
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
@ -122,9 +121,9 @@ fun PostDetails(post: UIPost, isRead: Boolean, modifier: Modifier = Modifier) {
TagRow(tags = post.tags.toImmutableList()) TagRow(tags = post.tags.toImmutableList())
Spacer(Modifier.height(4.dp)) Spacer(Modifier.height(4.dp))
Submitter( Submitter(
text = AnnotatedString("Submitted by ${post.submitter.username}"), text = AnnotatedString("Submitted by ${post.submitter}"),
avatarUrl = "https://lobste.rs/${post.submitter.avatarUrl}", avatarUrl = "https://lobste.rs/avatars/${post.submitter}-100.png",
contentDescription = "User avatar for ${post.submitter.username}", contentDescription = "User avatar for ${post.submitter}",
) )
} }
} }
@ -249,7 +248,7 @@ private fun LobstersCardPreview() {
createdAt = "2020-09-21T08:04:24.000-05:00", createdAt = "2020-09-21T08:04:24.000-05:00",
commentCount = 1, commentCount = 1,
commentsUrl = "https://lobste.rs/s/q1hh1g/simple_anomaly_detection_using_plain_sql", commentsUrl = "https://lobste.rs/s/q1hh1g/simple_anomaly_detection_using_plain_sql",
submitter = User("Haki", "", "", "/avatars/Haki-100.png", ""), submitter = "Haki",
tags = listOf("databases", "apis"), tags = listOf("databases", "apis"),
description = "", description = "",
isSaved = true, isSaved = true,
@ -275,7 +274,7 @@ private fun LobstersCardPreview() {
commentsUrl = "https://lobste.rs/s/q1hh1g/simple_anomaly_detection_using_plain_sql", commentsUrl = "https://lobste.rs/s/q1hh1g/simple_anomaly_detection_using_plain_sql",
tags = listOf("databases", "apis"), tags = listOf("databases", "apis"),
description = "", description = "",
submitter = User("Haki", "", "", "", ""), submitter = "Haki",
comments = emptyList(), comments = emptyList(),
) )
} }

View File

@ -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,7 +32,6 @@ object SavedPostSerializer : KSerializer<SavedPost> {
element<Int?>("commentCount", isOptional = true) element<Int?>("commentCount", isOptional = true)
element<String>("commentsUrl") element<String>("commentsUrl")
element<String>("submitterName") element<String>("submitterName")
element<String>("submitterAvatarUrl")
element<List<String>>("tags") element<List<String>>("tags")
element<String>("description") element<String>("description")
} }
@ -46,7 +45,6 @@ object SavedPostSerializer : KSerializer<SavedPost> {
var commentCount: Int? = null var commentCount: Int? = null
var commentsUrl = "" var commentsUrl = ""
var submitterName = "" var submitterName = ""
var submitterAvatarUrl = ""
var tags = emptyList<String>() var tags = emptyList<String>()
var description = "" var description = ""
while (true) { while (true) {
@ -58,9 +56,8 @@ object SavedPostSerializer : KSerializer<SavedPost> {
4 -> commentCount = decodeNullableSerializableElement(descriptor, 4, Int.serializer()) 4 -> commentCount = decodeNullableSerializableElement(descriptor, 4, Int.serializer())
5 -> commentsUrl = decodeStringElement(descriptor, 5) 5 -> commentsUrl = decodeStringElement(descriptor, 5)
6 -> submitterName = decodeStringElement(descriptor, 6) 6 -> submitterName = decodeStringElement(descriptor, 6)
7 -> submitterAvatarUrl = decodeStringElement(descriptor, 7) 7 -> tags = decodeSerializableElement(descriptor, 7, delegateSerializer)
8 -> tags = decodeSerializableElement(descriptor, 8, delegateSerializer) 8 -> description = decodeStringElement(descriptor, 8)
9 -> description = decodeStringElement(descriptor, 9)
CompositeDecoder.DECODE_DONE -> break CompositeDecoder.DECODE_DONE -> break
else -> error("Unexpected index: $index") else -> error("Unexpected index: $index")
} }
@ -73,7 +70,6 @@ object SavedPostSerializer : KSerializer<SavedPost> {
commentCount = commentCount, commentCount = commentCount,
commentsUrl = commentsUrl, commentsUrl = commentsUrl,
submitterName = submitterName, submitterName = submitterName,
submitterAvatarUrl = submitterAvatarUrl,
tags = tags, tags = tags,
description = description, description = description,
) )
@ -89,9 +85,8 @@ object SavedPostSerializer : KSerializer<SavedPost> {
encodeNullableSerializableElement(descriptor, 4, Int.serializer(), value.commentCount) encodeNullableSerializableElement(descriptor, 4, Int.serializer(), value.commentCount)
encodeStringElement(descriptor, 5, value.commentsUrl) encodeStringElement(descriptor, 5, value.commentsUrl)
encodeStringElement(descriptor, 6, value.submitterName) encodeStringElement(descriptor, 6, value.submitterName)
encodeStringElement(descriptor, 7, value.submitterAvatarUrl) encodeSerializableElement(descriptor, 7, delegateSerializer, value.tags)
encodeSerializableElement(descriptor, 8, delegateSerializer, value.tags) encodeStringElement(descriptor, 8, value.description)
encodeStringElement(descriptor, 9, value.description)
} }
} }
} }

View File

@ -10,7 +10,6 @@ CREATE TABLE IF NOT EXISTS SavedPost(
commentCount INTEGER AS Int, commentCount INTEGER AS Int,
commentsUrl TEXT NOT NULL, commentsUrl TEXT NOT NULL,
submitterName TEXT NOT NULL, submitterName TEXT NOT NULL,
submitterAvatarUrl TEXT NOT NULL,
tags TEXT AS List<String> NOT NULL, tags TEXT AS List<String> NOT NULL,
description TEXT NOT NULL DEFAULT "" description TEXT NOT NULL DEFAULT ""
); );

View File

@ -1,3 +1,3 @@
CREATE TABLE ReadPosts( CREATE TABLE IF NOT EXISTS ReadPosts(
id TEXT NOT NULL PRIMARY KEY id TEXT NOT NULL PRIMARY KEY
); );

View File

@ -0,0 +1,23 @@
import kotlin.Int;
import kotlin.String;
import kotlin.collections.List;
ALTER TABLE SavedPost RENAME TO SavedPost_Old;
CREATE TABLE IF NOT EXISTS SavedPost(
shortId TEXT NOT NULL PRIMARY KEY,
title TEXT NOT NULL,
url TEXT NOT NULL,
createdAt TEXT NOT NULL,
commentCount INTEGER AS Int,
commentsUrl TEXT NOT NULL,
submitterName TEXT NOT NULL,
tags TEXT AS List<String> NOT NULL,
description TEXT NOT NULL DEFAULT ""
);
INSERT INTO SavedPost(shortId, title, url, createdAt, commentCount, commentsUrl, submitterName, tags, description)
SELECT shortId, title, url, createdAt, commentCount, commentsUrl, submitterName, tags, description
FROM SavedPost_Old;
DROP TABLE SavedPost_Old;

View File

@ -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.
@ -52,7 +52,6 @@ class SavedPostSerializerTest {
commentCount = 13, commentCount = 13,
commentsUrl = "https://lobste.rs/s/nbigsf/fun_format_friday_you_now_have_super", commentsUrl = "https://lobste.rs/s/nbigsf/fun_format_friday_you_now_have_super",
submitterName = "LenFalken", submitterName = "LenFalken",
submitterAvatarUrl = "/avatars/LenFalken-100.png",
tags = listOf("ask", "programming"), tags = listOf("ask", "programming"),
description = description =
"<p>You suddenly have in your possession a super computer. What comes next? What projects are suddenly possible for you? What performance tests can you now explore?</p>\n", "<p>You suddenly have in your possession a super computer. What comes next? What projects are suddenly possible for you? What performance tests can you now explore?</p>\n",

View File

@ -1 +1 @@
{"short_id":"nbigsf","title":"Fun Format Friday: You now have a super computer. What next?","url":"","created_at":"2023-05-04T23:43:50.000-05:00","comment_count":13,"comments_url":"https://lobste.rs/s/nbigsf/fun_format_friday_you_now_have_super","submitter_name":"LenFalken","submitter_avatar_url":"/avatars/LenFalken-100.png","tags":["ask","programming"],"description":"<p>You suddenly have in your possession a super computer. What comes next? What projects are suddenly possible for you? What performance tests can you now explore?</p>\n"} {"short_id":"nbigsf","title":"Fun Format Friday: You now have a super computer. What next?","url":"","created_at":"2023-05-04T23:43:50.000-05:00","comment_count":13,"comments_url":"https://lobste.rs/s/nbigsf/fun_format_friday_you_now_have_super","submitter_name":"LenFalken","tags":["ask","programming"],"description":"<p>You suddenly have in your possession a super computer. What comes next? What projects are suddenly possible for you? What performance tests can you now explore?</p>\n"}

View File

@ -122,7 +122,6 @@ class SavedPostQueriesTest {
commentCount = 0, commentCount = 0,
commentsUrl = "test_comments_url", commentsUrl = "test_comments_url",
submitterName = "test_user_$i", submitterName = "test_user_$i",
submitterAvatarUrl = "test_avatar_url",
tags = listOf(), tags = listOf(),
description = "", description = "",
) )

View File

@ -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.
@ -24,5 +24,5 @@ class Comment(
@Serializable(with = JavaInstantSerializer::class) val createdAt: TemporalAccessor, @Serializable(with = JavaInstantSerializer::class) val createdAt: TemporalAccessor,
@Serializable(with = JavaInstantSerializer::class) val updatedAt: TemporalAccessor, @Serializable(with = JavaInstantSerializer::class) val updatedAt: TemporalAccessor,
val parentComment: String?, val parentComment: String?,
@SerialName("commenting_user") val user: User, @SerialName("commenting_user") val user: String,
) )

View File

@ -18,11 +18,7 @@ import kotlinx.serialization.Serializable
@Poko @Poko
@KonvertTo( @KonvertTo(
value = UIPost::class, value = UIPost::class,
mappings = mappings = [Mapping(target = "submitterName", expression = "it.submitter.username")],
[
Mapping(target = "submitterName", expression = "it.submitter.username"),
Mapping(target = "submitterAvatarUrl", expression = "it.submitter.avatarUrl"),
],
) )
class LobstersPost( class LobstersPost(
val shortId: String, val shortId: String,
@ -32,6 +28,6 @@ class LobstersPost(
val description: String, val description: String,
val commentCount: Int, val commentCount: Int,
val commentsUrl: String, val commentsUrl: String,
@SerialName("submitter_user") val submitter: User, @SerialName("submitter_user") val submitter: String,
val tags: List<String>, val tags: List<String>,
) )

View File

@ -19,19 +19,11 @@ import kotlinx.serialization.Serializable
@Poko @Poko
@KonvertTo( @KonvertTo(
value = UIPost::class, value = UIPost::class,
mappings = mappings = [Mapping(target = "submitterName", expression = "it.submitter")],
[
Mapping(target = "submitterName", expression = "it.submitter.username"),
Mapping(target = "submitterAvatarUrl", expression = "it.submitter.avatarUrl"),
],
) )
@KonvertTo( @KonvertTo(
value = SavedPost::class, value = SavedPost::class,
mappings = mappings = [Mapping(target = "submitterName", expression = "it.submitter")],
[
Mapping(target = "submitterName", expression = "it.submitter.username"),
Mapping(target = "submitterAvatarUrl", expression = "it.submitter.avatarUrl"),
],
) )
class LobstersPostDetails( class LobstersPostDetails(
val shortId: String, val shortId: String,
@ -41,7 +33,7 @@ class LobstersPostDetails(
val description: String, val description: String,
val commentCount: Int, val commentCount: Int,
val commentsUrl: String, val commentsUrl: String,
@SerialName("submitter_user") val submitter: User, @SerialName("submitter_user") val submitter: String,
val tags: List<String>, val tags: List<String>,
val comments: List<Comment>, val comments: List<Comment>,
) )

View File

@ -14,11 +14,7 @@ import kotlinx.serialization.SerialName
@KonvertTo( @KonvertTo(
value = SavedPost::class, value = SavedPost::class,
mappings = mappings = [Mapping(target = "submitterName", expression = "it.submitter")],
[
Mapping(target = "submitterName", expression = "it.submitter.username"),
Mapping(target = "submitterAvatarUrl", expression = "it.submitter.avatarUrl"),
],
) )
data class UIPost( data class UIPost(
val shortId: String, val shortId: String,
@ -28,7 +24,7 @@ data class UIPost(
val description: String, val description: String,
val commentCount: Int, val commentCount: Int,
val commentsUrl: String, val commentsUrl: String,
@SerialName("submitter_user") val submitter: User, @SerialName("submitter_user") val submitter: String,
val tags: List<String>, val tags: List<String>,
val comments: List<Comment> = emptyList(), val comments: List<Comment> = emptyList(),
val isSaved: Boolean = false, val isSaved: Boolean = false,
@ -38,10 +34,7 @@ data class UIPost(
value = SavedPost::class, value = SavedPost::class,
mappings = mappings =
[ [
Mapping( Mapping(target = "submitter", expression = "it.submitterName"),
target = "submitter",
expression = "User(it.submitterName, \"\", null, it.submitterAvatarUrl, \"\")",
),
Mapping(target = "commentCount", expression = "it.commentCount ?: 0"), Mapping(target = "commentCount", expression = "it.commentCount ?: 0"),
Mapping(target = "isSaved", expression = "true"), Mapping(target = "isSaved", expression = "true"),
], ],