feat: list of hidden posts (#966)

This commit is contained in:
Diego Beraldin 2024-06-11 00:15:07 +02:00 committed by GitHub
parent 4c65208907
commit 33aacd686f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
58 changed files with 1174 additions and 748 deletions

View File

@ -51,6 +51,7 @@ interface PostService {
@Query("saved_only") savedOnly: Boolean? = null,
@Query("liked_only") likedOnly: Boolean? = null,
@Query("disliked_only") dislikedOnly: Boolean? = null,
@Query("show_hidden") showHidden: Boolean? = null,
): GetPostsResponse
@GET("post")

View File

@ -416,4 +416,5 @@ internal val ArStrings =
override val communityVisibilityPublic = "عام"
override val noticeCommunityLocalOnly = "هذا المجتمع مرئي فقط في المثيل الحالي"
override val noticeBannedUser = "تم حظر المستخدم الحالي من هذا المجتمع"
override val settingsHiddenPosts = "المشاركات المخفية"
}

View File

@ -424,4 +424,5 @@ internal val BgStrings =
override val noticeCommunityLocalOnly =
"Тази общност е видима само в рамките на текущия екземпляр"
override val noticeBannedUser = "Настоящият потребител е забранен от тази общност"
override val settingsHiddenPosts = "Скрити публикации"
}

View File

@ -420,4 +420,5 @@ internal val CsStrings =
override val noticeCommunityLocalOnly =
"Tato komunita je viditelná pouze v aktuální instanci"
override val noticeBannedUser = "Aktuálnímu uživateli byl zakázán přístup do této komunity"
override val settingsHiddenPosts = "Skryté příspěvky"
}

View File

@ -420,4 +420,5 @@ internal val DaStrings =
"Dette fællesskab er kun synligt i den aktuelle forekomst"
override val noticeBannedUser =
"Den nuværende bruger er blevet udelukket fra dette fællesskab"
override val settingsHiddenPosts = "Skjulte indlæg"
}

View File

@ -424,4 +424,5 @@ internal val DeStrings =
override val noticeCommunityLocalOnly =
"Diese Community ist nur innerhalb der aktuellen Instanz sichtbar"
override val noticeBannedUser = "Der aktuelle Benutzer wurde aus dieser Community gesperrt"
override val settingsHiddenPosts = "Versteckte Beiträge"
}

View File

@ -427,4 +427,5 @@ internal val ElStrings =
override val noticeCommunityLocalOnly =
"Αυτή η κοινότητα είναι ορατή μόνο στην τρέχουσα παρουσία"
override val noticeBannedUser = "Ο τρέχων χρήστης έχει αποκλειστεί από αυτήν την κοινότητα"
override val settingsHiddenPosts = "Κρυφές αναρτήσεις"
}

View File

@ -417,4 +417,5 @@ internal val EnStrings =
override val noticeCommunityLocalOnly =
"This community is only visible within the current instance"
override val noticeBannedUser = "The current user has been banned from this community"
override val settingsHiddenPosts = "Hidden posts"
}

View File

@ -416,4 +416,5 @@ internal val EoStrings =
override val noticeCommunityLocalOnly =
"Ĉi tiu komunumo estas videbla nur ene de la nuna nodo"
override val noticeBannedUser = "La nuna uzanto estas malpermesita de ĉi tiu komunumo"
override val settingsHiddenPosts = "Kaŝitaj afiŝoj"
}

View File

@ -421,4 +421,5 @@ internal val EsStrings =
override val noticeCommunityLocalOnly =
"Esta comunidad sólo es visible dentro de la instancia actual"
override val noticeBannedUser = "El usuario actual ha sido baneado de esta comunidad"
override val settingsHiddenPosts = "Publicaciones escondidas"
}

View File

@ -422,4 +422,5 @@ internal val EtStrings =
override val noticeCommunityLocalOnly =
"See kommuun on nähtav ainult praeguses eksemplaris"
override val noticeBannedUser = "Praegune kasutaja on sellest kogukonnast keelatud"
override val settingsHiddenPosts = "Peidetud postitused"
}

View File

@ -417,4 +417,5 @@ internal val FiStrings =
override val communityVisibilityPublic = "julkinen"
override val noticeCommunityLocalOnly = "Tämä yhteisö näkyy vain nykyisessä ilmentymässä"
override val noticeBannedUser = "Nykyinen käyttäjä on estetty tästä yhteisöstä"
override val settingsHiddenPosts = "Piilotetut viestit"
}

View File

@ -426,4 +426,5 @@ internal val FrStrings =
override val noticeCommunityLocalOnly =
"Cette communauté n\'est visible que dans l\'instance actuelle"
override val noticeBannedUser = "L\'utilisateur actuel a été banni de cette communauté"
override val settingsHiddenPosts = "Messages masqués"
}

View File

@ -421,4 +421,5 @@ internal val GaStrings =
override val communityVisibilityPublic = "poiblí"
override val noticeCommunityLocalOnly = "Níl an pobal seo le feiceáil ach sa chás reatha"
override val noticeBannedUser = "Tá cosc ar an úsáideoir reatha ón bpobal seo"
override val settingsHiddenPosts = "Poist i bhfolach"
}

View File

@ -421,4 +421,5 @@ internal val HrStrings =
override val noticeCommunityLocalOnly =
"Ova je zajednica vidljiva samo unutar trenutne instance"
override val noticeBannedUser = "Trenutačni korisnik je zabranjen iz ove zajednice"
override val settingsHiddenPosts = "Skriveni postovi"
}

View File

@ -425,4 +425,5 @@ internal val HuStrings =
override val noticeCommunityLocalOnly =
"Ez a közösség csak az aktuális példányon belül látható"
override val noticeBannedUser = "A jelenlegi felhasználót kitiltották a közösségből"
override val settingsHiddenPosts = "Rejtett bejegyzések"
}

View File

@ -422,4 +422,5 @@ internal val ItStrings =
override val noticeCommunityLocalOnly =
"Questa comunità è visibile esclusivamente all'interno dell'istanza corrente"
override val noticeBannedUser = "L\'utente corrente è stato bannato da questa comunità"
override val settingsHiddenPosts = "Post nascosti"
}

View File

@ -420,4 +420,5 @@ internal val LtStrings =
"Ši bendruomenė matoma tik dabartiniame egzemplioriuje"
override val noticeBannedUser =
"Dabartinis vartotojas buvo uždraustas dalyvauti šioje bendruomenėje"
override val settingsHiddenPosts = "Paslėpti įrašai"
}

View File

@ -419,4 +419,5 @@ internal val LvStrings =
override val communityVisibilityPublic = "publiski"
override val noticeCommunityLocalOnly = "Šī kopiena ir redzama tikai pašreizējā instancē"
override val noticeBannedUser = "Pašreizējais lietotājs ir bloķēts no šīs kopienas"
override val settingsHiddenPosts = "Slēptās ziņas"
}

View File

@ -421,4 +421,5 @@ internal val MtStrings =
override val noticeCommunityLocalOnly: String =
"Din il-komunità hija viżibbli biss fl-istanza attwali"
override val noticeBannedUser = "L-utent attwali ġie pprojbit minn din il-komunità"
override val settingsHiddenPosts = "Postijiet moħbija"
}

View File

@ -422,4 +422,5 @@ internal val NlStrings =
override val noticeCommunityLocalOnly: String =
"Deze community is alleen zichtbaar binnen het huidige exemplaar"
override val noticeBannedUser = "De huidige gebruiker is uitgesloten van deze community"
override val settingsHiddenPosts = "Verborgen berichten"
}

View File

@ -419,4 +419,5 @@ internal val NoStrings =
"Dette fellesskapet er bare synlig i gjeldende forekomst"
override val noticeBannedUser =
"Den nåværende brukeren har blitt utestengt fra dette fellesskapet"
override val settingsHiddenPosts = "Skjulte innlegg"
}

View File

@ -421,4 +421,5 @@ internal val PlStrings =
override val noticeCommunityLocalOnly =
"Ta społeczność jest widoczna tylko w bieżącej instancji"
override val noticeBannedUser = "Bieżący użytkownik został zablokowany w tej społeczności"
override val settingsHiddenPosts = "Ukryte posty"
}

View File

@ -419,4 +419,5 @@ internal val PtBrStrings =
override val communityVisibilityPublic = "pública"
override val noticeCommunityLocalOnly = "Esta comunidade só é visível na instância atual"
override val noticeBannedUser = "O usuário atual foi banido desta comunidade"
override val settingsHiddenPosts = "Posts escondidos"
}

View File

@ -420,4 +420,5 @@ internal val PtStrings =
override val communityVisibilityPublic = "pública"
override val noticeCommunityLocalOnly = "Esta comunidade só é visível na instância atual"
override val noticeBannedUser = "O usuário atual foi banido desta comunidade"
override val settingsHiddenPosts = "Postagens ocultadas"
}

View File

@ -420,4 +420,5 @@ internal val RoStrings =
override val noticeCommunityLocalOnly =
"Această comunitate este vizibilă numai în instanța curentă"
override val noticeBannedUser = "Utilizatorul actual a fost exclus din această comunitate"
override val settingsHiddenPosts = "Postări ascunse"
}

View File

@ -422,4 +422,5 @@ internal val RuStrings =
override val communityVisibilityPublic = "общественный"
override val noticeCommunityLocalOnly = "Это сообщество видно только в текущем экземпляре"
override val noticeBannedUser = "Текущий пользователь заблокирован в этом сообществе"
override val settingsHiddenPosts = "Скрытые сообщения"
}

View File

@ -420,4 +420,5 @@ internal val SeStrings =
override val noticeCommunityLocalOnly =
"Denna grupp är endast synlig inom den aktuella instansen"
override val noticeBannedUser = "Den nuvarande användaren har blockerats från denna grupp"
override val settingsHiddenPosts = "Dolda inlägg"
}

View File

@ -422,4 +422,5 @@ internal val SkStrings =
override val noticeCommunityLocalOnly =
"Táto komunita je viditeľná iba v rámci aktuálnej inštancie"
override val noticeBannedUser = "Aktuálny používateľ má zakázaný prístup do tejto komunity"
override val settingsHiddenPosts = "Skryté príspevky"
}

View File

@ -420,4 +420,5 @@ internal val SlStrings =
override val noticeCommunityLocalOnly =
"Ta skupnost je vidna samo znotraj trenutne instance"
override val noticeBannedUser = "Trenutni uporabnik je bil izključen iz te skupnosti"
override val settingsHiddenPosts = "Skrite objave"
}

View File

@ -422,4 +422,5 @@ internal val SqStrings =
override val noticeCommunityLocalOnly =
"Ky komunitet është i dukshëm vetëm brenda shembullit aktual"
override val noticeBannedUser = "Përdoruesi aktual është përjashtuar nga ky komunitet"
override val settingsHiddenPosts = "Postimet e fshehura"
}

View File

@ -422,4 +422,5 @@ internal val SrStrings =
override val noticeCommunityLocalOnly =
"Ова заједница је видљива само у оквиру тренутне инстанце"
override val noticeBannedUser = "Тренутном кориснику је забрањен приступ овој заједници"
override val settingsHiddenPosts = "Скривени постови"
}

View File

@ -415,6 +415,7 @@ interface Strings {
val communityVisibilityPublic: String
val noticeCommunityLocalOnly: String
val noticeBannedUser: String
val settingsHiddenPosts: String
}
object Locales {

View File

@ -416,4 +416,5 @@ internal val TokStrings =
override val communityVisibilityPublic = "tawa ale"
override val noticeCommunityLocalOnly = "kulupu ni li ken lukin lon ilo nanpa ni taso"
override val noticeBannedUser = "sina ken lukin ala e lipu mute pi kulupu ni"
override val settingsHiddenPosts = "lipu pi lukin ala"
}

View File

@ -421,4 +421,5 @@ internal val TrStrings =
override val noticeCommunityLocalOnly =
"Bu topluluk yalnızca mevcut örnekte görülebilir"
override val noticeBannedUser = "Mevcut kullanıcı bu topluluktan yasaklandı"
override val settingsHiddenPosts = "Gizli gönderiler"
}

View File

@ -422,4 +422,5 @@ internal val UkStrings =
override val noticeCommunityLocalOnly =
"Цю спільноту можна побачити лише в поточному екземплярі"
override val noticeBannedUser = "Поточний користувач був забанений у цій спільноті"
override val settingsHiddenPosts = "Приховані пости"
}

View File

@ -27,8 +27,7 @@ internal class DefaultPostPaginationManager(
private val multiCommunityPaginator: MultiCommunityPaginator,
dispatcher: CoroutineDispatcher = Dispatchers.IO,
notificationCenter: NotificationCenter,
) : PostPaginationManager {
) : PostPaginationManager {
override var canFetchMore: Boolean = true
private set
override val history: MutableList<PostModel> = mutableListOf()
@ -197,6 +196,27 @@ internal class DefaultPostPaginationManager(
.deduplicate()
.filterDeleted()
}
is PostPaginationSpecification.Hidden -> {
val (itemList, nextPage) =
userRepository.getHiddenPosts(
auth = auth,
page = currentPage,
pageCursor = pageCursor,
sort = SortType.New,
) ?: (null to null)
if (!itemList.isNullOrEmpty()) {
currentPage++
}
if (nextPage != null) {
pageCursor = nextPage
}
canFetchMore = itemList?.isEmpty() != true
itemList
.orEmpty()
.deduplicate()
.filterDeleted()
}
}
history.addAll(result)

View File

@ -41,4 +41,8 @@ sealed interface PostPaginationSpecification {
data class Saved(
val sortType: SortType = SortType.Active,
) : PostPaginationSpecification
data class Hidden(
val sortType: SortType = SortType.Active,
) : PostPaginationSpecification
}

View File

@ -0,0 +1,56 @@
package com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository
import com.github.diegoberaldin.raccoonforlemmy.core.testutils.DispatcherTestRule
import io.mockk.coEvery
import io.mockk.mockk
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class DefaultGetSiteSupportsHiddenPostsUseCaseTest {
@get:Rule
val dispatcherTestRule = DispatcherTestRule()
private val isSiteVersionAtLeastUseCase: IsSiteVersionAtLeastUseCase = mockk()
private val sut =
DefaultGetSiteSupportsHiddenPostsUseCase(
isSiteVersionAtLeastUseCase = isSiteVersionAtLeastUseCase,
)
@Test
fun givenVersionAboveThreshold_whenInvoke_thenResultsIsAsExpected() =
runTest {
coEvery {
isSiteVersionAtLeastUseCase.execute(
major = any(),
minor = any(),
patch = any(),
otherInstance = any(),
)
} returns true
val res = sut.invoke()
assertTrue(res)
}
@Test
fun givenVersionBelowThreshold_whenInvoke_thenResultsIsAsExpected() =
runTest {
coEvery {
isSiteVersionAtLeastUseCase.execute(
major = any(),
minor = any(),
patch = any(),
otherInstance = any(),
)
} returns false
val res = sut.invoke()
assertFalse(res)
}
}

View File

@ -14,187 +14,122 @@ class DefaultGetSortTypesUseCaseTest {
@get:Rule
val dispatcherTestRule = DispatcherTestRule()
private val siteRepository: SiteRepository = mockk(relaxUnitFun = true)
private val isSiteVersionAtLeastUseCase: IsSiteVersionAtLeastUseCase = mockk()
private val sut = DefaultGetSortTypesUseCase(
siteRepository = siteRepository,
)
private val sut =
DefaultGetSortTypesUseCase(
isSiteVersionAtLeastUseCase = isSiteVersionAtLeastUseCase,
)
@Test
fun givenVersionEqualTo019_whenGetTypesForPosts_thenResultsIsAsExpected() = runTest {
coEvery {
siteRepository.getSiteVersion(
auth = any(),
otherInstance = any()
)
} returns "0.19"
fun givenVersionEqualToThreshold_whenGetTypesForPosts_thenResultsIsAsExpected() =
runTest {
coEvery {
isSiteVersionAtLeastUseCase.execute(any(), any(), any(), any())
} returns true
val res = sut.getTypesForPosts()
val res = sut.getTypesForPosts()
assertTrue(res.contains(SortType.Controversial))
assertTrue(res.contains(SortType.Scaled))
assertTrue(res.contains(SortType.Active))
assertTrue(res.contains(SortType.Hot))
assertTrue(res.contains(SortType.New))
assertTrue(res.contains(SortType.Old))
assertTrue(res.contains(SortType.MostComments))
assertTrue(res.contains(SortType.NewComments))
assertTrue(res.contains(SortType.Top.Generic))
}
assertTrue(res.contains(SortType.Controversial))
assertTrue(res.contains(SortType.Scaled))
assertTrue(res.contains(SortType.Active))
assertTrue(res.contains(SortType.Hot))
assertTrue(res.contains(SortType.New))
assertTrue(res.contains(SortType.Old))
assertTrue(res.contains(SortType.MostComments))
assertTrue(res.contains(SortType.NewComments))
assertTrue(res.contains(SortType.Top.Generic))
}
@Test
fun givenVersionGreaterThan019WithPatch_whenGetTypesForPosts_thenResultsIsAsExpected() = runTest {
coEvery {
siteRepository.getSiteVersion(
auth = any(),
otherInstance = any()
)
} returns "0.19.2"
fun givenVersionGreaterThanThreshold_whenGetTypesForPosts_thenResultsIsAsExpected() =
runTest {
coEvery {
isSiteVersionAtLeastUseCase.execute(any(), any(), any(), any())
} returns true
val res = sut.getTypesForPosts()
val res = sut.getTypesForPosts()
assertTrue(res.contains(SortType.Controversial))
assertTrue(res.contains(SortType.Scaled))
assertTrue(res.contains(SortType.Active))
assertTrue(res.contains(SortType.Hot))
assertTrue(res.contains(SortType.New))
assertTrue(res.contains(SortType.Old))
assertTrue(res.contains(SortType.MostComments))
assertTrue(res.contains(SortType.NewComments))
assertTrue(res.contains(SortType.Top.Generic))
}
assertTrue(res.contains(SortType.Controversial))
assertTrue(res.contains(SortType.Scaled))
assertTrue(res.contains(SortType.Active))
assertTrue(res.contains(SortType.Hot))
assertTrue(res.contains(SortType.New))
assertTrue(res.contains(SortType.Old))
assertTrue(res.contains(SortType.MostComments))
assertTrue(res.contains(SortType.NewComments))
assertTrue(res.contains(SortType.Top.Generic))
}
@Test
fun givenVersionGreaterThan019WithMinor_whenGetTypesForPosts_thenResultsIsAsExpected() = runTest {
coEvery {
siteRepository.getSiteVersion(
auth = any(),
otherInstance = any()
)
} returns "0.20"
fun givenVersionLessThanThreshold_whenGetTypesForPosts_thenResultsIsAsExpected() =
runTest {
coEvery {
isSiteVersionAtLeastUseCase.execute(any(), any(), any(), any())
} returns false
val res = sut.getTypesForPosts()
val res = sut.getTypesForPosts()
assertTrue(res.contains(SortType.Controversial))
assertTrue(res.contains(SortType.Scaled))
assertTrue(res.contains(SortType.Active))
assertTrue(res.contains(SortType.Hot))
assertTrue(res.contains(SortType.New))
assertTrue(res.contains(SortType.Old))
assertTrue(res.contains(SortType.MostComments))
assertTrue(res.contains(SortType.NewComments))
assertTrue(res.contains(SortType.Top.Generic))
}
assertFalse(res.contains(SortType.Controversial))
assertFalse(res.contains(SortType.Scaled))
assertTrue(res.contains(SortType.Active))
assertTrue(res.contains(SortType.Hot))
assertTrue(res.contains(SortType.New))
assertTrue(res.contains(SortType.Old))
assertTrue(res.contains(SortType.MostComments))
assertTrue(res.contains(SortType.NewComments))
assertTrue(res.contains(SortType.Top.Generic))
}
@Test
fun givenVersionLessThan019_whenGetTypesForPosts_thenResultsIsAsExpected() = runTest {
coEvery {
siteRepository.getSiteVersion(
auth = any(),
otherInstance = any()
)
} returns "0.18.5"
fun givenVersionGreaterThanThreshold_whenGetTypesForComments_thenResultsIsAsExpected() =
runTest {
coEvery {
isSiteVersionAtLeastUseCase.execute(any(), any(), any(), any())
} returns true
val res = sut.getTypesForPosts()
val res = sut.getTypesForComments()
assertFalse(res.contains(SortType.Controversial))
assertFalse(res.contains(SortType.Scaled))
assertTrue(res.contains(SortType.Active))
assertTrue(res.contains(SortType.Hot))
assertTrue(res.contains(SortType.New))
assertTrue(res.contains(SortType.Old))
assertTrue(res.contains(SortType.MostComments))
assertTrue(res.contains(SortType.NewComments))
assertTrue(res.contains(SortType.Top.Generic))
}
assertTrue(res.contains(SortType.Controversial))
assertTrue(res.contains(SortType.Hot))
assertTrue(res.contains(SortType.New))
assertTrue(res.contains(SortType.Old))
assertTrue(res.contains(SortType.Top.Generic))
}
@Test
fun givenVersionEqualTo019_whenGetTypesForComments_thenResultsIsAsExpected() = runTest {
coEvery {
siteRepository.getSiteVersion(
auth = any(),
otherInstance = any()
)
} returns "0.19"
fun givenVersionLessThanThreshold_whenGetTypesForComments_thenResultsIsAsExpected() =
runTest {
coEvery {
isSiteVersionAtLeastUseCase.execute(any(), any(), any(), any())
} returns false
val res = sut.getTypesForComments()
val res = sut.getTypesForComments()
assertTrue(res.contains(SortType.Controversial))
assertTrue(res.contains(SortType.Hot))
assertTrue(res.contains(SortType.New))
assertTrue(res.contains(SortType.Old))
assertTrue(res.contains(SortType.Top.Generic))
}
assertFalse(res.contains(SortType.Controversial))
assertTrue(res.contains(SortType.Hot))
assertTrue(res.contains(SortType.New))
assertTrue(res.contains(SortType.Old))
assertTrue(res.contains(SortType.Top.Generic))
}
@Test
fun givenVersionGreaterThan019WithPatch_whenGetTypesForComments_thenResultsIsAsExpected() = runTest {
coEvery {
siteRepository.getSiteVersion(
auth = any(),
otherInstance = any()
)
} returns "0.19.2"
fun whenGetTypesForCommunities_thenResultsIsAsExpected() =
runTest {
val res = sut.getTypesForCommunities()
val res = sut.getTypesForComments()
assertTrue(res.contains(SortType.Controversial))
assertTrue(res.contains(SortType.Hot))
assertTrue(res.contains(SortType.New))
assertTrue(res.contains(SortType.Old))
assertTrue(res.contains(SortType.Top.Generic))
}
assertTrue(res.contains(SortType.Active))
assertTrue(res.contains(SortType.New))
assertTrue(res.contains(SortType.MostComments))
}
@Test
fun givenVersionGreaterThan019WithMinor_whenGetTypesForComments_thenResultsIsAsExpected() = runTest {
coEvery {
siteRepository.getSiteVersion(
auth = any(),
otherInstance = any()
)
} returns "0.20"
fun whenGetTypesForSavedItems_thenResultsIsAsExpected() =
runTest {
val res = sut.getTypesForSavedItems()
val res = sut.getTypesForPosts()
assertTrue(res.contains(SortType.Controversial))
assertTrue(res.contains(SortType.Hot))
assertTrue(res.contains(SortType.New))
assertTrue(res.contains(SortType.Old))
assertTrue(res.contains(SortType.Top.Generic))
}
@Test
fun givenVersionLessThan019_whenGetTypesForComments_thenResultsIsAsExpected() = runTest {
coEvery {
siteRepository.getSiteVersion(
auth = any(),
otherInstance = any()
)
} returns "0.18.5"
val res = sut.getTypesForComments()
assertFalse(res.contains(SortType.Controversial))
assertTrue(res.contains(SortType.Hot))
assertTrue(res.contains(SortType.New))
assertTrue(res.contains(SortType.Old))
assertTrue(res.contains(SortType.Top.Generic))
}
@Test
fun whenGetTypesForCommunities_thenResultsIsAsExpected() = runTest {
val res = sut.getTypesForCommunities()
assertTrue(res.contains(SortType.Active))
assertTrue(res.contains(SortType.New))
assertTrue(res.contains(SortType.MostComments))
}
@Test
fun whenGetTypesForSavedItems_thenResultsIsAsExpected() = runTest {
val res = sut.getTypesForSavedItems()
assertTrue(res.contains(SortType.Hot))
assertTrue(res.contains(SortType.New))
assertTrue(res.contains(SortType.Old))
}
}
assertTrue(res.contains(SortType.Hot))
assertTrue(res.contains(SortType.New))
assertTrue(res.contains(SortType.Old))
}
}

View File

@ -0,0 +1,103 @@
package com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository
import com.github.diegoberaldin.raccoonforlemmy.core.testutils.DispatcherTestRule
import io.mockk.coEvery
import io.mockk.mockk
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class DefaultIsSiteVersionAtLeastUseCaseTest {
@get:Rule
val dispatcherTestRule = DispatcherTestRule()
private val siteRepository = mockk<SiteRepository>()
private val sut = DefaultIsSiteVersionAtLeastUseCase(siteRepository = siteRepository)
@Test
fun givenSameVersion_whenExecute_thenResultIsAsExpected() =
runTest {
coEvery {
siteRepository.getSiteVersion()
} returns "0.19.2"
val res = sut.execute(0, 19, 2)
assertTrue(res)
}
@Test
fun givenPatchLessThanThreshold_whenExecute_thenResultIsAsExpected() =
runTest {
coEvery {
siteRepository.getSiteVersion()
} returns "0.19.2"
val res = sut.execute(0, 19, 3)
assertFalse(res)
}
@Test
fun givenPatchGreaterThanThreshold_whenExecute_thenResultIsAsExpected() =
runTest {
coEvery {
siteRepository.getSiteVersion()
} returns "0.19.2"
val res = sut.execute(0, 19, 1)
assertTrue(res)
}
@Test
fun givenMinorLessThanThreshold_whenExecute_thenResultIsAsExpected() =
runTest {
coEvery {
siteRepository.getSiteVersion()
} returns "0.18.2"
val res = sut.execute(0, 19, 1)
assertFalse(res)
}
@Test
fun givenMinorGreaterThanThreshold_whenExecute_thenResultIsAsExpected() =
runTest {
coEvery {
siteRepository.getSiteVersion()
} returns "0.20.2"
val res = sut.execute(0, 19, 1)
assertTrue(res)
}
@Test
fun givenMajorLessThanThreshold_whenExecute_thenResultIsAsExpected() =
runTest {
coEvery {
siteRepository.getSiteVersion()
} returns "0.2.1"
val res = sut.execute(1, 1, 1)
assertFalse(res)
}
@Test
fun givenMajorGreaterThanThreshold_whenExecute_thenResultIsAsExpected() =
runTest {
coEvery {
siteRepository.getSiteVersion()
} returns "1.1.0"
val res = sut.execute(0, 19, 1)
assertTrue(res)
}
}

View File

@ -0,0 +1,18 @@
package com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository
internal class DefaultGetSiteSupportsHiddenPostsUseCase(
private val isSiteVersionAtLeastUseCase: IsSiteVersionAtLeastUseCase,
) : GetSiteSupportsHiddenPostsUseCase {
companion object {
const val THRESHOLD_MAJOR = 0
const val THRESHOLD_MINOR = 19
const val THRESHOLD_PATCH = 4
}
override suspend fun invoke(): Boolean =
isSiteVersionAtLeastUseCase.execute(
major = THRESHOLD_MAJOR,
minor = THRESHOLD_MINOR,
patch = THRESHOLD_PATCH,
)
}

View File

@ -3,49 +3,50 @@ package com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.SortType
internal class DefaultGetSortTypesUseCase(
private val siteRepository: SiteRepository,
private val isSiteVersionAtLeastUseCase: IsSiteVersionAtLeastUseCase,
) : GetSortTypesUseCase {
companion object {
private const val THRESHOLD_MAJOR = 0
private const val THRESHOLD_MINOR = 19
private val LEMMY_VERSION_REGEX = Regex("(?<major>\\d+).(?<minor>\\d+)(.(?<patch>\\d+))?")
}
override suspend fun getTypesForPosts(otherInstance: String?): List<SortType> {
val version = siteRepository.getSiteVersion(otherInstance = otherInstance).orEmpty()
val matchResult = LEMMY_VERSION_REGEX.find(version)
val major = matchResult?.groups?.get("major")?.value?.toIntOrNull() ?: 0
val minor = matchResult?.groups?.get("minor")?.value?.toIntOrNull() ?: 0
return buildList {
override suspend fun getTypesForPosts(otherInstance: String?): List<SortType> =
buildList {
val isAtLeastThreshold =
isSiteVersionAtLeastUseCase.execute(
major = THRESHOLD_MAJOR,
minor = THRESHOLD_MINOR,
otherInstance = otherInstance,
)
this += SortType.Active
this += SortType.Hot
this += SortType.New
this += SortType.NewComments
this += SortType.MostComments
this += SortType.Old
if (major >= THRESHOLD_MAJOR && minor >= THRESHOLD_MINOR) {
if (isAtLeastThreshold) {
this += SortType.Controversial
this += SortType.Scaled
}
this += SortType.Top.Generic
}
}
override suspend fun getTypesForComments(otherInstance: String?): List<SortType> {
val version = siteRepository.getSiteVersion(otherInstance = otherInstance).orEmpty()
val matchResult = LEMMY_VERSION_REGEX.find(version)
val major = matchResult?.groups?.get("major")?.value?.toIntOrNull() ?: 0
val minor = matchResult?.groups?.get("minor")?.value?.toIntOrNull() ?: 0
return buildList {
override suspend fun getTypesForComments(otherInstance: String?): List<SortType> =
buildList {
val isAtLeastThreshold =
isSiteVersionAtLeastUseCase.execute(
major = THRESHOLD_MAJOR,
minor = THRESHOLD_MINOR,
otherInstance = otherInstance,
)
this += SortType.Hot
this += SortType.New
this += SortType.Old
if (major >= THRESHOLD_MAJOR && minor >= THRESHOLD_MINOR) {
if (isAtLeastThreshold) {
this += SortType.Controversial
}
this += SortType.Top.Generic
}
}
override suspend fun getTypesForCommunities(otherInstance: String?): List<SortType> =
buildList {

View File

@ -0,0 +1,30 @@
package com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository
internal class DefaultIsSiteVersionAtLeastUseCase(
private val siteRepository: SiteRepository,
) : IsSiteVersionAtLeastUseCase {
companion object {
private val LEMMY_VERSION_REGEX = Regex("(?<major>\\d+).(?<minor>\\d+)(.(?<patch>\\d+))?")
}
override suspend fun execute(
major: Int,
minor: Int,
patch: Int,
otherInstance: String?,
): Boolean {
val version = siteRepository.getSiteVersion(otherInstance = otherInstance).orEmpty()
val matchResult = LEMMY_VERSION_REGEX.find(version)
val actualMajor = matchResult?.groups?.get("major")?.value?.toIntOrNull() ?: 0
val actualMinor = matchResult?.groups?.get("minor")?.value?.toIntOrNull() ?: 0
val actualPatch = matchResult?.groups?.get("patch")?.value?.toIntOrNull() ?: 0
return when {
actualMajor < major -> false
actualMajor > major -> true
actualMinor < minor -> false
actualMinor > minor -> true
actualPatch < patch -> false
else -> true
}
}
}

View File

@ -379,4 +379,29 @@ internal class DefaultUserRepository(
)
require(response.success)
}
override suspend fun getHiddenPosts(
auth: String?,
page: Int,
pageCursor: String?,
limit: Int,
sort: SortType,
): Pair<List<PostModel>, String?>? =
withContext(Dispatchers.IO) {
runCatching {
val response =
services.post.getAll(
authHeader = auth.toAuthHeader(),
auth = auth,
page = page,
pageCursor = pageCursor,
limit = limit,
sort = sort.toDto(),
type = ListingType.All,
showHidden = true,
)
val posts = response.posts.map { it.toModel() }
posts to response.nextPage
}.getOrNull()
}
}

View File

@ -0,0 +1,5 @@
package com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository
interface GetSiteSupportsHiddenPostsUseCase {
suspend operator fun invoke(): Boolean
}

View File

@ -0,0 +1,10 @@
package com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository
interface IsSiteVersionAtLeastUseCase {
suspend fun execute(
major: Int,
minor: Int = 0,
patch: Int = 0,
otherInstance: String? = null,
): Boolean
}

View File

@ -119,4 +119,12 @@ interface UserRepository {
id: Long,
reason: String? = null,
)
suspend fun getHiddenPosts(
auth: String?,
page: Int,
pageCursor: String? = null,
limit: Int = PostRepository.DEFAULT_PAGE_SIZE,
sort: SortType = SortType.New,
): Pair<List<PostModel>, String?>?
}

View File

@ -5,14 +5,18 @@ import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.CommentR
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.CommunityRepository
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.DefaultCommentRepository
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.DefaultCommunityRepository
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.DefaultGetSiteSupportsHiddenPostsUseCase
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.DefaultGetSortTypesUseCase
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.DefaultIsSiteVersionAtLeastUseCase
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.DefaultLemmyItemCache
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.DefaultModlogRepository
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.DefaultPostRepository
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.DefaultPrivateMessageRepository
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.DefaultSiteRepository
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.DefaultUserRepository
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.GetSiteSupportsHiddenPostsUseCase
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.GetSortTypesUseCase
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.IsSiteVersionAtLeastUseCase
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.LemmyItemCache
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.ModlogRepository
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.PostRepository
@ -26,9 +30,14 @@ private const val CACHE_SIZE = 5
val repositoryModule =
module {
single<IsSiteVersionAtLeastUseCase> {
DefaultIsSiteVersionAtLeastUseCase(
siteRepository = get(),
)
}
single<GetSortTypesUseCase> {
DefaultGetSortTypesUseCase(
siteRepository = get(),
isSiteVersionAtLeastUseCase = get(),
)
}
single<PostRepository> {
@ -79,4 +88,9 @@ val repositoryModule =
userCache = LruCache.factory(CACHE_SIZE),
)
}
single<GetSiteSupportsHiddenPostsUseCase> {
DefaultGetSiteSupportsHiddenPostsUseCase(
isSiteVersionAtLeastUseCase = get(),
)
}
}

View File

@ -68,6 +68,7 @@ kotlin {
implementation(projects.unit.choosefont)
implementation(projects.unit.configureswipeactions)
implementation(projects.unit.configurecontentview)
implementation(projects.unit.filteredcontents)
}
}
val commonTest by getting {

View File

@ -23,6 +23,7 @@ val settingsTabModule =
crashReportConfiguration = get(),
getSortTypesUseCase = get(),
customTabsHelper = get(),
siteSupportsHiddenPosts = get(),
)
}
factory<SettingsColorAndFontMviModel> {

View File

@ -39,6 +39,7 @@ interface SettingsMviModel :
val availableSortTypesForPosts: List<SortType> = emptyList(),
val availableSortTypesForComments: List<SortType> = emptyList(),
val customTabsEnabled: Boolean = true,
val supportsHiddenPosts: Boolean = false,
)
sealed interface Effect

View File

@ -72,6 +72,9 @@ import com.github.diegoberaldin.raccoonforlemmy.unit.about.AboutDialog
import com.github.diegoberaldin.raccoonforlemmy.unit.accountsettings.AccountSettingsScreen
import com.github.diegoberaldin.raccoonforlemmy.unit.configurecontentview.ConfigureContentViewScreen
import com.github.diegoberaldin.raccoonforlemmy.unit.configureswipeactions.ConfigureSwipeActionsScreen
import com.github.diegoberaldin.raccoonforlemmy.unit.filteredcontents.FilteredContentsScreen
import com.github.diegoberaldin.raccoonforlemmy.unit.filteredcontents.FilteredContentsType
import com.github.diegoberaldin.raccoonforlemmy.unit.filteredcontents.toInt
import com.github.diegoberaldin.raccoonforlemmy.unit.manageban.ManageBanScreen
import com.github.diegoberaldin.raccoonforlemmy.unit.web.WebViewScreen
import kotlinx.coroutines.flow.launchIn
@ -342,6 +345,21 @@ class SettingsScreen : Screen {
navigationCoordinator.pushScreen(screen)
},
)
if (uiState.supportsHiddenPosts) {
SettingsRow(
title = LocalStrings.current.settingsHiddenPosts,
disclosureIndicator = true,
onTap =
rememberCallback {
val screen =
FilteredContentsScreen(
type = FilteredContentsType.Hidden.toInt(),
)
navigationCoordinator.pushScreen(screen)
},
)
}
}
SettingsHeader(

View File

@ -22,6 +22,7 @@ import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.SortType
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.toInt
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.toListingType
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.toSortType
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.GetSiteSupportsHiddenPostsUseCase
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.GetSortTypesUseCase
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@ -37,6 +38,7 @@ class SettingsViewModel(
private val l10nManager: L10nManager,
private val getSortTypesUseCase: GetSortTypesUseCase,
private val customTabsHelper: CustomTabsHelper,
private val siteSupportsHiddenPosts: GetSiteSupportsHiddenPostsUseCase,
) : SettingsMviModel,
DefaultMviModel<SettingsMviModel.Intent, SettingsMviModel.UiState, SettingsMviModel.Effect>(
initialState = SettingsMviModel.UiState(),
@ -90,10 +92,12 @@ class SettingsViewModel(
val availableSortTypesForPosts = getSortTypesUseCase.getTypesForPosts()
val availableSortTypesForComments = getSortTypesUseCase.getTypesForComments()
val supportsHiddenPosts = siteSupportsHiddenPosts()
updateState {
it.copy(
availableSortTypesForPosts = availableSortTypesForPosts,
availableSortTypesForComments = availableSortTypesForComments,
supportsHiddenPosts = supportsHiddenPosts,
)
}

View File

@ -14,6 +14,8 @@ sealed interface FilteredContentsType {
data object Moderated : FilteredContentsType
data object Bookmarks : FilteredContentsType
data object Hidden : FilteredContentsType
}
fun FilteredContentsType.toInt(): Int =
@ -21,10 +23,12 @@ fun FilteredContentsType.toInt(): Int =
FilteredContentsType.Moderated -> 0
FilteredContentsType.Votes -> 1
FilteredContentsType.Bookmarks -> 2
FilteredContentsType.Hidden -> 3
}
fun Int.toFilteredContentsType(): FilteredContentsType =
when (this) {
3 -> FilteredContentsType.Hidden
2 -> FilteredContentsType.Bookmarks
1 -> FilteredContentsType.Votes
else -> FilteredContentsType.Moderated
@ -92,6 +96,7 @@ interface FilteredContentsMviModel :
val fadeReadPosts: Boolean = false,
val showUnreadComments: Boolean = false,
val downVoteEnabled: Boolean = true,
val isPostOnly: Boolean = false,
val actionsOnSwipeToStartPosts: List<ActionOnSwipe> = emptyList(),
val actionsOnSwipeToEndPosts: List<ActionOnSwipe> = emptyList(),
val actionsOnSwipeToStartComments: List<ActionOnSwipe> = emptyList(),

View File

@ -183,6 +183,7 @@ class FilteredContentsScreen(
FilteredContentsType.Moderated -> LocalStrings.current.moderatorZoneActionContents
FilteredContentsType.Votes -> LocalStrings.current.profileUpvotesDownvotes
FilteredContentsType.Bookmarks -> LocalStrings.current.navigationDrawerTitleBookmarks
FilteredContentsType.Hidden -> LocalStrings.current.settingsHiddenPosts
},
style = MaterialTheme.typography.titleMedium,
)
@ -266,26 +267,28 @@ class FilteredContentsScreen(
),
verticalArrangement = Arrangement.spacedBy(Spacing.s),
) {
SectionSelector(
titles =
listOf(
LocalStrings.current.profileSectionPosts,
LocalStrings.current.profileSectionComments,
),
currentSection =
when (uiState.section) {
FilteredContentsSection.Comments -> 1
else -> 0
if (!uiState.isPostOnly) {
SectionSelector(
titles =
listOf(
LocalStrings.current.profileSectionPosts,
LocalStrings.current.profileSectionComments,
),
currentSection =
when (uiState.section) {
FilteredContentsSection.Comments -> 1
else -> 0
},
onSectionSelected = {
val section =
when (it) {
1 -> FilteredContentsSection.Comments
else -> FilteredContentsSection.Posts
}
model.reduce(FilteredContentsMviModel.Intent.ChangeSection(section))
},
onSectionSelected = {
val section =
when (it) {
1 -> FilteredContentsSection.Comments
else -> FilteredContentsSection.Posts
}
model.reduce(FilteredContentsMviModel.Intent.ChangeSection(section))
},
)
)
}
Box(
modifier =

View File

@ -49,10 +49,12 @@ class FilteredContentsViewModel(
init {
screenModelScope.launch {
updateState {
it.copy(contentsType = contentsType.toFilteredContentsType())
}
updateState {
it.copy(isAdmin = identityRepository.cachedUser?.admin == true)
val type = contentsType.toFilteredContentsType()
it.copy(
contentsType = type,
isAdmin = identityRepository.cachedUser?.admin == true,
isPostOnly = type == FilteredContentsType.Hidden,
)
}
themeRepository.postLayout.onEach { layout ->
updateState { it.copy(postLayout = layout) }
@ -76,12 +78,14 @@ class FilteredContentsViewModel(
}
}.launchIn(this)
notificationCenter.subscribe(NotificationCenterEvent.ChangedLikedType::class).onEach { evt ->
changeLiked(evt.value)
}.launchIn(this)
notificationCenter.subscribe(NotificationCenterEvent.ChangedLikedType::class)
.onEach { evt ->
changeLiked(evt.value)
}.launchIn(this)
if (uiState.value.initial) {
val downVoteEnabled = siteRepository.isDownVoteEnabled(identityRepository.authToken.value)
val downVoteEnabled =
siteRepository.isDownVoteEnabled(identityRepository.authToken.value)
updateState { it.copy(downVoteEnabled = downVoteEnabled) }
refresh(initial = true)
}
@ -208,6 +212,11 @@ class FilteredContentsViewModel(
PostPaginationSpecification.Saved(
sortType = SortType.New,
)
FilteredContentsType.Hidden ->
PostPaginationSpecification.Hidden(
sortType = SortType.New,
)
}
postPaginationManager.reset(postSpecification)
val commentSpecification =
@ -228,8 +237,12 @@ class FilteredContentsViewModel(
CommentPaginationSpecification.Saved(
sortType = SortType.New,
)
FilteredContentsType.Hidden -> null
}
commentPaginationManager.reset(commentSpecification)
if (commentSpecification != null) {
commentPaginationManager.reset(commentSpecification)
}
updateState {
it.copy(
canFetchMore = true,
@ -267,15 +280,19 @@ class FilteredContentsViewModel(
postPaginationManager.loadNextPage()
}.await()
val comments =
async {
if (currentState.comments.isEmpty() || refreshing) {
// this is needed because otherwise on first selector change
// the lazy column scrolls back to top (it must have an empty data set)
commentPaginationManager.loadNextPage()
} else {
currentState.comments
}
}.await()
if (currentState.isPostOnly) {
emptyList()
} else {
async {
if (currentState.comments.isEmpty() || refreshing) {
// this is needed because otherwise on first selector change
// the lazy column scrolls back to top (it must have an empty data set)
commentPaginationManager.loadNextPage()
} else {
currentState.comments
}
}.await()
}
if (uiState.value.autoLoadImages) {
posts.forEach { post ->
post.imageUrl.takeIf { i -> i.isNotEmpty() }?.also { url ->