feat: change favorites from navigation drawer (#993)

This commit is contained in:
Diego Beraldin 2024-06-17 09:35:02 +02:00 committed by GitHub
parent 98ad71100a
commit 005b2cbdbc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
54 changed files with 742 additions and 419 deletions

View File

@ -416,4 +416,5 @@ internal val ArStrings =
override val noticeBannedUser = "تم حظر المستخدم الحالي من هذا المجتمع" override val noticeBannedUser = "تم حظر المستخدم الحالي من هذا المجتمع"
override val settingsHiddenPosts = "المشاركات المخفية" override val settingsHiddenPosts = "المشاركات المخفية"
override val settingsMediaList = "تحميلات الوسائط" override val settingsMediaList = "تحميلات الوسائط"
override val settingsEnableToggleFavoriteInNavDrawer = "إضافة/إزالة المفضلة في درج التنقل"
} }

View File

@ -424,4 +424,6 @@ internal val BgStrings =
override val noticeBannedUser = "Настоящият потребител е забранен от тази общност" override val noticeBannedUser = "Настоящият потребител е забранен от тази общност"
override val settingsHiddenPosts = "Скрити публикации" override val settingsHiddenPosts = "Скрити публикации"
override val settingsMediaList = "Качване на мултимедия" override val settingsMediaList = "Качване на мултимедия"
override val settingsEnableToggleFavoriteInNavDrawer =
"Добавяне/премахване на любими в чекмеджето за навигация"
} }

View File

@ -420,4 +420,6 @@ internal val CsStrings =
override val noticeBannedUser = "Aktuálnímu uživateli byl zakázán přístup do této komunity" 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" override val settingsHiddenPosts = "Skryté příspěvky"
override val settingsMediaList = "Nahrávání médií" override val settingsMediaList = "Nahrávání médií"
override val settingsEnableToggleFavoriteInNavDrawer =
"Přidat/odebrat oblíbené položky v navigační zásuvce"
} }

View File

@ -420,4 +420,6 @@ internal val DaStrings =
"Den nuværende bruger er blevet udelukket fra dette fællesskab" "Den nuværende bruger er blevet udelukket fra dette fællesskab"
override val settingsHiddenPosts = "Skjulte indlæg" override val settingsHiddenPosts = "Skjulte indlæg"
override val settingsMediaList = "Medieuploads" override val settingsMediaList = "Medieuploads"
override val settingsEnableToggleFavoriteInNavDrawer =
"Tilføj/fjern favoritter i navigationsskuffen"
} }

View File

@ -423,4 +423,6 @@ internal val DeStrings =
override val contentScaleFit = "Angepasste Größe" override val contentScaleFit = "Angepasste Größe"
override val contentScaleFillWidth = "Breite füllen" override val contentScaleFillWidth = "Breite füllen"
override val settingsMediaList = "Medien-Uploads" override val settingsMediaList = "Medien-Uploads"
override val settingsEnableToggleFavoriteInNavDrawer =
"Favoriten in der Navigationsleiste hinzufügen/entfernen"
} }

View File

@ -427,4 +427,6 @@ internal val ElStrings =
override val noticeBannedUser = "Ο τρέχων χρήστης έχει αποκλειστεί από αυτήν την κοινότητα" override val noticeBannedUser = "Ο τρέχων χρήστης έχει αποκλειστεί από αυτήν την κοινότητα"
override val settingsHiddenPosts = "Κρυφές αναρτήσεις" override val settingsHiddenPosts = "Κρυφές αναρτήσεις"
override val settingsMediaList = "Μεταφορτώσεις πολυμέσων" override val settingsMediaList = "Μεταφορτώσεις πολυμέσων"
override val settingsEnableToggleFavoriteInNavDrawer =
"Προσθήκη/αφαίρεση αγαπημένων στο συρτάρι πλοήγησης"
} }

View File

@ -417,4 +417,5 @@ internal val EnStrings =
override val noticeBannedUser = "The current user has been banned from this community" override val noticeBannedUser = "The current user has been banned from this community"
override val settingsHiddenPosts = "Hidden posts" override val settingsHiddenPosts = "Hidden posts"
override val settingsMediaList = "Media uploads" override val settingsMediaList = "Media uploads"
override val settingsEnableToggleFavoriteInNavDrawer = "Add/remove favorites in navigation drawer"
} }

View File

@ -416,4 +416,5 @@ internal val EoStrings =
override val noticeBannedUser = "La nuna uzanto estas malpermesita de ĉi tiu komunumo" override val noticeBannedUser = "La nuna uzanto estas malpermesita de ĉi tiu komunumo"
override val settingsHiddenPosts = "Kaŝitaj afiŝoj" override val settingsHiddenPosts = "Kaŝitaj afiŝoj"
override val settingsMediaList = "Amaskomunikiloj alŝutoj" override val settingsMediaList = "Amaskomunikiloj alŝutoj"
override val settingsEnableToggleFavoriteInNavDrawer = "Aldoni/forigi plej ŝatatajn en navigada tirkesto"
} }

View File

@ -421,4 +421,6 @@ internal val EsStrings =
override val noticeBannedUser = "El usuario actual ha sido baneado de esta comunidad" override val noticeBannedUser = "El usuario actual ha sido baneado de esta comunidad"
override val settingsHiddenPosts = "Publicaciones escondidas" override val settingsHiddenPosts = "Publicaciones escondidas"
override val settingsMediaList = "Cargas de medios" override val settingsMediaList = "Cargas de medios"
override val settingsEnableToggleFavoriteInNavDrawer =
"Agregar/eliminar favoritos en el cajón de navegación"
} }

View File

@ -422,4 +422,6 @@ internal val EtStrings =
override val noticeBannedUser = "Praegune kasutaja on sellest kogukonnast keelatud" override val noticeBannedUser = "Praegune kasutaja on sellest kogukonnast keelatud"
override val settingsHiddenPosts = "Peidetud postitused" override val settingsHiddenPosts = "Peidetud postitused"
override val settingsMediaList = "Meedia üleslaadimine" override val settingsMediaList = "Meedia üleslaadimine"
override val settingsEnableToggleFavoriteInNavDrawer =
"Lemmikute lisamine/eemaldamine navigeerimissahtlis"
} }

View File

@ -417,4 +417,6 @@ internal val FiStrings =
override val noticeBannedUser = "Nykyinen käyttäjä on estetty tästä yhteisöstä" override val noticeBannedUser = "Nykyinen käyttäjä on estetty tästä yhteisöstä"
override val settingsHiddenPosts = "Piilotetut viestit" override val settingsHiddenPosts = "Piilotetut viestit"
override val settingsMediaList = "Median lataukset" override val settingsMediaList = "Median lataukset"
override val settingsEnableToggleFavoriteInNavDrawer =
"Lisää/poista suosikkeja navigointipaneelissa"
} }

View File

@ -426,4 +426,6 @@ internal val FrStrings =
override val noticeBannedUser = "L\'utilisateur actuel a été banni de cette communauté" override val noticeBannedUser = "L\'utilisateur actuel a été banni de cette communauté"
override val settingsHiddenPosts = "Messages masqués" override val settingsHiddenPosts = "Messages masqués"
override val settingsMediaList = "Téléchargements de médias" override val settingsMediaList = "Téléchargements de médias"
override val settingsEnableToggleFavoriteInNavDrawer =
"Ajouter/supprimer des favoris dans le tiroir de navigation"
} }

View File

@ -421,4 +421,6 @@ internal val GaStrings =
override val noticeBannedUser = "Tá cosc ar an úsáideoir reatha ón bpobal seo" override val noticeBannedUser = "Tá cosc ar an úsáideoir reatha ón bpobal seo"
override val settingsHiddenPosts = "Poist i bhfolach" override val settingsHiddenPosts = "Poist i bhfolach"
override val settingsMediaList = "Uaslódálacha meáin" override val settingsMediaList = "Uaslódálacha meáin"
override val settingsEnableToggleFavoriteInNavDrawer =
"Cuir leis/bain na ceanáin sa tarraiceán nascleanúna"
} }

View File

@ -421,4 +421,6 @@ internal val HrStrings =
override val noticeBannedUser = "Trenutačni korisnik je zabranjen iz ove zajednice" override val noticeBannedUser = "Trenutačni korisnik je zabranjen iz ove zajednice"
override val settingsHiddenPosts = "Skriveni postovi" override val settingsHiddenPosts = "Skriveni postovi"
override val settingsMediaList = "Prijenos medija" override val settingsMediaList = "Prijenos medija"
override val settingsEnableToggleFavoriteInNavDrawer =
"Dodajte/uklonite favorite u ladici za navigaciju"
} }

View File

@ -425,4 +425,6 @@ internal val HuStrings =
override val noticeBannedUser = "A jelenlegi felhasználót kitiltották a közösségből" override val noticeBannedUser = "A jelenlegi felhasználót kitiltották a közösségből"
override val settingsHiddenPosts = "Rejtett bejegyzések" override val settingsHiddenPosts = "Rejtett bejegyzések"
override val settingsMediaList = "Médiafeltöltések" override val settingsMediaList = "Médiafeltöltések"
override val settingsEnableToggleFavoriteInNavDrawer =
"Kedvencek hozzáadása/eltávolítása a navigációs fiókban"
} }

View File

@ -422,4 +422,5 @@ internal val ItStrings =
override val noticeBannedUser = "L\'utente corrente è stato bannato da questa comunità" override val noticeBannedUser = "L\'utente corrente è stato bannato da questa comunità"
override val settingsHiddenPosts = "Post nascosti" override val settingsHiddenPosts = "Post nascosti"
override val settingsMediaList = "Caricamenti multimediali" override val settingsMediaList = "Caricamenti multimediali"
override val settingsEnableToggleFavoriteInNavDrawer = "Aggiungi/rimuovi preferiti in menu laterale"
} }

View File

@ -420,4 +420,6 @@ internal val LtStrings =
"Dabartinis vartotojas buvo uždraustas dalyvauti šioje bendruomenėje" "Dabartinis vartotojas buvo uždraustas dalyvauti šioje bendruomenėje"
override val settingsHiddenPosts = "Paslėpti įrašai" override val settingsHiddenPosts = "Paslėpti įrašai"
override val settingsMediaList = "Žiniasklaidos įkėlimai" override val settingsMediaList = "Žiniasklaidos įkėlimai"
override val settingsEnableToggleFavoriteInNavDrawer =
"Pridėti / pašalinti mėgstamiausius naršymo skydelyje"
} }

View File

@ -419,4 +419,6 @@ internal val LvStrings =
override val noticeBannedUser = "Pašreizējais lietotājs ir bloķēts no šīs kopienas" override val noticeBannedUser = "Pašreizējais lietotājs ir bloķēts no šīs kopienas"
override val settingsHiddenPosts = "Slēptās ziņas" override val settingsHiddenPosts = "Slēptās ziņas"
override val settingsMediaList = "Multivides augšupielādes" override val settingsMediaList = "Multivides augšupielādes"
override val settingsEnableToggleFavoriteInNavDrawer =
"Pievienojiet/noņemiet izlasi navigācijas atvilktnē"
} }

View File

@ -421,4 +421,6 @@ internal val MtStrings =
override val noticeBannedUser = "L-utent attwali ġie pprojbit minn din il-komunità" override val noticeBannedUser = "L-utent attwali ġie pprojbit minn din il-komunità"
override val settingsHiddenPosts = "Postijiet moħbija" override val settingsHiddenPosts = "Postijiet moħbija"
override val settingsMediaList = "Uploads tal-midja" override val settingsMediaList = "Uploads tal-midja"
override val settingsEnableToggleFavoriteInNavDrawer =
"Żid/neħħi l-favoriti fil-kexxun tan-navigazzjoni"
} }

View File

@ -422,4 +422,6 @@ internal val NlStrings =
override val noticeBannedUser = "De huidige gebruiker is uitgesloten van deze community" override val noticeBannedUser = "De huidige gebruiker is uitgesloten van deze community"
override val settingsHiddenPosts = "Verborgen berichten" override val settingsHiddenPosts = "Verborgen berichten"
override val settingsMediaList = "Media-uploads" override val settingsMediaList = "Media-uploads"
override val settingsEnableToggleFavoriteInNavDrawer =
"Favorieten toevoegen/verwijderen in de navigatielade"
} }

View File

@ -419,4 +419,6 @@ internal val NoStrings =
"Den nåværende brukeren har blitt utestengt fra dette fellesskapet" "Den nåværende brukeren har blitt utestengt fra dette fellesskapet"
override val settingsHiddenPosts = "Skjulte innlegg" override val settingsHiddenPosts = "Skjulte innlegg"
override val settingsMediaList = "Medieopplastinger" override val settingsMediaList = "Medieopplastinger"
override val settingsEnableToggleFavoriteInNavDrawer =
"Legg til/fjern favoritter i navigasjonsskuffen"
} }

View File

@ -421,4 +421,5 @@ internal val PlStrings =
override val noticeBannedUser = "Bieżący użytkownik został zablokowany w tej społeczności" override val noticeBannedUser = "Bieżący użytkownik został zablokowany w tej społeczności"
override val settingsHiddenPosts = "Ukryte posty" override val settingsHiddenPosts = "Ukryte posty"
override val settingsMediaList = "Przesyłanie multimediów" override val settingsMediaList = "Przesyłanie multimediów"
override val settingsEnableToggleFavoriteInNavDrawer = "Dodaj/usuń ulubione w szufladzie nawigacji"
} }

View File

@ -419,4 +419,6 @@ internal val PtBrStrings =
override val noticeBannedUser = "O usuário atual foi banido desta comunidade" override val noticeBannedUser = "O usuário atual foi banido desta comunidade"
override val settingsHiddenPosts = "Posts escondidos" override val settingsHiddenPosts = "Posts escondidos"
override val settingsMediaList = "Carregamentos de mídia" override val settingsMediaList = "Carregamentos de mídia"
override val settingsEnableToggleFavoriteInNavDrawer =
"Adicionar/remover favoritos na gaveta de navegação"
} }

View File

@ -420,4 +420,6 @@ internal val PtStrings =
override val noticeBannedUser = "O usuário atual foi banido desta comunidade" override val noticeBannedUser = "O usuário atual foi banido desta comunidade"
override val settingsHiddenPosts = "Postagens ocultadas" override val settingsHiddenPosts = "Postagens ocultadas"
override val settingsMediaList = "Carregamentos de mídia" override val settingsMediaList = "Carregamentos de mídia"
override val settingsEnableToggleFavoriteInNavDrawer =
"Adicionar/remover favoritos na gaveta de navegação"
} }

View File

@ -420,4 +420,6 @@ internal val RoStrings =
override val noticeBannedUser = "Utilizatorul actual a fost exclus din această comunitate" override val noticeBannedUser = "Utilizatorul actual a fost exclus din această comunitate"
override val settingsHiddenPosts = "Postări ascunse" override val settingsHiddenPosts = "Postări ascunse"
override val settingsMediaList = "Încărcări media" override val settingsMediaList = "Încărcări media"
override val settingsEnableToggleFavoriteInNavDrawer =
"Adăugă/elimină favoritele din sertarul de navigare"
} }

View File

@ -422,4 +422,6 @@ internal val RuStrings =
override val noticeBannedUser = "Текущий пользователь заблокирован в этом сообществе" override val noticeBannedUser = "Текущий пользователь заблокирован в этом сообществе"
override val settingsHiddenPosts = "Скрытые сообщения" override val settingsHiddenPosts = "Скрытые сообщения"
override val settingsMediaList = "Загрузка мультимедиа" override val settingsMediaList = "Загрузка мультимедиа"
override val settingsEnableToggleFavoriteInNavDrawer =
"Добавить/удалить избранное в панели навигации"
} }

View File

@ -420,4 +420,6 @@ internal val SeStrings =
override val noticeBannedUser = "Den nuvarande användaren har blockerats från denna grupp" override val noticeBannedUser = "Den nuvarande användaren har blockerats från denna grupp"
override val settingsHiddenPosts = "Dolda inlägg" override val settingsHiddenPosts = "Dolda inlägg"
override val settingsMediaList = "Mediauppladdningar" override val settingsMediaList = "Mediauppladdningar"
override val settingsEnableToggleFavoriteInNavDrawer =
"Lägg till/ta bort favoriter i navigeringslådan"
} }

View File

@ -422,4 +422,6 @@ internal val SkStrings =
override val noticeBannedUser = "Aktuálny používateľ má zakázaný prístup do tejto komunity" override val noticeBannedUser = "Aktuálny používateľ má zakázaný prístup do tejto komunity"
override val settingsHiddenPosts = "Skryté príspevky" override val settingsHiddenPosts = "Skryté príspevky"
override val settingsMediaList = "Nahrávanie médií" override val settingsMediaList = "Nahrávanie médií"
override val settingsEnableToggleFavoriteInNavDrawer =
"Pridať/odstrániť obľúbené položky v navigačnej zásuvke"
} }

View File

@ -420,4 +420,6 @@ internal val SlStrings =
override val noticeBannedUser = "Trenutni uporabnik je bil izključen iz te skupnosti" override val noticeBannedUser = "Trenutni uporabnik je bil izključen iz te skupnosti"
override val settingsHiddenPosts = "Skrite objave" override val settingsHiddenPosts = "Skrite objave"
override val settingsMediaList = "Nalaganje medijev" override val settingsMediaList = "Nalaganje medijev"
override val settingsEnableToggleFavoriteInNavDrawer =
"Dodajte/odstranite priljubljene v predalu za krmarjenje"
} }

View File

@ -422,4 +422,6 @@ internal val SqStrings =
override val noticeBannedUser = "Përdoruesi aktual është përjashtuar nga ky komunitet" override val noticeBannedUser = "Përdoruesi aktual është përjashtuar nga ky komunitet"
override val settingsHiddenPosts = "Postimet e fshehura" override val settingsHiddenPosts = "Postimet e fshehura"
override val settingsMediaList = "Ngarkimet e mediave" override val settingsMediaList = "Ngarkimet e mediave"
override val settingsEnableToggleFavoriteInNavDrawer =
"Shto/hiq të preferuarat në sirtarin e navigimit"
} }

View File

@ -422,4 +422,6 @@ internal val SrStrings =
override val noticeBannedUser = "Тренутном кориснику је забрањен приступ овој заједници" override val noticeBannedUser = "Тренутном кориснику је забрањен приступ овој заједници"
override val settingsHiddenPosts = "Скривени постови" override val settingsHiddenPosts = "Скривени постови"
override val settingsMediaList = "Учитавање медија" override val settingsMediaList = "Учитавање медија"
override val settingsEnableToggleFavoriteInNavDrawer =
"Додајте/уклоните фаворите у фиоци за навигацију"
} }

View File

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

View File

@ -416,4 +416,6 @@ internal val TokStrings =
override val noticeBannedUser = "sina ken lukin ala e lipu mute pi kulupu ni" override val noticeBannedUser = "sina ken lukin ala e lipu mute pi kulupu ni"
override val settingsHiddenPosts = "lipu pi lukin ala" override val settingsHiddenPosts = "lipu pi lukin ala"
override val settingsMediaList = "sitelen linluwi" override val settingsMediaList = "sitelen linluwi"
override val settingsEnableToggleFavoriteInNavDrawer =
"o sin/o weka e ijo pona lon leko luka"
} }

View File

@ -421,4 +421,6 @@ internal val TrStrings =
override val noticeBannedUser = "Mevcut kullanıcı bu topluluktan yasaklandı" override val noticeBannedUser = "Mevcut kullanıcı bu topluluktan yasaklandı"
override val settingsHiddenPosts = "Gizli gönderiler" override val settingsHiddenPosts = "Gizli gönderiler"
override val settingsMediaList = "Medya yüklemeleri" override val settingsMediaList = "Medya yüklemeleri"
override val settingsEnableToggleFavoriteInNavDrawer =
"Gezinme çekmecesinde favorileri ekle/kaldır"
} }

View File

@ -422,4 +422,6 @@ internal val UkStrings =
override val noticeBannedUser = "Поточний користувач був забанений у цій спільноті" override val noticeBannedUser = "Поточний користувач був забанений у цій спільноті"
override val settingsHiddenPosts = "Приховані пости" override val settingsHiddenPosts = "Приховані пости"
override val settingsMediaList = "Завантаження медіа" override val settingsMediaList = "Завантаження медіа"
override val settingsEnableToggleFavoriteInNavDrawer =
"Додати/видалити вибране в панелі навігації"
} }

View File

@ -411,4 +411,6 @@ internal val ZhHkStrings =
override val noticeBannedUser = "呢個用戶已被呢個社區禁止" override val noticeBannedUser = "呢個用戶已被呢個社區禁止"
override val settingsHiddenPosts = "隱藏帖子" override val settingsHiddenPosts = "隱藏帖子"
override val settingsMediaList = "媒體上傳" override val settingsMediaList = "媒體上傳"
override val settingsEnableToggleFavoriteInNavDrawer =
"Add/remove favorites in navigation drawer"
} }

View File

@ -411,4 +411,6 @@ internal val ZhTwStrings =
override val settingsAboutLicences = "授權" override val settingsAboutLicences = "授權"
override val never = "從不" override val never = "從不"
override val appIconDefault = "默認" override val appIconDefault = "默認"
override val settingsEnableToggleFavoriteInNavDrawer =
"Add/remove favorites in navigation drawer"
} }

View File

@ -162,4 +162,6 @@ sealed interface NotificationCenterEvent {
data class ChangeCommunityVisibility(val value: CommunityVisibilityType) : data class ChangeCommunityVisibility(val value: CommunityVisibilityType) :
NotificationCenterEvent NotificationCenterEvent
data object FavoritesUpdated : NotificationCenterEvent
} }

View File

@ -155,6 +155,7 @@ class DefaultSettingsRepositoryTest {
fullWidthImages = if (model.fullWidthImages) 1 else 0, fullWidthImages = if (model.fullWidthImages) 1 else 0,
showUnreadComments = if (model.showUnreadComments) 1 else 0, showUnreadComments = if (model.showUnreadComments) 1 else 0,
commentIndentAmount = model.commentIndentAmount.toLong(), commentIndentAmount = model.commentIndentAmount.toLong(),
enableToggleFavoriteInNavDrawer = if (model.enableToggleFavoriteInNavDrawer) 1 else 0,
account_id = 1, account_id = 1,
) )
} }
@ -226,6 +227,7 @@ class DefaultSettingsRepositoryTest {
fullWidthImages = if (model.fullWidthImages) 1 else 0, fullWidthImages = if (model.fullWidthImages) 1 else 0,
showUnreadComments = if (model.showUnreadComments) 1 else 0, showUnreadComments = if (model.showUnreadComments) 1 else 0,
commentIndentAmount = model.commentIndentAmount.toLong(), commentIndentAmount = model.commentIndentAmount.toLong(),
enableToggleFavoriteInNavDrawer = if (model.enableToggleFavoriteInNavDrawer) 1 else 0,
account_id = 1, account_id = 1,
) )
} }
@ -293,6 +295,7 @@ class DefaultSettingsRepositoryTest {
fullWidthImages: Boolean = false, fullWidthImages: Boolean = false,
showUnreadComments: Boolean = true, showUnreadComments: Boolean = true,
commentIndentAmount: Int = 2, commentIndentAmount: Int = 2,
enableToggleFavoriteInNavDrawer: Boolean = false,
) = GetBy( ) = GetBy(
id = id, id = id,
theme = theme, theme = theme,
@ -353,5 +356,6 @@ class DefaultSettingsRepositoryTest {
fullWidthImages = if (fullWidthImages) 1 else 0, fullWidthImages = if (fullWidthImages) 1 else 0,
showUnreadComments = if (showUnreadComments) 1 else 0, showUnreadComments = if (showUnreadComments) 1 else 0,
commentIndentAmount = commentIndentAmount.toLong(), commentIndentAmount = commentIndentAmount.toLong(),
enableToggleFavoriteInNavDrawer = if (enableToggleFavoriteInNavDrawer) 1 else 0,
) )
} }

View File

@ -62,4 +62,5 @@ data class SettingsModel(
val showUnreadComments: Boolean = false, val showUnreadComments: Boolean = false,
val enableButtonsToScrollBetweenComments: Boolean = false, val enableButtonsToScrollBetweenComments: Boolean = false,
val commentIndentAmount: Int = 2, val commentIndentAmount: Int = 2,
val enableToggleFavoriteInNavDrawer: Boolean = false,
) )

View File

@ -120,22 +120,28 @@ internal class DefaultSettingsRepository(
postBodyMaxLines = settings.postBodyMaxLines?.toLong(), postBodyMaxLines = settings.postBodyMaxLines?.toLong(),
infiniteScrollEnabled = if (settings.infiniteScrollEnabled) 1 else 0, infiniteScrollEnabled = if (settings.infiniteScrollEnabled) 1 else 0,
actionsOnSwipeToStartPosts = actionsOnSwipeToStartPosts =
settings.actionsOnSwipeToStartPosts.map { it.toInt() } settings.actionsOnSwipeToStartPosts
.map { it.toInt() }
.joinToString(","), .joinToString(","),
actionsOnSwipeToEndPosts = actionsOnSwipeToEndPosts =
settings.actionsOnSwipeToEndPosts.map { it.toInt() } settings.actionsOnSwipeToEndPosts
.map { it.toInt() }
.joinToString(","), .joinToString(","),
actionsOnSwipeToStartComments = actionsOnSwipeToStartComments =
settings.actionsOnSwipeToStartComments.map { it.toInt() } settings.actionsOnSwipeToStartComments
.map { it.toInt() }
.joinToString(","), .joinToString(","),
actionsOnSwipeToEndComments = actionsOnSwipeToEndComments =
settings.actionsOnSwipeToEndComments.map { it.toInt() } settings.actionsOnSwipeToEndComments
.map { it.toInt() }
.joinToString(","), .joinToString(","),
actionsOnSwipeToStartInbox = actionsOnSwipeToStartInbox =
settings.actionsOnSwipeToStartInbox.map { it.toInt() } settings.actionsOnSwipeToStartInbox
.map { it.toInt() }
.joinToString(","), .joinToString(","),
actionsOnSwipeToEndInbox = actionsOnSwipeToEndInbox =
settings.actionsOnSwipeToEndInbox.map { it.toInt() } settings.actionsOnSwipeToEndInbox
.map { it.toInt() }
.joinToString(","), .joinToString(","),
opaqueSystemBars = if (settings.opaqueSystemBars) 1L else 0L, opaqueSystemBars = if (settings.opaqueSystemBars) 1L else 0L,
showScores = if (settings.showScores) 1L else 0L, showScores = if (settings.showScores) 1L else 0L,
@ -150,6 +156,7 @@ internal class DefaultSettingsRepository(
showUnreadComments = if (settings.showUnreadComments) 1L else 0L, showUnreadComments = if (settings.showUnreadComments) 1L else 0L,
enableButtonsToScrollBetweenComments = if (settings.enableButtonsToScrollBetweenComments) 1L else 0L, enableButtonsToScrollBetweenComments = if (settings.enableButtonsToScrollBetweenComments) 1L else 0L,
fullWidthImages = if (settings.fullWidthImages) 1L else 0L, fullWidthImages = if (settings.fullWidthImages) 1L else 0L,
enableToggleFavoriteInNavDrawer = if (settings.enableToggleFavoriteInNavDrawer) 1L else 0L,
) )
} }
@ -387,22 +394,28 @@ internal class DefaultSettingsRepository(
postBodyMaxLines = settings.postBodyMaxLines?.toLong(), postBodyMaxLines = settings.postBodyMaxLines?.toLong(),
infiniteScrollEnabled = if (settings.infiniteScrollEnabled) 1L else 0L, infiniteScrollEnabled = if (settings.infiniteScrollEnabled) 1L else 0L,
actionsOnSwipeToStartPosts = actionsOnSwipeToStartPosts =
settings.actionsOnSwipeToStartPosts.map { it.toInt() } settings.actionsOnSwipeToStartPosts
.map { it.toInt() }
.joinToString(","), .joinToString(","),
actionsOnSwipeToEndPosts = actionsOnSwipeToEndPosts =
settings.actionsOnSwipeToEndPosts.map { it.toInt() } settings.actionsOnSwipeToEndPosts
.map { it.toInt() }
.joinToString(","), .joinToString(","),
actionsOnSwipeToStartComments = actionsOnSwipeToStartComments =
settings.actionsOnSwipeToStartComments.map { it.toInt() } settings.actionsOnSwipeToStartComments
.map { it.toInt() }
.joinToString(","), .joinToString(","),
actionsOnSwipeToEndComments = actionsOnSwipeToEndComments =
settings.actionsOnSwipeToEndComments.map { it.toInt() } settings.actionsOnSwipeToEndComments
.map { it.toInt() }
.joinToString(","), .joinToString(","),
actionsOnSwipeToStartInbox = actionsOnSwipeToStartInbox =
settings.actionsOnSwipeToStartInbox.map { it.toInt() } settings.actionsOnSwipeToStartInbox
.map { it.toInt() }
.joinToString(","), .joinToString(","),
actionsOnSwipeToEndInbox = actionsOnSwipeToEndInbox =
settings.actionsOnSwipeToEndInbox.map { it.toInt() } settings.actionsOnSwipeToEndInbox
.map { it.toInt() }
.joinToString(","), .joinToString(","),
opaqueSystemBars = if (settings.opaqueSystemBars) 1L else 0L, opaqueSystemBars = if (settings.opaqueSystemBars) 1L else 0L,
showScores = if (settings.showScores) 1L else 0L, showScores = if (settings.showScores) 1L else 0L,
@ -417,6 +430,7 @@ internal class DefaultSettingsRepository(
showUnreadComments = if (settings.showUnreadComments) 1L else 0L, showUnreadComments = if (settings.showUnreadComments) 1L else 0L,
enableButtonsToScrollBetweenComments = if (settings.enableButtonsToScrollBetweenComments) 1L else 0L, enableButtonsToScrollBetweenComments = if (settings.enableButtonsToScrollBetweenComments) 1L else 0L,
fullWidthImages = if (settings.fullWidthImages) 1L else 0L, fullWidthImages = if (settings.fullWidthImages) 1L else 0L,
enableToggleFavoriteInNavDrawer = if (settings.enableToggleFavoriteInNavDrawer) 1L else 0L,
) )
} }
} }
@ -472,27 +486,33 @@ private fun GetBy.toModel() =
postBodyMaxLines = postBodyMaxLines?.toInt(), postBodyMaxLines = postBodyMaxLines?.toInt(),
infiniteScrollEnabled = infiniteScrollEnabled != 0L, infiniteScrollEnabled = infiniteScrollEnabled != 0L,
actionsOnSwipeToStartPosts = actionsOnSwipeToStartPosts =
actionsOnSwipeToStartPosts?.split(",") actionsOnSwipeToStartPosts
?.split(",")
?.mapNotNull { it.toIntOrNull()?.toActionOnSwipe() } ?.mapNotNull { it.toIntOrNull()?.toActionOnSwipe() }
?: ActionOnSwipe.DEFAULT_SWIPE_TO_START_POSTS, ?: ActionOnSwipe.DEFAULT_SWIPE_TO_START_POSTS,
actionsOnSwipeToEndPosts = actionsOnSwipeToEndPosts =
actionsOnSwipeToEndPosts?.split(",") actionsOnSwipeToEndPosts
?.split(",")
?.mapNotNull { it.toIntOrNull()?.toActionOnSwipe() } ?.mapNotNull { it.toIntOrNull()?.toActionOnSwipe() }
?: ActionOnSwipe.DEFAULT_SWIPE_TO_END_POSTS, ?: ActionOnSwipe.DEFAULT_SWIPE_TO_END_POSTS,
actionsOnSwipeToStartComments = actionsOnSwipeToStartComments =
actionsOnSwipeToStartComments?.split(",") actionsOnSwipeToStartComments
?.split(",")
?.mapNotNull { it.toIntOrNull()?.toActionOnSwipe() } ?.mapNotNull { it.toIntOrNull()?.toActionOnSwipe() }
?: ActionOnSwipe.DEFAULT_SWIPE_TO_START_COMMENTS, ?: ActionOnSwipe.DEFAULT_SWIPE_TO_START_COMMENTS,
actionsOnSwipeToEndComments = actionsOnSwipeToEndComments =
actionsOnSwipeToEndComments?.split(",") actionsOnSwipeToEndComments
?.split(",")
?.mapNotNull { it.toIntOrNull()?.toActionOnSwipe() } ?.mapNotNull { it.toIntOrNull()?.toActionOnSwipe() }
?: ActionOnSwipe.DEFAULT_SWIPE_TO_END_COMMENTS, ?: ActionOnSwipe.DEFAULT_SWIPE_TO_END_COMMENTS,
actionsOnSwipeToStartInbox = actionsOnSwipeToStartInbox =
actionsOnSwipeToStartInbox?.split(",") actionsOnSwipeToStartInbox
?.split(",")
?.mapNotNull { it.toIntOrNull()?.toActionOnSwipe() } ?.mapNotNull { it.toIntOrNull()?.toActionOnSwipe() }
?: ActionOnSwipe.DEFAULT_SWIPE_TO_START_INBOX, ?: ActionOnSwipe.DEFAULT_SWIPE_TO_START_INBOX,
actionsOnSwipeToEndInbox = actionsOnSwipeToEndInbox =
actionsOnSwipeToEndInbox?.split(",") actionsOnSwipeToEndInbox
?.split(",")
?.mapNotNull { it.toIntOrNull()?.toActionOnSwipe() } ?.mapNotNull { it.toIntOrNull()?.toActionOnSwipe() }
?: ActionOnSwipe.DEFAULT_SWIPE_TO_END_INBOX, ?: ActionOnSwipe.DEFAULT_SWIPE_TO_END_INBOX,
opaqueSystemBars = opaqueSystemBars == 1L, opaqueSystemBars = opaqueSystemBars == 1L,
@ -508,4 +528,5 @@ private fun GetBy.toModel() =
showUnreadComments = showUnreadComments == 1L, showUnreadComments = showUnreadComments == 1L,
enableButtonsToScrollBetweenComments = enableButtonsToScrollBetweenComments == 1L, enableButtonsToScrollBetweenComments = enableButtonsToScrollBetweenComments == 1L,
fullWidthImages = fullWidthImages == 1L, fullWidthImages = fullWidthImages == 1L,
enableToggleFavoriteInNavDrawer = enableToggleFavoriteInNavDrawer == 1L,
) )

View File

@ -74,6 +74,7 @@ internal data class SerializableSettings(
val showUnreadComments: Boolean = false, val showUnreadComments: Boolean = false,
val enableButtonsToScrollBetweenComments: Boolean = false, val enableButtonsToScrollBetweenComments: Boolean = false,
val fullWidthImages: Boolean = false, val fullWidthImages: Boolean = false,
val enableToggleFavoriteInNavDrawer: Boolean = false,
) )
internal fun SerializableSettings.toModel() = internal fun SerializableSettings.toModel() =
@ -139,6 +140,7 @@ internal fun SerializableSettings.toModel() =
showUnreadComments = showUnreadComments, showUnreadComments = showUnreadComments,
enableButtonsToScrollBetweenComments = enableButtonsToScrollBetweenComments, enableButtonsToScrollBetweenComments = enableButtonsToScrollBetweenComments,
fullWidthImages = fullWidthImages, fullWidthImages = fullWidthImages,
enableToggleFavoriteInNavDrawer = enableToggleFavoriteInNavDrawer,
) )
internal fun SettingsModel.toData() = internal fun SettingsModel.toData() =
@ -198,4 +200,5 @@ internal fun SettingsModel.toData() =
showUnreadComments = showUnreadComments, showUnreadComments = showUnreadComments,
enableButtonsToScrollBetweenComments = enableButtonsToScrollBetweenComments, enableButtonsToScrollBetweenComments = enableButtonsToScrollBetweenComments,
fullWidthImages = fullWidthImages, fullWidthImages = fullWidthImages,
enableToggleFavoriteInNavDrawer = enableToggleFavoriteInNavDrawer,
) )

View File

@ -59,6 +59,7 @@ CREATE TABLE SettingsEntity (
enableButtonsToScrollBetweenComments INTEGER NOT NULL DEFAULT 0, enableButtonsToScrollBetweenComments INTEGER NOT NULL DEFAULT 0,
fullWidthImages INTEGER NOT NULL DEFAULT 0, fullWidthImages INTEGER NOT NULL DEFAULT 0,
commentIndentAmount INTEGER NOT NULL DEFAULT 2, commentIndentAmount INTEGER NOT NULL DEFAULT 2,
enableToggleFavoriteInNavDrawer INTEGER NOT NULL DEFAULT 0,
FOREIGN KEY (account_id) REFERENCES AccountEntity(id) ON DELETE CASCADE, FOREIGN KEY (account_id) REFERENCES AccountEntity(id) ON DELETE CASCADE,
UNIQUE(account_id) UNIQUE(account_id)
); );
@ -123,6 +124,7 @@ INSERT OR IGNORE INTO SettingsEntity (
enableButtonsToScrollBetweenComments, enableButtonsToScrollBetweenComments,
fullWidthImages, fullWidthImages,
commentIndentAmount, commentIndentAmount,
enableToggleFavoriteInNavDrawer,
account_id account_id
) VALUES ( ) VALUES (
?, ?,
@ -183,6 +185,7 @@ INSERT OR IGNORE INTO SettingsEntity (
?, ?,
?, ?,
?, ?,
?,
? ?
); );
@ -245,7 +248,8 @@ SET theme = ?,
showUnreadComments = ?, showUnreadComments = ?,
enableButtonsToScrollBetweenComments = ?, enableButtonsToScrollBetweenComments = ?,
fullWidthImages = ?, fullWidthImages = ?,
commentIndentAmount = ? commentIndentAmount = ?,
enableToggleFavoriteInNavDrawer = ?
WHERE account_id = ?; WHERE account_id = ?;
getBy: getBy:
@ -308,6 +312,7 @@ SELECT
showUnreadComments, showUnreadComments,
enableButtonsToScrollBetweenComments, enableButtonsToScrollBetweenComments,
fullWidthImages, fullWidthImages,
commentIndentAmount commentIndentAmount,
enableToggleFavoriteInNavDrawer
FROM SettingsEntity FROM SettingsEntity
WHERE account_id = ?; WHERE account_id = ?;

View File

@ -0,0 +1,2 @@
ALTER TABLE SettingsEntity
ADD COLUMN enableToggleFavoriteInNavDrawer INTEGER NOT NULL DEFAULT 0;

View File

@ -14,7 +14,7 @@
{ {
"title": "NicKoehler", "title": "NicKoehler",
"avatar": "https://avatars.githubusercontent.com/u/53040044?v=4", "avatar": "https://avatars.githubusercontent.com/u/53040044?v=4",
"subtitle": "App icon and artwork", "subtitle": "App icon designer, Code contributor",
"url": "https://github.com/NicKoehler" "url": "https://github.com/NicKoehler"
}, },
{ {

View File

@ -11,37 +11,71 @@ interface AdvancedSettingsMviModel :
ScreenModel, ScreenModel,
MviModel<AdvancedSettingsMviModel.Intent, AdvancedSettingsMviModel.UiState, AdvancedSettingsMviModel.Effect> { MviModel<AdvancedSettingsMviModel.Intent, AdvancedSettingsMviModel.UiState, AdvancedSettingsMviModel.Effect> {
sealed interface Intent { sealed interface Intent {
data class ChangeEnableDoubleTapAction(val value: Boolean) : Intent data class ChangeEnableDoubleTapAction(
val value: Boolean,
) : Intent
data class ChangeAutoLoadImages(val value: Boolean) : Intent data class ChangeAutoLoadImages(
val value: Boolean,
) : Intent
data class ChangeAutoExpandComments(val value: Boolean) : Intent data class ChangeAutoExpandComments(
val value: Boolean,
) : Intent
data class ChangeHideNavigationBarWhileScrolling(val value: Boolean) : Intent data class ChangeHideNavigationBarWhileScrolling(
val value: Boolean,
) : Intent
data class ChangeMarkAsReadWhileScrolling(val value: Boolean) : Intent data class ChangeMarkAsReadWhileScrolling(
val value: Boolean,
) : Intent
data class ChangeNavBarTitlesVisible(val value: Boolean) : Intent data class ChangeNavBarTitlesVisible(
val value: Boolean,
) : Intent
data class ChangeSearchPostTitleOnly(val value: Boolean) : Intent data class ChangeSearchPostTitleOnly(
val value: Boolean,
) : Intent
data class ChangeEdgeToEdge(val value: Boolean) : Intent data class ChangeEdgeToEdge(
val value: Boolean,
) : Intent
data class ChangeInfiniteScrollDisabled(val value: Boolean) : Intent data class ChangeInfiniteScrollDisabled(
val value: Boolean,
) : Intent
data class ChangeImageSourcePath(val value: Boolean) : Intent data class ChangeImageSourcePath(
val value: Boolean,
) : Intent
data class ChangeDefaultLanguage(val value: Long?) : Intent data class ChangeDefaultLanguage(
val value: Long?,
) : Intent
data class ChangeFadeReadPosts(val value: Boolean) : Intent data class ChangeFadeReadPosts(
val value: Boolean,
) : Intent
data class ChangeShowUnreadComments(val value: Boolean) : Intent data class ChangeShowUnreadComments(
val value: Boolean,
) : Intent
data object ExportSettings : Intent data object ExportSettings : Intent
data class ImportSettings(val content: String) : Intent data class ImportSettings(
val content: String,
) : Intent
data class ChangeEnableButtonsToScrollBetweenComments(val value: Boolean) : Intent data class ChangeEnableButtonsToScrollBetweenComments(
val value: Boolean,
) : Intent
data class ChangeEnableToggleFavoriteInNavDrawer(
val value: Boolean,
) : Intent
} }
data class UiState( data class UiState(
@ -71,9 +105,12 @@ interface AdvancedSettingsMviModel :
val supportSettingsImportExport: Boolean = true, val supportSettingsImportExport: Boolean = true,
val loading: Boolean = false, val loading: Boolean = false,
val enableButtonsToScrollBetweenComments: Boolean = false, val enableButtonsToScrollBetweenComments: Boolean = false,
val enableToggleFavoriteInNavDrawer: Boolean = false,
) )
sealed interface Effect { sealed interface Effect {
data class SaveSettings(val content: String) : Effect data class SaveSettings(
val content: String,
) : Effect
} }
} }

View File

@ -97,13 +97,14 @@ class AdvancedSettingsScreen : Screen {
var settingsContent by remember { mutableStateOf<String?>(null) } var settingsContent by remember { mutableStateOf<String?>(null) }
LaunchedEffect(model) { LaunchedEffect(model) {
model.effects.onEach { evt -> model.effects
when (evt) { .onEach { evt ->
is AdvancedSettingsMviModel.Effect.SaveSettings -> { when (evt) {
settingsContent = evt.content is AdvancedSettingsMviModel.Effect.SaveSettings -> {
settingsContent = evt.content
}
} }
} }.launchIn(this)
}.launchIn(this)
} }
Scaffold( Scaffold(
@ -155,8 +156,7 @@ class AdvancedSettingsScreen : Screen {
Modifier Modifier
.padding( .padding(
top = padding.calculateTopPadding(), top = padding.calculateTopPadding(),
) ).nestedScroll(scrollBehavior.nestedScrollConnection),
.nestedScroll(scrollBehavior.nestedScrollConnection),
) { ) {
Column( Column(
modifier = Modifier.fillMaxSize().verticalScroll(scrollState), modifier = Modifier.fillMaxSize().verticalScroll(scrollState),
@ -287,11 +287,12 @@ class AdvancedSettingsScreen : Screen {
// default language // default language
val languageValue = val languageValue =
uiState.availableLanguages.firstOrNull { l -> uiState.availableLanguages
l.id == uiState.defaultLanguageId .firstOrNull { l ->
}?.takeIf { l -> l.id == uiState.defaultLanguageId
l.id > 0 // undetermined language }?.takeIf { l ->
}?.name ?: LocalStrings.current.undetermined l.id > 0 // undetermined language
}?.name ?: LocalStrings.current.undetermined
SettingsRow( SettingsRow(
title = LocalStrings.current.advancedSettingsDefaultLanguage, title = LocalStrings.current.advancedSettingsDefaultLanguage,
value = languageValue, value = languageValue,
@ -365,7 +366,11 @@ class AdvancedSettingsScreen : Screen {
title = LocalStrings.current.settingsZombieModeScrollAmount, title = LocalStrings.current.settingsZombieModeScrollAmount,
value = value =
buildString { buildString {
val pt = uiState.zombieModeScrollAmount.toLocalDp().value.roundToInt() val pt =
uiState.zombieModeScrollAmount
.toLocalDp()
.value
.roundToInt()
append(pt) append(pt)
append(LocalStrings.current.settingsPointsShort) append(LocalStrings.current.settingsPointsShort)
}, },
@ -429,6 +434,20 @@ class AdvancedSettingsScreen : Screen {
icon = Icons.Default.Science, icon = Icons.Default.Science,
) )
if (uiState.isLogged) { if (uiState.isLogged) {
// edit favorites in navigation drawer
SettingsSwitchRow(
title = LocalStrings.current.settingsEnableToggleFavoriteInNavDrawer,
value = uiState.enableToggleFavoriteInNavDrawer,
onValueChanged =
rememberCallbackArgs(model) { value ->
model.reduce(
AdvancedSettingsMviModel.Intent.ChangeEnableToggleFavoriteInNavDrawer(
value,
),
)
},
)
// double tap // double tap
SettingsSwitchRow( SettingsSwitchRow(
title = LocalStrings.current.settingsEnableDoubleTap, title = LocalStrings.current.settingsEnableDoubleTap,

View File

@ -40,44 +40,59 @@ class AdvancedSettingsViewModel(
private val fileSystemManager: FileSystemManager, private val fileSystemManager: FileSystemManager,
private val importSettings: ImportSettingsUseCase, private val importSettings: ImportSettingsUseCase,
private val exportSettings: ExportSettingsUseCase, private val exportSettings: ExportSettingsUseCase,
) : AdvancedSettingsMviModel, ) : DefaultMviModel<AdvancedSettingsMviModel.Intent, AdvancedSettingsMviModel.UiState, AdvancedSettingsMviModel.Effect>(
DefaultMviModel<AdvancedSettingsMviModel.Intent, AdvancedSettingsMviModel.UiState, AdvancedSettingsMviModel.Effect>(
initialState = AdvancedSettingsMviModel.UiState(), initialState = AdvancedSettingsMviModel.UiState(),
) { ),
AdvancedSettingsMviModel {
init { init {
screenModelScope.launch { screenModelScope.launch {
themeRepository.navItemTitles.onEach { value -> themeRepository.navItemTitles
updateState { it.copy(navBarTitlesVisible = value) } .onEach { value ->
}.launchIn(this) updateState { it.copy(navBarTitlesVisible = value) }
}.launchIn(this)
identityRepository.isLogged.onEach { logged -> identityRepository.isLogged
updateState { it.copy(isLogged = logged ?: false) } .onEach { logged ->
}.launchIn(this) updateState { it.copy(isLogged = logged ?: false) }
}.launchIn(this)
notificationCenter.subscribe(NotificationCenterEvent.ChangeZombieInterval::class).onEach { evt -> notificationCenter
changeZombieModeInterval(evt.value) .subscribe(NotificationCenterEvent.ChangeZombieInterval::class)
}.launchIn(this) .onEach { evt ->
notificationCenter.subscribe(NotificationCenterEvent.ChangeFeedType::class).onEach { evt -> changeZombieModeInterval(evt.value)
if (evt.screenKey == "advancedSettings") { }.launchIn(this)
changeExploreType(evt.value) notificationCenter
} .subscribe(NotificationCenterEvent.ChangeFeedType::class)
}.launchIn(this) .onEach { evt ->
notificationCenter.subscribe(NotificationCenterEvent.ChangeZombieScrollAmount::class).onEach { evt -> if (evt.screenKey == "advancedSettings") {
changeZombieModeScrollAmount(evt.value) changeExploreType(evt.value)
}.launchIn(this) }
notificationCenter.subscribe(NotificationCenterEvent.ChangeInboxType::class).onEach { evt -> }.launchIn(this)
changeDefaultInboxUnreadOnly(evt.unreadOnly) notificationCenter
}.launchIn(this) .subscribe(NotificationCenterEvent.ChangeZombieScrollAmount::class)
notificationCenter.subscribe(NotificationCenterEvent.ChangeSystemBarTheme::class).onEach { evt -> .onEach { evt ->
changeSystemBarTheme(evt.value) changeZombieModeScrollAmount(evt.value)
}.launchIn(this) }.launchIn(this)
notificationCenter.subscribe(NotificationCenterEvent.ChangeInboxBackgroundCheckPeriod::class) notificationCenter
.subscribe(NotificationCenterEvent.ChangeInboxType::class)
.onEach { evt ->
changeDefaultInboxUnreadOnly(evt.unreadOnly)
}.launchIn(this)
notificationCenter
.subscribe(NotificationCenterEvent.ChangeSystemBarTheme::class)
.onEach { evt ->
changeSystemBarTheme(evt.value)
}.launchIn(this)
notificationCenter
.subscribe(NotificationCenterEvent.ChangeInboxBackgroundCheckPeriod::class)
.onEach { evt -> .onEach { evt ->
changeInboxBackgroundCheckPeriod(evt.value) changeInboxBackgroundCheckPeriod(evt.value)
}.launchIn(this) }.launchIn(this)
notificationCenter.subscribe(NotificationCenterEvent.AppIconVariantSelected::class).onEach { evt -> notificationCenter
changeAppIconVariant(evt.value.toAppIconVariant()) .subscribe(NotificationCenterEvent.AppIconVariantSelected::class)
}.launchIn(this) .onEach { evt ->
changeAppIconVariant(evt.value.toAppIconVariant())
}.launchIn(this)
updateAvailableLanguages() updateAvailableLanguages()
@ -106,6 +121,7 @@ class AdvancedSettingsViewModel(
inboxBackgroundCheckPeriod = settings.inboxBackgroundCheckPeriod, inboxBackgroundCheckPeriod = settings.inboxBackgroundCheckPeriod,
supportSettingsImportExport = fileSystemManager.isSupported, supportSettingsImportExport = fileSystemManager.isSupported,
enableButtonsToScrollBetweenComments = settings.enableButtonsToScrollBetweenComments, enableButtonsToScrollBetweenComments = settings.enableButtonsToScrollBetweenComments,
enableToggleFavoriteInNavDrawer = settings.enableToggleFavoriteInNavDrawer,
) )
} }
} }
@ -129,26 +145,44 @@ class AdvancedSettingsViewModel(
is AdvancedSettingsMviModel.Intent.ChangeMarkAsReadWhileScrolling -> is AdvancedSettingsMviModel.Intent.ChangeMarkAsReadWhileScrolling ->
changeMarkAsReadWhileScrolling(intent.value) changeMarkAsReadWhileScrolling(intent.value)
is AdvancedSettingsMviModel.Intent.ChangeSearchPostTitleOnly -> changeSearchPostTitleOnly(intent.value) is AdvancedSettingsMviModel.Intent.ChangeSearchPostTitleOnly ->
changeSearchPostTitleOnly(
intent.value,
)
is AdvancedSettingsMviModel.Intent.ChangeEdgeToEdge -> changeEdgeToEdge(intent.value) is AdvancedSettingsMviModel.Intent.ChangeEdgeToEdge -> changeEdgeToEdge(intent.value)
is AdvancedSettingsMviModel.Intent.ChangeInfiniteScrollDisabled -> is AdvancedSettingsMviModel.Intent.ChangeInfiniteScrollDisabled ->
changeInfiniteScrollDisabled(intent.value) changeInfiniteScrollDisabled(intent.value)
is AdvancedSettingsMviModel.Intent.ChangeImageSourcePath -> changeImageSourcePath(intent.value) is AdvancedSettingsMviModel.Intent.ChangeImageSourcePath -> changeImageSourcePath(intent.value)
is AdvancedSettingsMviModel.Intent.ChangeDefaultLanguage -> changeDefaultLanguageId(intent.value) is AdvancedSettingsMviModel.Intent.ChangeDefaultLanguage ->
changeDefaultLanguageId(
intent.value,
)
is AdvancedSettingsMviModel.Intent.ChangeFadeReadPosts -> changeFadeReadPosts(intent.value) is AdvancedSettingsMviModel.Intent.ChangeFadeReadPosts -> changeFadeReadPosts(intent.value)
is AdvancedSettingsMviModel.Intent.ChangeShowUnreadComments -> changeShowUnreadPosts(intent.value) is AdvancedSettingsMviModel.Intent.ChangeShowUnreadComments ->
changeShowUnreadPosts(
intent.value,
)
is AdvancedSettingsMviModel.Intent.ExportSettings -> handleExportSettings() is AdvancedSettingsMviModel.Intent.ExportSettings -> handleExportSettings()
is AdvancedSettingsMviModel.Intent.ImportSettings -> handleImportSettings(intent.content) is AdvancedSettingsMviModel.Intent.ImportSettings -> handleImportSettings(intent.content)
is AdvancedSettingsMviModel.Intent.ChangeEnableButtonsToScrollBetweenComments -> is AdvancedSettingsMviModel.Intent.ChangeEnableButtonsToScrollBetweenComments ->
changeEnableButtonsToScrollBetweenComments(intent.value) changeEnableButtonsToScrollBetweenComments(intent.value)
is AdvancedSettingsMviModel.Intent.ChangeEnableToggleFavoriteInNavDrawer ->
changeEnableToggleFavoriteInNavDrawer(
intent.value,
)
} }
} }
private fun changeNavBarTitlesVisible(value: Boolean) { private fun changeNavBarTitlesVisible(value: Boolean) {
themeRepository.changeNavItemTitles(value) themeRepository.changeNavItemTitles(value)
screenModelScope.launch { screenModelScope.launch {
val settings = settingsRepository.currentSettings.value.copy(navigationTitlesVisible = value) val settings =
settingsRepository.currentSettings.value.copy(navigationTitlesVisible = value)
saveSettings(settings) saveSettings(settings)
} }
} }
@ -156,7 +190,8 @@ class AdvancedSettingsViewModel(
private fun changeEnableDoubleTapAction(value: Boolean) { private fun changeEnableDoubleTapAction(value: Boolean) {
screenModelScope.launch { screenModelScope.launch {
updateState { it.copy(enableDoubleTapAction = value) } updateState { it.copy(enableDoubleTapAction = value) }
val settings = settingsRepository.currentSettings.value.copy(enableDoubleTapAction = value) val settings =
settingsRepository.currentSettings.value.copy(enableDoubleTapAction = value)
saveSettings(settings) saveSettings(settings)
} }
} }
@ -180,7 +215,8 @@ class AdvancedSettingsViewModel(
private fun changeHideNavigationBarWhileScrolling(value: Boolean) { private fun changeHideNavigationBarWhileScrolling(value: Boolean) {
screenModelScope.launch { screenModelScope.launch {
updateState { it.copy(hideNavigationBarWhileScrolling = value) } updateState { it.copy(hideNavigationBarWhileScrolling = value) }
val settings = settingsRepository.currentSettings.value.copy(hideNavigationBarWhileScrolling = value) val settings =
settingsRepository.currentSettings.value.copy(hideNavigationBarWhileScrolling = value)
saveSettings(settings) saveSettings(settings)
} }
} }
@ -188,7 +224,8 @@ class AdvancedSettingsViewModel(
private fun changeMarkAsReadWhileScrolling(value: Boolean) { private fun changeMarkAsReadWhileScrolling(value: Boolean) {
screenModelScope.launch { screenModelScope.launch {
updateState { it.copy(markAsReadWhileScrolling = value) } updateState { it.copy(markAsReadWhileScrolling = value) }
val settings = settingsRepository.currentSettings.value.copy(markAsReadWhileScrolling = value) val settings =
settingsRepository.currentSettings.value.copy(markAsReadWhileScrolling = value)
saveSettings(settings) saveSettings(settings)
} }
} }
@ -204,7 +241,8 @@ class AdvancedSettingsViewModel(
private fun changeZombieModeScrollAmount(value: Float) { private fun changeZombieModeScrollAmount(value: Float) {
screenModelScope.launch { screenModelScope.launch {
updateState { it.copy(zombieModeScrollAmount = value) } updateState { it.copy(zombieModeScrollAmount = value) }
val settings = settingsRepository.currentSettings.value.copy(zombieModeScrollAmount = value) val settings =
settingsRepository.currentSettings.value.copy(zombieModeScrollAmount = value)
saveSettings(settings) saveSettings(settings)
} }
} }
@ -212,7 +250,8 @@ class AdvancedSettingsViewModel(
private fun changeDefaultInboxUnreadOnly(value: Boolean) { private fun changeDefaultInboxUnreadOnly(value: Boolean) {
screenModelScope.launch { screenModelScope.launch {
updateState { it.copy(defaultInboxUnreadOnly = value) } updateState { it.copy(defaultInboxUnreadOnly = value) }
val settings = settingsRepository.currentSettings.value.copy(defaultInboxType = value.toInboxDefaultType()) val settings =
settingsRepository.currentSettings.value.copy(defaultInboxType = value.toInboxDefaultType())
saveSettings(settings) saveSettings(settings)
notificationCenter.send(NotificationCenterEvent.ResetInbox) notificationCenter.send(NotificationCenterEvent.ResetInbox)
} }
@ -221,7 +260,8 @@ class AdvancedSettingsViewModel(
private fun changeSearchPostTitleOnly(value: Boolean) { private fun changeSearchPostTitleOnly(value: Boolean) {
screenModelScope.launch { screenModelScope.launch {
updateState { it.copy(searchPostTitleOnly = value) } updateState { it.copy(searchPostTitleOnly = value) }
val settings = settingsRepository.currentSettings.value.copy(searchPostTitleOnly = value) val settings =
settingsRepository.currentSettings.value.copy(searchPostTitleOnly = value)
saveSettings(settings) saveSettings(settings)
} }
} }
@ -229,7 +269,8 @@ class AdvancedSettingsViewModel(
private fun changeExploreType(value: ListingType) { private fun changeExploreType(value: ListingType) {
screenModelScope.launch { screenModelScope.launch {
updateState { it.copy(defaultExploreType = value) } updateState { it.copy(defaultExploreType = value) }
val settings = settingsRepository.currentSettings.value.copy(defaultExploreType = value.toInt()) val settings =
settingsRepository.currentSettings.value.copy(defaultExploreType = value.toInt())
saveSettings(settings) saveSettings(settings)
} }
} }
@ -245,7 +286,8 @@ class AdvancedSettingsViewModel(
private fun changeInfiniteScrollDisabled(value: Boolean) { private fun changeInfiniteScrollDisabled(value: Boolean) {
screenModelScope.launch { screenModelScope.launch {
updateState { it.copy(infiniteScrollDisabled = value) } updateState { it.copy(infiniteScrollDisabled = value) }
val settings = settingsRepository.currentSettings.value.copy(infiniteScrollEnabled = !value) val settings =
settingsRepository.currentSettings.value.copy(infiniteScrollEnabled = !value)
saveSettings(settings) saveSettings(settings)
} }
} }
@ -290,7 +332,8 @@ class AdvancedSettingsViewModel(
private fun changeInboxBackgroundCheckPeriod(value: Duration) { private fun changeInboxBackgroundCheckPeriod(value: Duration) {
screenModelScope.launch { screenModelScope.launch {
updateState { it.copy(inboxBackgroundCheckPeriod = value) } updateState { it.copy(inboxBackgroundCheckPeriod = value) }
val settings = settingsRepository.currentSettings.value.copy(inboxBackgroundCheckPeriod = value) val settings =
settingsRepository.currentSettings.value.copy(inboxBackgroundCheckPeriod = value)
saveSettings(settings) saveSettings(settings)
} }
} }
@ -314,7 +357,17 @@ class AdvancedSettingsViewModel(
private fun changeEnableButtonsToScrollBetweenComments(value: Boolean) { private fun changeEnableButtonsToScrollBetweenComments(value: Boolean) {
screenModelScope.launch { screenModelScope.launch {
updateState { it.copy(enableButtonsToScrollBetweenComments = value) } updateState { it.copy(enableButtonsToScrollBetweenComments = value) }
val settings = settingsRepository.currentSettings.value.copy(enableButtonsToScrollBetweenComments = value) val settings =
settingsRepository.currentSettings.value.copy(enableButtonsToScrollBetweenComments = value)
saveSettings(settings)
}
}
private fun changeEnableToggleFavoriteInNavDrawer(value: Boolean) {
screenModelScope.launch {
updateState { it.copy(enableToggleFavoriteInNavDrawer = value) }
val settings =
settingsRepository.currentSettings.value.copy(enableToggleFavoriteInNavDrawer = value)
saveSettings(settings) saveSettings(settings)
} }
} }

View File

@ -67,10 +67,10 @@ class CommunityDetailViewModel(
private val communitySortRepository: CommunitySortRepository, private val communitySortRepository: CommunitySortRepository,
private val postNavigationManager: PostNavigationManager, private val postNavigationManager: PostNavigationManager,
private val communityPreferredLanguageRepository: CommunityPreferredLanguageRepository, private val communityPreferredLanguageRepository: CommunityPreferredLanguageRepository,
) : CommunityDetailMviModel, ) : DefaultMviModel<CommunityDetailMviModel.Intent, CommunityDetailMviModel.UiState, CommunityDetailMviModel.Effect>(
DefaultMviModel<CommunityDetailMviModel.Intent, CommunityDetailMviModel.UiState, CommunityDetailMviModel.Effect>(
initialState = CommunityDetailMviModel.UiState(), initialState = CommunityDetailMviModel.UiState(),
) { ),
CommunityDetailMviModel {
private var hideReadPosts = false private var hideReadPosts = false
private val searchEventChannel = Channel<Unit>() private val searchEventChannel = Channel<Unit>()
@ -88,47 +88,56 @@ class CommunityDetailViewModel(
} }
} }
themeRepository.postLayout.onEach { layout -> themeRepository.postLayout
updateState { it.copy(postLayout = layout) } .onEach { layout ->
}.launchIn(this) updateState { it.copy(postLayout = layout) }
identityRepository.isLogged.onEach { logged -> }.launchIn(this)
updateState { it.copy(isLogged = logged ?: false) } identityRepository.isLogged
updateAvailableSortTypes() .onEach { logged ->
}.launchIn(this) updateState { it.copy(isLogged = logged ?: false) }
updateAvailableSortTypes()
}.launchIn(this)
settingsRepository.currentSettings.onEach { settings -> settingsRepository.currentSettings
updateState { .onEach { settings ->
it.copy( updateState {
blurNsfw = settings.blurNsfw, it.copy(
swipeActionsEnabled = settings.enableSwipeActions, blurNsfw = settings.blurNsfw,
doubleTapActionEnabled = settings.enableDoubleTapAction, swipeActionsEnabled = settings.enableSwipeActions,
fullHeightImages = settings.fullHeightImages, doubleTapActionEnabled = settings.enableDoubleTapAction,
fullWidthImages = settings.fullWidthImages, fullHeightImages = settings.fullHeightImages,
voteFormat = settings.voteFormat, fullWidthImages = settings.fullWidthImages,
autoLoadImages = settings.autoLoadImages, voteFormat = settings.voteFormat,
preferNicknames = settings.preferUserNicknames, autoLoadImages = settings.autoLoadImages,
actionsOnSwipeToStartPosts = settings.actionsOnSwipeToStartPosts, preferNicknames = settings.preferUserNicknames,
actionsOnSwipeToEndPosts = settings.actionsOnSwipeToEndPosts, actionsOnSwipeToStartPosts = settings.actionsOnSwipeToStartPosts,
showScores = settings.showScores, actionsOnSwipeToEndPosts = settings.actionsOnSwipeToEndPosts,
fadeReadPosts = settings.fadeReadPosts, showScores = settings.showScores,
showUnreadComments = settings.showUnreadComments, fadeReadPosts = settings.fadeReadPosts,
) showUnreadComments = settings.showUnreadComments,
} )
}.launchIn(this) }
}.launchIn(this)
zombieModeHelper.index.onEach { index -> zombieModeHelper.index
if (uiState.value.zombieModeActive) { .onEach { index ->
emitEffect(CommunityDetailMviModel.Effect.ZombieModeTick(index)) if (uiState.value.zombieModeActive) {
} emitEffect(CommunityDetailMviModel.Effect.ZombieModeTick(index))
}.launchIn(this) }
}.launchIn(this)
notificationCenter.subscribe(NotificationCenterEvent.PostUpdated::class).onEach { evt -> notificationCenter
handlePostUpdate(evt.model) .subscribe(NotificationCenterEvent.PostUpdated::class)
}.launchIn(this) .onEach { evt ->
notificationCenter.subscribe(NotificationCenterEvent.PostRemoved::class).onEach { evt -> handlePostUpdate(evt.model)
handlePostDelete(evt.model.id) }.launchIn(this)
}.launchIn(this) notificationCenter
notificationCenter.subscribe(NotificationCenterEvent.UserBannedPost::class) .subscribe(NotificationCenterEvent.PostRemoved::class)
.onEach { evt ->
handlePostDelete(evt.model.id)
}.launchIn(this)
notificationCenter
.subscribe(NotificationCenterEvent.UserBannedPost::class)
.onEach { evt -> .onEach { evt ->
val postId = evt.postId val postId = evt.postId
val newUser = evt.user val newUser = evt.user
@ -148,7 +157,8 @@ class CommunityDetailViewModel(
) )
} }
}.launchIn(this) }.launchIn(this)
notificationCenter.subscribe(NotificationCenterEvent.CommentRemoved::class) notificationCenter
.subscribe(NotificationCenterEvent.CommentRemoved::class)
.onEach { evt -> .onEach { evt ->
val postId = evt.model.postId val postId = evt.model.postId
uiState.value.posts.firstOrNull { it.id == postId }?.also { uiState.value.posts.firstOrNull { it.id == postId }?.also {
@ -157,7 +167,8 @@ class CommunityDetailViewModel(
} }
}.launchIn(this) }.launchIn(this)
val communityHandle = uiState.value.community.readableHandle val communityHandle = uiState.value.community.readableHandle
notificationCenter.subscribe(NotificationCenterEvent.ChangeSortType::class) notificationCenter
.subscribe(NotificationCenterEvent.ChangeSortType::class)
.onEach { evt -> .onEach { evt ->
if (evt.screenKey == communityHandle) { if (evt.screenKey == communityHandle) {
if (evt.defaultForCommunity) { if (evt.defaultForCommunity) {
@ -166,19 +177,26 @@ class CommunityDetailViewModel(
applySortType(evt.value) applySortType(evt.value)
} }
}.launchIn(this) }.launchIn(this)
notificationCenter.subscribe(NotificationCenterEvent.Share::class).onEach { evt -> notificationCenter
shareHelper.share(evt.url) .subscribe(NotificationCenterEvent.Share::class)
}.launchIn(this) .onEach { evt ->
notificationCenter.subscribe(NotificationCenterEvent.CopyText::class).onEach { shareHelper.share(evt.url)
emitEffect(CommunityDetailMviModel.Effect.TriggerCopy(it.value)) }.launchIn(this)
}.launchIn(this) notificationCenter
.subscribe(NotificationCenterEvent.CopyText::class)
.onEach {
emitEffect(CommunityDetailMviModel.Effect.TriggerCopy(it.value))
}.launchIn(this)
searchEventChannel.receiveAsFlow().debounce(1_000).onEach { searchEventChannel
updateState { it.copy(loading = false) } .receiveAsFlow()
emitEffect(CommunityDetailMviModel.Effect.BackToTop) .debounce(1_000)
delay(50) .onEach {
refresh() updateState { it.copy(loading = false) }
}.launchIn(this) emitEffect(CommunityDetailMviModel.Effect.BackToTop)
delay(50)
refresh()
}.launchIn(this)
if (uiState.value.currentUserId == null) { if (uiState.value.currentUserId == null) {
val auth = identityRepository.authToken.value.orEmpty() val auth = identityRepository.authToken.value.orEmpty()
@ -194,7 +212,8 @@ class CommunityDetailViewModel(
} }
if (uiState.value.initial) { if (uiState.value.initial) {
val defaultPostSortType = val defaultPostSortType =
settingsRepository.currentSettings.value.defaultPostSortType.toSortType() settingsRepository.currentSettings.value.defaultPostSortType
.toSortType()
val customPostSortType = val customPostSortType =
communitySortRepository.get(communityHandle)?.toSortType() communitySortRepository.get(communityHandle)?.toSortType()
val preferredLanguageId = communityPreferredLanguageRepository.get(communityHandle) val preferredLanguageId = communityPreferredLanguageRepository.get(communityHandle)
@ -296,19 +315,22 @@ class CommunityDetailViewModel(
} }
is CommunityDetailMviModel.Intent.ModFeaturePost -> is CommunityDetailMviModel.Intent.ModFeaturePost ->
uiState.value.posts.firstOrNull { it.id == intent.id } uiState.value.posts
.firstOrNull { it.id == intent.id }
?.also { post -> ?.also { post ->
feature(post = post) feature(post = post)
} }
is CommunityDetailMviModel.Intent.AdminFeaturePost -> is CommunityDetailMviModel.Intent.AdminFeaturePost ->
uiState.value.posts.firstOrNull { it.id == intent.id } uiState.value.posts
.firstOrNull { it.id == intent.id }
?.also { post -> ?.also { post ->
featureLocal(post = post) featureLocal(post = post)
} }
is CommunityDetailMviModel.Intent.ModLockPost -> is CommunityDetailMviModel.Intent.ModLockPost ->
uiState.value.posts.firstOrNull { it.id == intent.id } uiState.value.posts
.firstOrNull { it.id == intent.id }
?.also { post -> ?.also { post ->
lock(post = post) lock(post = post)
} }
@ -381,12 +403,13 @@ class CommunityDetailViewModel(
val isFavorite = val isFavorite =
favoriteCommunityRepository.getBy(accountId, currentState.community.id) != null favoriteCommunityRepository.getBy(accountId, currentState.community.id) != null
val refreshedCommunity = val refreshedCommunity =
communityRepository.get( communityRepository
auth = auth, .get(
name = currentState.community.name, auth = auth,
id = currentState.community.id, name = currentState.community.name,
instance = otherInstance, id = currentState.community.id,
)?.copy(favorite = isFavorite) instance = otherInstance,
)?.copy(favorite = isFavorite)
val moderators = val moderators =
communityRepository.getModerators( communityRepository.getModerators(
auth = auth, auth = auth,
@ -436,26 +459,28 @@ class CommunityDetailViewModel(
} }
updateState { it.copy(loading = true) } updateState { it.copy(loading = true) }
val posts = val posts =
postPaginationManager.loadNextPage().let { postPaginationManager
if (!hideReadPosts) { .loadNextPage()
it .let {
} else { if (!hideReadPosts) {
it.filter { post -> !post.read } it
} } else {
}.let { it.filter { post -> !post.read }
if (currentState.searching) { }
it.filter { post -> }.let {
listOf(post.title, post.text).any { s -> if (currentState.searching) {
s.contains( it.filter { post ->
other = currentState.searchText, listOf(post.title, post.text).any { s ->
ignoreCase = true, s.contains(
) other = currentState.searchText,
} ignoreCase = true,
)
}
}
} else {
it
} }
} else {
it
} }
}
if (uiState.value.autoLoadImages) { if (uiState.value.autoLoadImages) {
posts.forEach { post -> posts.forEach { post ->
post.imageUrl.takeIf { i -> i.isNotEmpty() }?.also { url -> post.imageUrl.takeIf { i -> i.isNotEmpty() }?.also { url ->
@ -775,9 +800,11 @@ class CommunityDetailViewModel(
if (newValue) { if (newValue) {
val model = FavoriteCommunityModel(communityId = communityId) val model = FavoriteCommunityModel(communityId = communityId)
favoriteCommunityRepository.create(model, accountId) favoriteCommunityRepository.create(model, accountId)
notificationCenter.send(NotificationCenterEvent.FavoritesUpdated)
} else { } else {
favoriteCommunityRepository.getBy(accountId, communityId)?.also { toDelete -> favoriteCommunityRepository.getBy(accountId, communityId)?.also { toDelete ->
favoriteCommunityRepository.delete(accountId, toDelete) favoriteCommunityRepository.delete(accountId, toDelete)
notificationCenter.send(NotificationCenterEvent.FavoritesUpdated)
} }
} }
val newCommunity = uiState.value.community.copy(favorite = newValue) val newCommunity = uiState.value.community.copy(favorite = newValue)

View File

@ -103,20 +103,24 @@ object ModalDrawerContent : Tab {
var uiFontSizeWorkaround by remember { mutableStateOf(true) } var uiFontSizeWorkaround by remember { mutableStateOf(true) }
LaunchedEffect(themeRepository) { LaunchedEffect(themeRepository) {
themeRepository.uiFontScale.drop(1).onEach { themeRepository.uiFontScale
uiFontSizeWorkaround = false .drop(1)
delay(50) .onEach {
uiFontSizeWorkaround = true uiFontSizeWorkaround = false
}.launchIn(this) delay(50)
uiFontSizeWorkaround = true
}.launchIn(this)
} }
if (!uiFontSizeWorkaround) { if (!uiFontSizeWorkaround) {
return return
} }
LaunchedEffect(notificationCenter) { LaunchedEffect(notificationCenter) {
notificationCenter.subscribe(NotificationCenterEvent.InstanceSelected::class).onEach { notificationCenter
// closes the navigation drawer after instance change .subscribe(NotificationCenterEvent.InstanceSelected::class)
coordinator.closeDrawer() .onEach {
}.launchIn(this) // closes the navigation drawer after instance change
coordinator.closeDrawer()
}.launchIn(this)
} }
ModalDrawerSheet { ModalDrawerSheet {
@ -125,21 +129,22 @@ object ModalDrawerContent : Tab {
instance = uiState.instance, instance = uiState.instance,
autoLoadImages = uiState.autoLoadImages, autoLoadImages = uiState.autoLoadImages,
onOpenChangeInstance = onOpenChangeInstance =
rememberCallback(model) { rememberCallback(model) {
navigationCoordinator.showBottomSheet(SelectInstanceBottomSheet()) navigationCoordinator.showBottomSheet(SelectInstanceBottomSheet())
}, },
onOpenSwitchAccount = { onOpenSwitchAccount =
navigationCoordinator.showBottomSheet(ManageAccountsScreen()) rememberCallback {
}, navigationCoordinator.showBottomSheet(ManageAccountsScreen())
},
) )
HorizontalDivider( HorizontalDivider(
modifier = modifier =
Modifier Modifier
.padding( .padding(
top = Spacing.s, top = Spacing.s,
bottom = Spacing.s, bottom = Spacing.s,
), ),
) )
if (uiState.user != null) { if (uiState.user != null) {
@ -147,16 +152,16 @@ object ModalDrawerContent : Tab {
rememberPullRefreshState( rememberPullRefreshState(
refreshing = uiState.refreshing, refreshing = uiState.refreshing,
onRefresh = onRefresh =
rememberCallback(model) { rememberCallback(model) {
model.reduce(ModalDrawerMviModel.Intent.Refresh) model.reduce(ModalDrawerMviModel.Intent.Refresh)
}, },
) )
Box( Box(
modifier = modifier =
Modifier Modifier
.weight(1f) .weight(1f)
.nestedScroll(keyboardScrollConnection) .nestedScroll(keyboardScrollConnection)
.pullRefresh(pullRefreshState), .pullRefresh(pullRefreshState),
) { ) {
LazyColumn( LazyColumn(
modifier = Modifier.fillMaxSize().padding(horizontal = Spacing.xxs), modifier = Modifier.fillMaxSize().padding(horizontal = Spacing.xxs),
@ -165,45 +170,45 @@ object ModalDrawerContent : Tab {
item { item {
TextField( TextField(
modifier = modifier =
Modifier Modifier
.scale(0.95f) .scale(0.95f)
.padding( .padding(
horizontal = Spacing.xxs, horizontal = Spacing.xxs,
vertical = Spacing.xxs, vertical = Spacing.xxs,
).fillMaxWidth(), ).fillMaxWidth(),
label = { label = {
Text(text = LocalStrings.current.exploreSearchPlaceholder) Text(text = LocalStrings.current.exploreSearchPlaceholder)
}, },
singleLine = true, singleLine = true,
value = uiState.searchText, value = uiState.searchText,
keyboardOptions = keyboardOptions =
KeyboardOptions( KeyboardOptions(
keyboardType = KeyboardType.Text, keyboardType = KeyboardType.Text,
imeAction = ImeAction.Search, imeAction = ImeAction.Search,
), ),
onValueChange = { value -> onValueChange = { value ->
model.reduce(ModalDrawerMviModel.Intent.SetSearch(value)) model.reduce(ModalDrawerMviModel.Intent.SetSearch(value))
}, },
trailingIcon = { trailingIcon = {
Icon( Icon(
modifier = modifier =
Modifier.onClick( Modifier.onClick(
onClick = { onClick = {
if (uiState.searchText.isNotEmpty()) { if (uiState.searchText.isNotEmpty()) {
model.reduce( model.reduce(
ModalDrawerMviModel.Intent.SetSearch( ModalDrawerMviModel.Intent.SetSearch(
"", "",
), ),
) )
} }
}, },
), ),
imageVector = imageVector =
if (uiState.searchText.isEmpty()) { if (uiState.searchText.isEmpty()) {
Icons.Default.Search Icons.Default.Search
} else { } else {
Icons.Default.Clear Icons.Default.Clear
}, },
contentDescription = null, contentDescription = null,
) )
}, },
@ -223,15 +228,15 @@ object ModalDrawerContent : Tab {
title = listingType.toReadableName(), title = listingType.toReadableName(),
icon = listingType.toIcon(), icon = listingType.toIcon(),
onSelected = onSelected =
rememberCallback(coordinator) { rememberCallback(coordinator) {
scope.launch { scope.launch {
focusManager.clearFocus() focusManager.clearFocus()
coordinator.toggleDrawer() coordinator.toggleDrawer()
coordinator.sendEvent( coordinator.sendEvent(
DrawerEvent.ChangeListingType(listingType), DrawerEvent.ChangeListingType(listingType),
) )
} }
}, },
) )
} }
} }
@ -259,7 +264,7 @@ object ModalDrawerContent : Tab {
items( items(
items = uiState.favorites, items = uiState.favorites,
key = { it.id.toString() }, key = { "${it.id}-favorite" },
) { community -> ) { community ->
DrawerCommunityItem( DrawerCommunityItem(
title = community.readableName(uiState.preferNicknames), title = community.readableName(uiState.preferNicknames),
@ -276,17 +281,28 @@ object ModalDrawerContent : Tab {
) )
} }
}, },
onToggleFavorite =
if (!uiState.enableToggleFavorite) {
null
} else {
rememberCallback(model) {
model.reduce(
ModalDrawerMviModel.Intent.ToggleFavorite(community.id),
)
}
},
) )
} }
items( items(
items = uiState.communities, items = uiState.communities,
key = { it.id.toString() }, key = { "${it.id}-community" },
) { community -> ) { community ->
DrawerCommunityItem( DrawerCommunityItem(
title = community.readableName(uiState.preferNicknames), title = community.readableName(uiState.preferNicknames),
subtitle = community.readableHandle, subtitle = community.readableHandle,
url = community.icon, url = community.icon,
favorite = false,
autoLoadImages = uiState.autoLoadImages, autoLoadImages = uiState.autoLoadImages,
onSelected = { onSelected = {
scope.launch { scope.launch {
@ -297,6 +313,16 @@ object ModalDrawerContent : Tab {
) )
} }
}, },
onToggleFavorite =
if (!uiState.enableToggleFavorite) {
null
} else {
rememberCallback(model) {
model.reduce(
ModalDrawerMviModel.Intent.ToggleFavorite(community.id),
)
}
},
) )
} }

View File

@ -14,8 +14,15 @@ interface ModalDrawerMviModel :
sealed interface Intent { sealed interface Intent {
data object Refresh : Intent data object Refresh : Intent
data class SetSearch(val value: String) : Intent data class SetSearch(
val value: String,
) : Intent
data object LoadNextPage : Intent data object LoadNextPage : Intent
data class ToggleFavorite(
val id: Long,
) : Intent
} }
data class UiState( data class UiState(
@ -31,6 +38,7 @@ interface ModalDrawerMviModel :
val favorites: List<CommunityModel> = emptyList(), val favorites: List<CommunityModel> = emptyList(),
val searchText: String = "", val searchText: String = "",
val isFiltering: Boolean = false, val isFiltering: Boolean = false,
val enableToggleFavorite: Boolean = false,
) )
sealed interface Effect sealed interface Effect

View File

@ -4,6 +4,7 @@ import cafe.adriel.voyager.core.model.screenModelScope
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.DefaultMviModel import com.github.diegoberaldin.raccoonforlemmy.core.architecture.DefaultMviModel
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenter import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenter
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterEvent import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterEvent
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.data.FavoriteCommunityModel
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository.AccountRepository import com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository.AccountRepository
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository.FavoriteCommunityRepository import com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository.FavoriteCommunityRepository
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository.MultiCommunityRepository import com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository.MultiCommunityRepository
@ -12,21 +13,15 @@ import com.github.diegoberaldin.raccoonforlemmy.domain.identity.repository.ApiCo
import com.github.diegoberaldin.raccoonforlemmy.domain.identity.repository.IdentityRepository import com.github.diegoberaldin.raccoonforlemmy.domain.identity.repository.IdentityRepository
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.CommunityRepository import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.CommunityRepository
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.SiteRepository import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.SiteRepository
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapConcat
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withTimeout import kotlinx.coroutines.withTimeout
import kotlinx.coroutines.yield import kotlinx.coroutines.yield
@ -41,47 +36,60 @@ class ModalDrawerViewModel(
private val settingsRepository: SettingsRepository, private val settingsRepository: SettingsRepository,
private val favoriteCommunityRepository: FavoriteCommunityRepository, private val favoriteCommunityRepository: FavoriteCommunityRepository,
private val notificationCenter: NotificationCenter, private val notificationCenter: NotificationCenter,
) : ModalDrawerMviModel, ) : DefaultMviModel<ModalDrawerMviModel.Intent, ModalDrawerMviModel.UiState, ModalDrawerMviModel.Effect>(
DefaultMviModel<ModalDrawerMviModel.Intent, ModalDrawerMviModel.UiState, ModalDrawerMviModel.Effect>(
initialState = ModalDrawerMviModel.UiState(), initialState = ModalDrawerMviModel.UiState(),
) { ),
ModalDrawerMviModel {
private var currentPage = 1 private var currentPage = 1
private val searchEventChannel = Channel<Unit>() private val searchEventChannel = Channel<Unit>()
init { init {
screenModelScope.launch { screenModelScope.launch {
apiConfigurationRepository.instance.onEach { instance -> apiConfigurationRepository.instance
updateState { .onEach { instance ->
it.copy(instance = instance) updateState {
} it.copy(instance = instance)
}.launchIn(this) }
}.launchIn(this)
identityRepository.isLogged.onEach { _ -> identityRepository.isLogged
refreshUser() .onEach { _ ->
refresh() refreshUser()
}.launchIn(this) refresh()
}.launchIn(this)
notificationCenter.subscribe(NotificationCenterEvent.Logout::class).onEach { notificationCenter
delay(250) .subscribe(NotificationCenterEvent.Logout::class)
refreshUser() .onEach {
refresh() delay(250)
}.launchIn(this) refreshUser()
refresh()
}.launchIn(this)
settingsRepository.currentSettings.onEach { settings -> notificationCenter
updateState { .subscribe(NotificationCenterEvent.FavoritesUpdated::class)
it.copy( .onEach {
autoLoadImages = settings.autoLoadImages, refresh()
preferNicknames = settings.preferUserNicknames, }.launchIn(this)
)
} settingsRepository.currentSettings
}.launchIn(this) .onEach { settings ->
updateState {
it.copy(
autoLoadImages = settings.autoLoadImages,
preferNicknames = settings.preferUserNicknames,
enableToggleFavorite = settings.enableToggleFavoriteInNavDrawer,
)
}
}.launchIn(this)
@OptIn(FlowPreview::class) @OptIn(FlowPreview::class)
searchEventChannel.receiveAsFlow().debounce(1000).onEach { searchEventChannel
refresh() .receiveAsFlow()
}.launchIn(this) .debounce(1000)
.onEach {
observeChangesInFavoriteCommunities() refresh()
}.launchIn(this)
delay(250) delay(250)
refreshUser() refreshUser()
@ -89,53 +97,25 @@ class ModalDrawerViewModel(
} }
} }
@OptIn(ExperimentalCoroutinesApi::class)
private fun CoroutineScope.observeChangesInFavoriteCommunities() {
channelFlow {
while (isActive) {
val accountId = accountRepository.getActive()?.id
trySend(accountId)
delay(1000)
}
}.distinctUntilChanged().flatMapConcat { accountId ->
channelFlow {
while (isActive) {
val communityIds =
favoriteCommunityRepository.getAll(accountId).map { it.communityId }
trySend(communityIds)
delay(1000)
}
}.distinctUntilChanged()
}.onEach { favoriteCommunityIds ->
val currentState = uiState.value
val newFavorites =
currentState.favorites.filter { it.id in favoriteCommunityIds } + currentState.communities.filter { it.id in favoriteCommunityIds }
val newCommunities =
(currentState.communities.filter { it.id !in favoriteCommunityIds } + currentState.favorites.filter { it.id !in favoriteCommunityIds })
updateState {
it.copy(
favorites = newFavorites,
communities = newCommunities,
)
}
}.launchIn(this)
}
override fun reduce(intent: ModalDrawerMviModel.Intent) { override fun reduce(intent: ModalDrawerMviModel.Intent) {
when (intent) { when (intent) {
ModalDrawerMviModel.Intent.Refresh -> screenModelScope.launch { ModalDrawerMviModel.Intent.Refresh ->
refresh() screenModelScope.launch {
} refresh()
}
is ModalDrawerMviModel.Intent.SetSearch -> screenModelScope.launch { is ModalDrawerMviModel.Intent.SetSearch ->
updateState { it.copy(searchText = intent.value) } screenModelScope.launch {
searchEventChannel.send(Unit) updateState { it.copy(searchText = intent.value) }
} searchEventChannel.send(Unit)
}
ModalDrawerMviModel.Intent.LoadNextPage -> screenModelScope.launch { ModalDrawerMviModel.Intent.LoadNextPage ->
loadNextPage() screenModelScope.launch {
} loadNextPage()
}
is ModalDrawerMviModel.Intent.ToggleFavorite -> toggleFavorite(intent.id)
} }
} }
@ -175,46 +155,48 @@ class ModalDrawerViewModel(
val accountId = accountRepository.getActive()?.id val accountId = accountRepository.getActive()?.id
val searchText = uiState.value.searchText val searchText = uiState.value.searchText
val multiCommunities = val multiCommunities =
accountId?.let { accountId
multiCommunityRepository.getAll(it) ?.let {
.let { communities -> multiCommunityRepository
if (searchText.isEmpty()) { .getAll(it)
communities .let { communities ->
} else { if (searchText.isEmpty()) {
communities.filter { c -> communities
c.name.contains( } else {
other = searchText, communities.filter { c ->
ignoreCase = true c.name.contains(
) other = searchText,
ignoreCase = true,
)
}
} }
} }.sortedBy { e -> e.name }
} }.orEmpty()
.sortedBy { e -> e.name }
}.orEmpty()
val favorites = coroutineScope { val favorites =
val auth = identityRepository.authToken.value coroutineScope {
favoriteCommunityRepository.getAll(accountId).mapNotNull { favorite -> val auth = identityRepository.authToken.value
val communityId = favorite.communityId favoriteCommunityRepository.getAll(accountId).mapNotNull { favorite ->
async { val communityId = favorite.communityId
communityRepository.get( async {
auth = auth, communityRepository.get(
id = communityId, auth = auth,
) id = communityId,
}.await() )
} }.await()
}.let { communities -> }
if (searchText.isEmpty()) { }.let { communities ->
communities if (searchText.isEmpty()) {
} else { communities
communities.filter { c -> } else {
c.name.contains( communities.filter { c ->
other = searchText, c.name.contains(
ignoreCase = true other = searchText,
) ignoreCase = true,
)
}
} }
} }
}
updateState { updateState {
it.copy( it.copy(
@ -234,21 +216,23 @@ class ModalDrawerViewModel(
} }
val auth = identityRepository.authToken.value val auth = identityRepository.authToken.value
val searchText = uiState.value.searchText val searchText = uiState.value.searchText
val itemsToAdd = communityRepository.getSubscribed( val itemsToAdd =
auth = auth, communityRepository
page = currentPage, .getSubscribed(
query = searchText, auth = auth,
).filter { c1 -> page = currentPage,
// exclude items already included in favorites query = searchText,
currentState.favorites.none { c2 -> c2.id == c1.id } ).filter { c1 ->
}.filter { c1 -> // exclude items already included in favorites
// prevents accidental duplication currentState.favorites.none { c2 -> c2.id == c1.id }
if (!currentState.refreshing) { }.filter { c1 ->
currentState.communities.none { c2 -> c2.id == c1.id } // prevents accidental duplication
} else { if (!currentState.refreshing) {
true currentState.communities.none { c2 -> c2.id == c1.id }
} } else {
} true
}
}
if (itemsToAdd.isNotEmpty()) { if (itemsToAdd.isNotEmpty()) {
currentPage++ currentPage++
} }
@ -256,14 +240,49 @@ class ModalDrawerViewModel(
it.copy( it.copy(
isFiltering = searchText.isNotEmpty(), isFiltering = searchText.isNotEmpty(),
refreshing = false, refreshing = false,
communities = if (currentState.refreshing) { communities =
itemsToAdd if (currentState.refreshing) {
} else { itemsToAdd
currentState.communities + itemsToAdd } else {
}, currentState.communities + itemsToAdd
},
canFetchMore = itemsToAdd.isNotEmpty(), canFetchMore = itemsToAdd.isNotEmpty(),
loading = false, loading = false,
) )
} }
} }
private fun toggleFavorite(communityId: Long) {
screenModelScope.launch {
val currentState = uiState.value
val accountId = accountRepository.getActive()?.id ?: 0L
val isCurrentlyFavorite = currentState.favorites.any { it.id == communityId }
if (isCurrentlyFavorite) {
val community = currentState.favorites.first { it.id == communityId }
favoriteCommunityRepository.getBy(accountId, communityId)?.also { toDelete ->
favoriteCommunityRepository.delete(accountId, toDelete)
}
val newFavorites = currentState.favorites.filter { it.id != communityId }
val newCommunities = currentState.communities + community
updateState {
it.copy(
favorites = newFavorites,
communities = newCommunities,
)
}
} else {
val model = FavoriteCommunityModel(communityId = communityId)
favoriteCommunityRepository.create(model, accountId)
val community = currentState.communities.first { it.id == communityId }
val newFavorites = currentState.favorites + community
val newCommunities = currentState.communities.filter { it.id != communityId }
updateState {
it.copy(
favorites = newFavorites,
communities = newCommunities,
)
}
}
}
}
} }

View File

@ -1,10 +1,12 @@
package com.github.diegoberaldin.raccoonforlemmy.unit.drawer.components package com.github.diegoberaldin.raccoonforlemmy.unit.drawer.components
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Star import androidx.compose.material.icons.filled.Star
import androidx.compose.material.icons.filled.StarBorder
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationDrawerItem import androidx.compose.material3.NavigationDrawerItem
@ -13,10 +15,12 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.dp
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.IconSize import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.IconSize
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.ancillaryTextAlpha import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.ancillaryTextAlpha
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.CustomImage import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.CustomImage
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.PlaceholderImage import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.PlaceholderImage
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick
@Composable @Composable
internal fun DrawerCommunityItem( internal fun DrawerCommunityItem(
@ -27,6 +31,7 @@ internal fun DrawerCommunityItem(
favorite: Boolean = false, favorite: Boolean = false,
autoLoadImages: Boolean = true, autoLoadImages: Boolean = true,
onSelected: (() -> Unit)? = null, onSelected: (() -> Unit)? = null,
onToggleFavorite: (() -> Unit)? = null,
) { ) {
NavigationDrawerItem( NavigationDrawerItem(
modifier = modifier, modifier = modifier,
@ -51,7 +56,8 @@ internal fun DrawerCommunityItem(
}, },
label = { label = {
val fullColor = MaterialTheme.colorScheme.onBackground val fullColor = MaterialTheme.colorScheme.onBackground
val ancillaryColor = MaterialTheme.colorScheme.onBackground.copy(alpha = ancillaryTextAlpha) val ancillaryColor =
MaterialTheme.colorScheme.onBackground.copy(alpha = ancillaryTextAlpha)
Column { Column {
Text( Text(
text = title, text = title,
@ -70,13 +76,24 @@ internal fun DrawerCommunityItem(
} }
}, },
badge = badge =
if (favorite) { if (onToggleFavorite != null || favorite) {
@Composable { @Composable {
Icon( Icon(
modifier = Modifier.size(IconSize.s), modifier =
imageVector = Icons.Default.Star, Modifier
.size(IconSize.s)
.padding(start = 1.dp)
.onClick(onClick = { onToggleFavorite?.invoke() }),
imageVector = if (favorite) Icons.Default.Star else Icons.Default.StarBorder,
contentDescription = "", contentDescription = "",
tint = MaterialTheme.colorScheme.onBackground, tint =
MaterialTheme.colorScheme.onBackground.let {
if (favorite) {
it
} else {
it.copy(alpha = 0.25f)
}
},
) )
} }
} else { } else {

View File

@ -34,36 +34,42 @@ class ManageSubscriptionsViewModel(
private val siteRepository: SiteRepository, private val siteRepository: SiteRepository,
private val hapticFeedback: HapticFeedback, private val hapticFeedback: HapticFeedback,
private val notificationCenter: NotificationCenter, private val notificationCenter: NotificationCenter,
) : ManageSubscriptionsMviModel, ) : DefaultMviModel<ManageSubscriptionsMviModel.Intent, ManageSubscriptionsMviModel.UiState, ManageSubscriptionsMviModel.Effect>(
DefaultMviModel<ManageSubscriptionsMviModel.Intent, ManageSubscriptionsMviModel.UiState, ManageSubscriptionsMviModel.Effect>(
initialState = ManageSubscriptionsMviModel.UiState(), initialState = ManageSubscriptionsMviModel.UiState(),
) { ),
ManageSubscriptionsMviModel {
private var currentPage = 1 private var currentPage = 1
private val searchEventChannel = Channel<Unit>() private val searchEventChannel = Channel<Unit>()
init { init {
screenModelScope.launch { screenModelScope.launch {
settingsRepository.currentSettings.onEach { settings -> settingsRepository.currentSettings
updateState { .onEach { settings ->
it.copy( updateState {
autoLoadImages = settings.autoLoadImages, it.copy(
preferNicknames = settings.preferUserNicknames, autoLoadImages = settings.autoLoadImages,
) preferNicknames = settings.preferUserNicknames,
} )
}.launchIn(this) }
notificationCenter.subscribe(NotificationCenterEvent.MultiCommunityCreated::class) }.launchIn(this)
notificationCenter
.subscribe(NotificationCenterEvent.MultiCommunityCreated::class)
.onEach { evt -> .onEach { evt ->
handleMultiCommunityCreated(evt.model) handleMultiCommunityCreated(evt.model)
}.launchIn(this) }.launchIn(this)
notificationCenter.subscribe(NotificationCenterEvent.CommunitySubscriptionChanged::class) notificationCenter
.subscribe(NotificationCenterEvent.CommunitySubscriptionChanged::class)
.onEach { evt -> .onEach { evt ->
handleCommunityUpdate(evt.value) handleCommunityUpdate(evt.value)
}.launchIn(this) }.launchIn(this)
searchEventChannel.receiveAsFlow().debounce(1000).onEach { searchEventChannel
emitEffect(ManageSubscriptionsMviModel.Effect.BackToTop) .receiveAsFlow()
refresh() .debounce(1000)
}.launchIn(this) .onEach {
emitEffect(ManageSubscriptionsMviModel.Effect.BackToTop)
refresh()
}.launchIn(this)
if (uiState.value.communities.isEmpty()) { if (uiState.value.communities.isEmpty()) {
// determine whether community creation is allowed // determine whether community creation is allowed
val isAdmin = identityRepository.cachedUser?.admin ?: false val isAdmin = identityRepository.cachedUser?.admin ?: false
@ -89,11 +95,12 @@ class ManageSubscriptionsViewModel(
} }
is ManageSubscriptionsMviModel.Intent.DeleteMultiCommunity -> { is ManageSubscriptionsMviModel.Intent.DeleteMultiCommunity -> {
uiState.value.multiCommunities.firstOrNull { uiState.value.multiCommunities
(it.id ?: 0L) == intent.id .firstOrNull {
}?.also { community -> (it.id ?: 0L) == intent.id
deleteMultiCommunity(community) }?.also { community ->
} deleteMultiCommunity(community)
}
} }
is ManageSubscriptionsMviModel.Intent.ToggleFavorite -> { is ManageSubscriptionsMviModel.Intent.ToggleFavorite -> {
@ -122,7 +129,8 @@ class ManageSubscriptionsViewModel(
currentPage = 1 currentPage = 1
val accountId = accountRepository.getActive()?.id ?: 0L val accountId = accountRepository.getActive()?.id ?: 0L
val multiCommunitites = val multiCommunitites =
multiCommunityRepository.getAll(accountId) multiCommunityRepository
.getAll(accountId)
.let { .let {
val searchText = uiState.value.searchText val searchText = uiState.value.searchText
if (searchText.isNotEmpty()) { if (searchText.isNotEmpty()) {
@ -132,8 +140,7 @@ class ManageSubscriptionsViewModel(
} else { } else {
it it
} }
} }.sortedBy { it.name }
.sortedBy { it.name }
updateState { updateState {
it.copy( it.copy(
@ -193,9 +200,11 @@ class ManageSubscriptionsViewModel(
if (newValue) { if (newValue) {
val model = FavoriteCommunityModel(communityId = communityId) val model = FavoriteCommunityModel(communityId = communityId)
favoriteCommunityRepository.create(model, accountId) favoriteCommunityRepository.create(model, accountId)
notificationCenter.send(NotificationCenterEvent.FavoritesUpdated)
} else { } else {
favoriteCommunityRepository.getBy(accountId, communityId)?.also { toDelete -> favoriteCommunityRepository.getBy(accountId, communityId)?.also { toDelete ->
favoriteCommunityRepository.delete(accountId, toDelete) favoriteCommunityRepository.delete(accountId, toDelete)
notificationCenter.send(NotificationCenterEvent.FavoritesUpdated)
} }
} }
val newCommunity = community.copy(favorite = newValue) val newCommunity = community.copy(favorite = newValue)
@ -240,17 +249,19 @@ class ManageSubscriptionsViewModel(
val favoriteCommunityIds = val favoriteCommunityIds =
favoriteCommunityRepository.getAll(accountId).map { it.communityId } favoriteCommunityRepository.getAll(accountId).map { it.communityId }
val itemsToAdd = val itemsToAdd =
communityRepository.getSubscribed( communityRepository
auth = auth, .getSubscribed(
page = currentPage, auth = auth,
query = searchText, page = currentPage,
).map { community -> query = searchText,
community.copy(favorite = community.id in favoriteCommunityIds) ).map { community ->
}.sortedBy { it.name }.let { community.copy(favorite = community.id in favoriteCommunityIds)
val favorites = it.filter { e -> e.favorite } }.sortedBy { it.name }
val res = it - favorites.toSet() .let {
favorites + res val favorites = it.filter { e -> e.favorite }
} val res = it - favorites.toSet()
favorites + res
}
if (itemsToAdd.isNotEmpty()) { if (itemsToAdd.isNotEmpty()) {
currentPage++ currentPage++
} }