diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 7af3d8218..ded78cdb8 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -189,7 +189,7 @@ dependencies { implementation("androidx.profileinstaller:profileinstaller") baselineProfile(project(":benchmarks")) - implementation("it.vercruysse.lemmyapi:lemmy-api:0.3.3-SNAPSHOT") + implementation("it.vercruysse.lemmyapi:lemmy-api:0.3.4-SNAPSHOT") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.1") // For custom logging plugin diff --git a/app/src/main/java/com/jerboa/feed/FeedController.kt b/app/src/main/java/com/jerboa/feed/FeedController.kt index 511862ee0..23e642f32 100644 --- a/app/src/main/java/com/jerboa/feed/FeedController.kt +++ b/app/src/main/java/com/jerboa/feed/FeedController.kt @@ -8,7 +8,7 @@ open class FeedController { val feed: List = items - fun updateAll( + open fun updateAll( selector: (List) -> List, transformer: (T) -> T, ) { @@ -17,7 +17,7 @@ open class FeedController { } } - fun safeUpdate( + open fun safeUpdate( index: Int, transformer: (T) -> T, ) { @@ -29,7 +29,7 @@ open class FeedController { safeUpdate(index, transformer(items[index])) } - fun safeUpdate( + open fun safeUpdate( selector: (List) -> Int, transformer: (T) -> T, ) { @@ -44,7 +44,7 @@ open class FeedController { * Example: a network request to update an item succeeded after the list has changed. * So, we ignore it */ - fun safeUpdate( + open fun safeUpdate( index: Int, new: T, ) { @@ -55,26 +55,28 @@ open class FeedController { } } - fun init(newItems: List) { - items.clear() - items.addAll(newItems) + open fun init(newItems: List) { + clear() + addAll(newItems) } - fun get(index: Int): T? = items.getOrNull(index) + open fun get(index: Int): T? = items.getOrNull(index) - fun add(item: T) = items.add(item) + open fun add(item: T) = items.add(item) - fun remove(item: T) = items.remove(item) + open fun remove(item: T) = items.remove(item) - fun removeAt(index: Int) { + open fun removeAt(index: Int) { if (isValidIndex(index)) { items.removeAt(index) } } - fun clear() = items.clear() + open fun clear() = items.clear() - fun addAll(newItems: List) = items.addAll(newItems) + open fun addAll(newItems: List) { + items.addAll(newItems) + } protected inline fun Iterable.indexesOf(predicate: (E) -> Boolean) = mapIndexedNotNull { index, elem -> diff --git a/app/src/main/java/com/jerboa/feed/PostController.kt b/app/src/main/java/com/jerboa/feed/PostController.kt index eaa84a94a..b092b8e51 100644 --- a/app/src/main/java/com/jerboa/feed/PostController.kt +++ b/app/src/main/java/com/jerboa/feed/PostController.kt @@ -5,7 +5,7 @@ import it.vercruysse.lemmyapi.datatypes.HidePost import it.vercruysse.lemmyapi.datatypes.Person import it.vercruysse.lemmyapi.datatypes.PostView -open class PostController : FeedController() { +open class PostController : UniqueFeedController() { fun findAndUpdatePost(updatedPostView: PostView) { safeUpdate({ posts -> posts.indexOfFirst { diff --git a/app/src/main/java/com/jerboa/feed/UniqueFeedController.kt b/app/src/main/java/com/jerboa/feed/UniqueFeedController.kt new file mode 100644 index 000000000..edf9b6097 --- /dev/null +++ b/app/src/main/java/com/jerboa/feed/UniqueFeedController.kt @@ -0,0 +1,33 @@ +package com.jerboa.feed + +import it.vercruysse.lemmyapi.Identity + +open class UniqueFeedController : FeedController() { + private val ids = mutableSetOf() + + override fun add(item: T): Boolean { + if (ids.add(item.id)) { + items.add(item) + return true + } + return false + } + + override fun addAll(newItems: List) { + newItems.forEach { + if (ids.add(it.id)) { + items.add(it) + } + } + } + + override fun clear() { + super.clear() + ids.clear() + } + + override fun remove(item: T): Boolean { + ids.remove(item.id) + return super.remove(item) + } +} diff --git a/app/src/test/java/com/jerboa/feed/UniqueFeedControllerTest.kt b/app/src/test/java/com/jerboa/feed/UniqueFeedControllerTest.kt new file mode 100644 index 000000000..18eedbb8d --- /dev/null +++ b/app/src/test/java/com/jerboa/feed/UniqueFeedControllerTest.kt @@ -0,0 +1,82 @@ +package com.jerboa.feed + +import it.vercruysse.lemmyapi.Identity +import org.junit.Assert.* +import org.junit.Test + +class UniqueFeedControllerTest { + private data class PostView( + override val id: Long, + ) : Identity + + @Test + fun `Should not add duplicate posts`() { + val controller = UniqueFeedController() + controller.add(PostView(1)) + assertEquals(1, controller.feed.size) + controller.add(PostView(1)) + assertEquals(1, controller.feed.size) + controller.add(PostView(2)) + assertEquals(2, controller.feed.size) + } + + @Test + fun `Should remove post`() { + val controller = UniqueFeedController() + controller.add(PostView(1)) + assertEquals(1, controller.feed.size) + controller.remove(PostView(1)) + assertEquals(0, controller.feed.size) + } + + @Test + fun `Post removal should clear id`() { + val controller = UniqueFeedController() + controller.add(PostView(1)) + assertEquals(1, controller.feed.size) + controller.remove(PostView(1)) + assertTrue(controller.feed.isEmpty()) + controller.add(PostView(1)) + assertEquals(1, controller.feed.size) + } + + @Test + fun `Should clear all posts`() { + val controller = UniqueFeedController() + controller.add(PostView(1)) + controller.add(PostView(2)) + assertEquals(2, controller.feed.size) + controller.clear() + assertTrue(controller.feed.isEmpty()) + } + + @Test + fun `Clear should clear ids`() { + val controller = UniqueFeedController() + controller.add(PostView(1)) + controller.add(PostView(2)) + assertEquals(2, controller.feed.size) + controller.clear() + assertTrue(controller.feed.isEmpty()) + controller.add(PostView(1)) + controller.add(PostView(2)) + assertEquals(2, controller.feed.size) + } + + @Test + fun `Add all should not add duplicates`() { + val controller = UniqueFeedController() + controller.addAll(listOf(PostView(1), PostView(2), PostView(1))) + assertEquals(2, controller.feed.size) + } + + @Test + fun `Init should clear ids`() { + val controller = UniqueFeedController() + controller.add(PostView(1)) + controller.add(PostView(2)) + assertEquals(2, controller.feed.size) + controller.init(listOf(PostView(1), PostView(2))) + assertEquals(2, controller.feed.size) + } +}