From 20057d2072a56cb6c5536f6ba79b1592e4fdf60b Mon Sep 17 00:00:00 2001 From: Devota Aabel Date: Thu, 16 Nov 2023 13:23:39 -0500 Subject: [PATCH] Blocks and mutes list part 2 - Created and populated blocks list screen with api result (#285) I've been reading the [API guidelines for compose](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md#component_s-purpose), and I've tried to implement a few suggestions in this PR: - Created separate named styled text composables (see `SmallTextLabel`, `LargeTextTitle`, etc.) instead of manually always needing to pass in style to emulate "building blocks" described in docs - Started updating parameter order - Added modifier to all components (as the first optional param always) - Tried to give each component only one problem to solve - Updated `QuickAccountView` with a button slot (and other guidelines) --- .../core/designsystem/component/TextBody.kt | 135 ++++++++++++++++++ .../designsystem/component/TextDisplay.kt | 135 ++++++++++++++++++ .../core/designsystem/component/TextLabel.kt | 135 ++++++++++++++++++ .../core/designsystem/component/TextTitle.kt | 135 ++++++++++++++++++ .../social/core/network/mastodon/BlocksApi.kt | 10 ++ .../network/mastodon/MastodonNetworkModule.kt | 10 +- .../social/core/network/mastodon/MutesApi.kt | 9 ++ .../repository/mastodon/BlocksRepository.kt | 8 ++ .../mastodon/MastodonRepositoryModule.kt | 2 + .../repository/mastodon/MutesRepository.kt | 8 ++ .../account/quickview/AccountQuickView.kt | 79 +++++----- .../feature/followers/FollowersScreen.kt | 7 +- .../social/feature/settings/SettingsModule.kt | 5 + .../settings/about/AboutSettingsScreen.kt | 2 +- .../BlockedUsersSettingsScreen.kt | 55 ++++++- .../blockedusers/BlockedUsersViewModel.kt | 33 +++++ .../mutedusers/MutedUsersSettingsViewModel.kt | 31 ++++ .../settings/src/main/res/values/strings.xml | 1 + 18 files changed, 745 insertions(+), 55 deletions(-) create mode 100644 core/designsystem/src/main/java/org/mozilla/social/core/designsystem/component/TextBody.kt create mode 100644 core/designsystem/src/main/java/org/mozilla/social/core/designsystem/component/TextDisplay.kt create mode 100644 core/designsystem/src/main/java/org/mozilla/social/core/designsystem/component/TextLabel.kt create mode 100644 core/designsystem/src/main/java/org/mozilla/social/core/designsystem/component/TextTitle.kt create mode 100644 core/network/mastodon/src/main/java/org/mozilla/social/core/network/mastodon/BlocksApi.kt create mode 100644 core/network/mastodon/src/main/java/org/mozilla/social/core/network/mastodon/MutesApi.kt create mode 100644 core/repository/mastodon/src/main/java/org/mozilla/social/core/repository/mastodon/BlocksRepository.kt create mode 100644 core/repository/mastodon/src/main/java/org/mozilla/social/core/repository/mastodon/MutesRepository.kt create mode 100644 feature/settings/src/main/java/org/mozilla/social/feature/settings/contentpreferences/blockedusers/BlockedUsersViewModel.kt create mode 100644 feature/settings/src/main/java/org/mozilla/social/feature/settings/contentpreferences/mutedusers/MutedUsersSettingsViewModel.kt diff --git a/core/designsystem/src/main/java/org/mozilla/social/core/designsystem/component/TextBody.kt b/core/designsystem/src/main/java/org/mozilla/social/core/designsystem/component/TextBody.kt new file mode 100644 index 000000000..66604a7fb --- /dev/null +++ b/core/designsystem/src/main/java/org/mozilla/social/core/designsystem/component/TextBody.kt @@ -0,0 +1,135 @@ +package org.mozilla.social.core.designsystem.component + +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextLayoutResult +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextDecoration +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.TextUnit +import org.mozilla.social.core.designsystem.theme.MoSoTheme + +@Composable +fun SmallTextBody( + text: String, + modifier: Modifier = Modifier, + color: Color = Color.Unspecified, + fontSize: TextUnit = TextUnit.Unspecified, + fontStyle: FontStyle? = null, + fontWeight: FontWeight? = null, + fontFamily: FontFamily? = null, + letterSpacing: TextUnit = TextUnit.Unspecified, + textDecoration: TextDecoration? = null, + textAlign: TextAlign? = null, + lineHeight: TextUnit = TextUnit.Unspecified, + overflow: TextOverflow = TextOverflow.Clip, + softWrap: Boolean = true, + maxLines: Int = Int.MAX_VALUE, + minLines: Int = 1, + onTextLayout: (TextLayoutResult) -> Unit = {}, +) { + Text( + text = text, + modifier = modifier, + color = color, + fontSize = fontSize, + fontStyle = fontStyle, + fontWeight = fontWeight, + fontFamily = fontFamily, + letterSpacing = letterSpacing, + textDecoration = textDecoration, + textAlign = textAlign, + lineHeight = lineHeight, + overflow = overflow, + softWrap = softWrap, + maxLines = maxLines, + minLines = minLines, + onTextLayout = onTextLayout, + style = MoSoTheme.typography.bodySmall, + ) +} + +@Composable +fun MediumTextBody( + text: String, + modifier: Modifier = Modifier, + color: Color = Color.Unspecified, + fontSize: TextUnit = TextUnit.Unspecified, + fontStyle: FontStyle? = null, + fontWeight: FontWeight? = null, + fontFamily: FontFamily? = null, + letterSpacing: TextUnit = TextUnit.Unspecified, + textDecoration: TextDecoration? = null, + textAlign: TextAlign? = null, + lineHeight: TextUnit = TextUnit.Unspecified, + overflow: TextOverflow = TextOverflow.Clip, + softWrap: Boolean = true, + maxLines: Int = Int.MAX_VALUE, + minLines: Int = 1, + onTextLayout: (TextLayoutResult) -> Unit = {}, +) { + Text( + text = text, + modifier = modifier, + color = color, + fontSize = fontSize, + fontStyle = fontStyle, + fontWeight = fontWeight, + fontFamily = fontFamily, + letterSpacing = letterSpacing, + textDecoration = textDecoration, + textAlign = textAlign, + lineHeight = lineHeight, + overflow = overflow, + softWrap = softWrap, + maxLines = maxLines, + minLines = minLines, + onTextLayout = onTextLayout, + style = MoSoTheme.typography.bodyMedium, + ) +} + +@Composable +fun LargeTextBody( + text: String, + modifier: Modifier = Modifier, + color: Color = Color.Unspecified, + fontSize: TextUnit = TextUnit.Unspecified, + fontStyle: FontStyle? = null, + fontWeight: FontWeight? = null, + fontFamily: FontFamily? = null, + letterSpacing: TextUnit = TextUnit.Unspecified, + textDecoration: TextDecoration? = null, + textAlign: TextAlign? = null, + lineHeight: TextUnit = TextUnit.Unspecified, + overflow: TextOverflow = TextOverflow.Clip, + softWrap: Boolean = true, + maxLines: Int = Int.MAX_VALUE, + minLines: Int = 1, + onTextLayout: (TextLayoutResult) -> Unit = {}, +) { + Text( + text = text, + modifier = modifier, + color = color, + fontSize = fontSize, + fontStyle = fontStyle, + fontWeight = fontWeight, + fontFamily = fontFamily, + letterSpacing = letterSpacing, + textDecoration = textDecoration, + textAlign = textAlign, + lineHeight = lineHeight, + overflow = overflow, + softWrap = softWrap, + maxLines = maxLines, + minLines = minLines, + onTextLayout = onTextLayout, + style = MoSoTheme.typography.bodyLarge, + ) +} \ No newline at end of file diff --git a/core/designsystem/src/main/java/org/mozilla/social/core/designsystem/component/TextDisplay.kt b/core/designsystem/src/main/java/org/mozilla/social/core/designsystem/component/TextDisplay.kt new file mode 100644 index 000000000..7b237978d --- /dev/null +++ b/core/designsystem/src/main/java/org/mozilla/social/core/designsystem/component/TextDisplay.kt @@ -0,0 +1,135 @@ +package org.mozilla.social.core.designsystem.component + +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextLayoutResult +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextDecoration +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.TextUnit +import org.mozilla.social.core.designsystem.theme.MoSoTheme + +@Composable +fun SmallTextDisplay( + text: String, + modifier: Modifier = Modifier, + color: Color = Color.Unspecified, + fontSize: TextUnit = TextUnit.Unspecified, + fontStyle: FontStyle? = null, + fontWeight: FontWeight? = null, + fontFamily: FontFamily? = null, + letterSpacing: TextUnit = TextUnit.Unspecified, + textDecoration: TextDecoration? = null, + textAlign: TextAlign? = null, + lineHeight: TextUnit = TextUnit.Unspecified, + overflow: TextOverflow = TextOverflow.Clip, + softWrap: Boolean = true, + maxLines: Int = Int.MAX_VALUE, + minLines: Int = 1, + onTextLayout: (TextLayoutResult) -> Unit = {}, +) { + Text( + text = text, + modifier = modifier, + color = color, + fontSize = fontSize, + fontStyle = fontStyle, + fontWeight = fontWeight, + fontFamily = fontFamily, + letterSpacing = letterSpacing, + textDecoration = textDecoration, + textAlign = textAlign, + lineHeight = lineHeight, + overflow = overflow, + softWrap = softWrap, + maxLines = maxLines, + minLines = minLines, + onTextLayout = onTextLayout, + style = MoSoTheme.typography.displaySmall, + ) +} + +@Composable +fun MediumTextDisplay( + text: String, + modifier: Modifier = Modifier, + color: Color = Color.Unspecified, + fontSize: TextUnit = TextUnit.Unspecified, + fontStyle: FontStyle? = null, + fontWeight: FontWeight? = null, + fontFamily: FontFamily? = null, + letterSpacing: TextUnit = TextUnit.Unspecified, + textDecoration: TextDecoration? = null, + textAlign: TextAlign? = null, + lineHeight: TextUnit = TextUnit.Unspecified, + overflow: TextOverflow = TextOverflow.Clip, + softWrap: Boolean = true, + maxLines: Int = Int.MAX_VALUE, + minLines: Int = 1, + onTextLayout: (TextLayoutResult) -> Unit = {}, +) { + Text( + text = text, + modifier = modifier, + color = color, + fontSize = fontSize, + fontStyle = fontStyle, + fontWeight = fontWeight, + fontFamily = fontFamily, + letterSpacing = letterSpacing, + textDecoration = textDecoration, + textAlign = textAlign, + lineHeight = lineHeight, + overflow = overflow, + softWrap = softWrap, + maxLines = maxLines, + minLines = minLines, + onTextLayout = onTextLayout, + style = MoSoTheme.typography.displayMedium, + ) +} + +@Composable +fun LargeTextDisplay( + text: String, + modifier: Modifier = Modifier, + color: Color = Color.Unspecified, + fontSize: TextUnit = TextUnit.Unspecified, + fontStyle: FontStyle? = null, + fontWeight: FontWeight? = null, + fontFamily: FontFamily? = null, + letterSpacing: TextUnit = TextUnit.Unspecified, + textDecoration: TextDecoration? = null, + textAlign: TextAlign? = null, + lineHeight: TextUnit = TextUnit.Unspecified, + overflow: TextOverflow = TextOverflow.Clip, + softWrap: Boolean = true, + maxLines: Int = Int.MAX_VALUE, + minLines: Int = 1, + onTextLayout: (TextLayoutResult) -> Unit = {}, +) { + Text( + text = text, + modifier = modifier, + color = color, + fontSize = fontSize, + fontStyle = fontStyle, + fontWeight = fontWeight, + fontFamily = fontFamily, + letterSpacing = letterSpacing, + textDecoration = textDecoration, + textAlign = textAlign, + lineHeight = lineHeight, + overflow = overflow, + softWrap = softWrap, + maxLines = maxLines, + minLines = minLines, + onTextLayout = onTextLayout, + style = MoSoTheme.typography.displayLarge, + ) +} \ No newline at end of file diff --git a/core/designsystem/src/main/java/org/mozilla/social/core/designsystem/component/TextLabel.kt b/core/designsystem/src/main/java/org/mozilla/social/core/designsystem/component/TextLabel.kt new file mode 100644 index 000000000..696d2cc7c --- /dev/null +++ b/core/designsystem/src/main/java/org/mozilla/social/core/designsystem/component/TextLabel.kt @@ -0,0 +1,135 @@ +package org.mozilla.social.core.designsystem.component + +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextLayoutResult +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextDecoration +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.TextUnit +import org.mozilla.social.core.designsystem.theme.MoSoTheme + +@Composable +fun SmallTextLabel( + text: String, + modifier: Modifier = Modifier, + color: Color = Color.Unspecified, + fontSize: TextUnit = TextUnit.Unspecified, + fontStyle: FontStyle? = null, + fontWeight: FontWeight? = null, + fontFamily: FontFamily? = null, + letterSpacing: TextUnit = TextUnit.Unspecified, + textDecoration: TextDecoration? = null, + textAlign: TextAlign? = null, + lineHeight: TextUnit = TextUnit.Unspecified, + overflow: TextOverflow = TextOverflow.Clip, + softWrap: Boolean = true, + maxLines: Int = Int.MAX_VALUE, + minLines: Int = 1, + onTextLayout: (TextLayoutResult) -> Unit = {}, +) { + Text( + text = text, + modifier = modifier, + color = color, + fontSize = fontSize, + fontStyle = fontStyle, + fontWeight = fontWeight, + fontFamily = fontFamily, + letterSpacing = letterSpacing, + textDecoration = textDecoration, + textAlign = textAlign, + lineHeight = lineHeight, + overflow = overflow, + softWrap = softWrap, + maxLines = maxLines, + minLines = minLines, + onTextLayout = onTextLayout, + style = MoSoTheme.typography.labelSmall, + ) +} + +@Composable +fun MediumTextLabel( + text: String, + modifier: Modifier = Modifier, + color: Color = Color.Unspecified, + fontSize: TextUnit = TextUnit.Unspecified, + fontStyle: FontStyle? = null, + fontWeight: FontWeight? = null, + fontFamily: FontFamily? = null, + letterSpacing: TextUnit = TextUnit.Unspecified, + textDecoration: TextDecoration? = null, + textAlign: TextAlign? = null, + lineHeight: TextUnit = TextUnit.Unspecified, + overflow: TextOverflow = TextOverflow.Clip, + softWrap: Boolean = true, + maxLines: Int = Int.MAX_VALUE, + minLines: Int = 1, + onTextLayout: (TextLayoutResult) -> Unit = {}, +) { + Text( + text = text, + modifier = modifier, + color = color, + fontSize = fontSize, + fontStyle = fontStyle, + fontWeight = fontWeight, + fontFamily = fontFamily, + letterSpacing = letterSpacing, + textDecoration = textDecoration, + textAlign = textAlign, + lineHeight = lineHeight, + overflow = overflow, + softWrap = softWrap, + maxLines = maxLines, + minLines = minLines, + onTextLayout = onTextLayout, + style = MoSoTheme.typography.labelMedium, + ) +} + +@Composable +fun LargeTextLabel( + text: String, + modifier: Modifier = Modifier, + color: Color = Color.Unspecified, + fontSize: TextUnit = TextUnit.Unspecified, + fontStyle: FontStyle? = null, + fontWeight: FontWeight? = null, + fontFamily: FontFamily? = null, + letterSpacing: TextUnit = TextUnit.Unspecified, + textDecoration: TextDecoration? = null, + textAlign: TextAlign? = null, + lineHeight: TextUnit = TextUnit.Unspecified, + overflow: TextOverflow = TextOverflow.Clip, + softWrap: Boolean = true, + maxLines: Int = Int.MAX_VALUE, + minLines: Int = 1, + onTextLayout: (TextLayoutResult) -> Unit = {}, +) { + Text( + text = text, + modifier = modifier, + color = color, + fontSize = fontSize, + fontStyle = fontStyle, + fontWeight = fontWeight, + fontFamily = fontFamily, + letterSpacing = letterSpacing, + textDecoration = textDecoration, + textAlign = textAlign, + lineHeight = lineHeight, + overflow = overflow, + softWrap = softWrap, + maxLines = maxLines, + minLines = minLines, + onTextLayout = onTextLayout, + style = MoSoTheme.typography.labelLarge, + ) +} \ No newline at end of file diff --git a/core/designsystem/src/main/java/org/mozilla/social/core/designsystem/component/TextTitle.kt b/core/designsystem/src/main/java/org/mozilla/social/core/designsystem/component/TextTitle.kt new file mode 100644 index 000000000..d32ba934f --- /dev/null +++ b/core/designsystem/src/main/java/org/mozilla/social/core/designsystem/component/TextTitle.kt @@ -0,0 +1,135 @@ +package org.mozilla.social.core.designsystem.component + +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextLayoutResult +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextDecoration +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.TextUnit +import org.mozilla.social.core.designsystem.theme.MoSoTheme + +@Composable +fun SmallTextTitle( + text: String, + modifier: Modifier = Modifier, + color: Color = Color.Unspecified, + fontSize: TextUnit = TextUnit.Unspecified, + fontStyle: FontStyle? = null, + fontWeight: FontWeight? = null, + fontFamily: FontFamily? = null, + letterSpacing: TextUnit = TextUnit.Unspecified, + textDecoration: TextDecoration? = null, + textAlign: TextAlign? = null, + lineHeight: TextUnit = TextUnit.Unspecified, + overflow: TextOverflow = TextOverflow.Clip, + softWrap: Boolean = true, + maxLines: Int = Int.MAX_VALUE, + minLines: Int = 1, + onTextLayout: (TextLayoutResult) -> Unit = {}, +) { + Text( + text = text, + modifier = modifier, + color = color, + fontSize = fontSize, + fontStyle = fontStyle, + fontWeight = fontWeight, + fontFamily = fontFamily, + letterSpacing = letterSpacing, + textDecoration = textDecoration, + textAlign = textAlign, + lineHeight = lineHeight, + overflow = overflow, + softWrap = softWrap, + maxLines = maxLines, + minLines = minLines, + onTextLayout = onTextLayout, + style = MoSoTheme.typography.titleSmall, + ) +} + +@Composable +fun MediumTextTitle( + text: String, + modifier: Modifier = Modifier, + color: Color = Color.Unspecified, + fontSize: TextUnit = TextUnit.Unspecified, + fontStyle: FontStyle? = null, + fontWeight: FontWeight? = null, + fontFamily: FontFamily? = null, + letterSpacing: TextUnit = TextUnit.Unspecified, + textDecoration: TextDecoration? = null, + textAlign: TextAlign? = null, + lineHeight: TextUnit = TextUnit.Unspecified, + overflow: TextOverflow = TextOverflow.Clip, + softWrap: Boolean = true, + maxLines: Int = Int.MAX_VALUE, + minLines: Int = 1, + onTextLayout: (TextLayoutResult) -> Unit = {}, +) { + Text( + text = text, + modifier = modifier, + color = color, + fontSize = fontSize, + fontStyle = fontStyle, + fontWeight = fontWeight, + fontFamily = fontFamily, + letterSpacing = letterSpacing, + textDecoration = textDecoration, + textAlign = textAlign, + lineHeight = lineHeight, + overflow = overflow, + softWrap = softWrap, + maxLines = maxLines, + minLines = minLines, + onTextLayout = onTextLayout, + style = MoSoTheme.typography.titleMedium, + ) +} + +@Composable +fun LargeTextTitle( + text: String, + modifier: Modifier = Modifier, + color: Color = Color.Unspecified, + fontSize: TextUnit = TextUnit.Unspecified, + fontStyle: FontStyle? = null, + fontWeight: FontWeight? = null, + fontFamily: FontFamily? = null, + letterSpacing: TextUnit = TextUnit.Unspecified, + textDecoration: TextDecoration? = null, + textAlign: TextAlign? = null, + lineHeight: TextUnit = TextUnit.Unspecified, + overflow: TextOverflow = TextOverflow.Clip, + softWrap: Boolean = true, + maxLines: Int = Int.MAX_VALUE, + minLines: Int = 1, + onTextLayout: (TextLayoutResult) -> Unit = {}, +) { + Text( + text = text, + modifier = modifier, + color = color, + fontSize = fontSize, + fontStyle = fontStyle, + fontWeight = fontWeight, + fontFamily = fontFamily, + letterSpacing = letterSpacing, + textDecoration = textDecoration, + textAlign = textAlign, + lineHeight = lineHeight, + overflow = overflow, + softWrap = softWrap, + maxLines = maxLines, + minLines = minLines, + onTextLayout = onTextLayout, + style = MoSoTheme.typography.titleLarge, + ) +} \ No newline at end of file diff --git a/core/network/mastodon/src/main/java/org/mozilla/social/core/network/mastodon/BlocksApi.kt b/core/network/mastodon/src/main/java/org/mozilla/social/core/network/mastodon/BlocksApi.kt new file mode 100644 index 000000000..e5c0ca3b5 --- /dev/null +++ b/core/network/mastodon/src/main/java/org/mozilla/social/core/network/mastodon/BlocksApi.kt @@ -0,0 +1,10 @@ +package org.mozilla.social.core.network.mastodon + +import org.mozilla.social.core.network.mastodon.model.NetworkAccount +import retrofit2.http.GET + +interface BlocksApi { + @GET("/api/v1/blocks") + suspend fun getBlocks(): List +} + diff --git a/core/network/mastodon/src/main/java/org/mozilla/social/core/network/mastodon/MastodonNetworkModule.kt b/core/network/mastodon/src/main/java/org/mozilla/social/core/network/mastodon/MastodonNetworkModule.kt index 701d2a8fe..ff0667292 100644 --- a/core/network/mastodon/src/main/java/org/mozilla/social/core/network/mastodon/MastodonNetworkModule.kt +++ b/core/network/mastodon/src/main/java/org/mozilla/social/core/network/mastodon/MastodonNetworkModule.kt @@ -21,7 +21,7 @@ fun mastodonNetworkModule(isDebug: Boolean) = module { .connectTimeout(OKHTTP_TIMEOUT, TimeUnit.SECONDS) .addNetworkInterceptor(HttpLoggingInterceptor().apply { level = if (isDebug) { - HttpLoggingInterceptor.Level.BASIC + HttpLoggingInterceptor.Level.BODY } else { HttpLoggingInterceptor.Level.NONE } @@ -38,14 +38,16 @@ fun mastodonNetworkModule(isDebug: Boolean) = module { } single { get().create(AccountApi::class.java) } + single { get().create(AppApi::class.java) } + single { get().create(BlocksApi::class.java) } + single { get().create(InstanceApi::class.java) } single { get().create(MediaApi::class.java) } + single { get().create(MutesApi::class.java) } single { get().create(OauthApi::class.java) } + single { get().create(ReportApi::class.java) } single { get().create(SearchApi::class.java) } single { get().create(StatusApi::class.java) } single { get().create(TimelineApi::class.java) } - single { get().create(InstanceApi::class.java) } - single { get().create(ReportApi::class.java) } - single { get().create(AppApi::class.java) } } private var json: Json = Json { ignoreUnknownKeys = true } diff --git a/core/network/mastodon/src/main/java/org/mozilla/social/core/network/mastodon/MutesApi.kt b/core/network/mastodon/src/main/java/org/mozilla/social/core/network/mastodon/MutesApi.kt new file mode 100644 index 000000000..50fe741e1 --- /dev/null +++ b/core/network/mastodon/src/main/java/org/mozilla/social/core/network/mastodon/MutesApi.kt @@ -0,0 +1,9 @@ +package org.mozilla.social.core.network.mastodon + +import org.mozilla.social.core.network.mastodon.model.NetworkAccount +import retrofit2.http.GET + +interface MutesApi { + @GET("/api/v1/mutes") + suspend fun getMutes(): List +} \ No newline at end of file diff --git a/core/repository/mastodon/src/main/java/org/mozilla/social/core/repository/mastodon/BlocksRepository.kt b/core/repository/mastodon/src/main/java/org/mozilla/social/core/repository/mastodon/BlocksRepository.kt new file mode 100644 index 000000000..0684c0ad4 --- /dev/null +++ b/core/repository/mastodon/src/main/java/org/mozilla/social/core/repository/mastodon/BlocksRepository.kt @@ -0,0 +1,8 @@ +package org.mozilla.social.core.repository.mastodon + +import org.mozilla.social.core.network.mastodon.BlocksApi +import org.mozilla.social.core.repository.mastodon.model.status.toExternalModel + +class BlocksRepository(private val api: BlocksApi) { + suspend fun getBlocks() = api.getBlocks().map { it.toExternalModel() } +} \ No newline at end of file diff --git a/core/repository/mastodon/src/main/java/org/mozilla/social/core/repository/mastodon/MastodonRepositoryModule.kt b/core/repository/mastodon/src/main/java/org/mozilla/social/core/repository/mastodon/MastodonRepositoryModule.kt index a2cd226ff..c812bbaaf 100644 --- a/core/repository/mastodon/src/main/java/org/mozilla/social/core/repository/mastodon/MastodonRepositoryModule.kt +++ b/core/repository/mastodon/src/main/java/org/mozilla/social/core/repository/mastodon/MastodonRepositoryModule.kt @@ -20,5 +20,7 @@ fun mastodonRepositoryModule(isDebug: Boolean) = module { singleOf(::FollowersRepository) singleOf(::FollowingsRepository) singleOf(::RelationshipRepository) + singleOf(::MutesRepository) + singleOf(::BlocksRepository) includes(mastodonNetworkModule(isDebug)) } \ No newline at end of file diff --git a/core/repository/mastodon/src/main/java/org/mozilla/social/core/repository/mastodon/MutesRepository.kt b/core/repository/mastodon/src/main/java/org/mozilla/social/core/repository/mastodon/MutesRepository.kt new file mode 100644 index 000000000..7aeb68409 --- /dev/null +++ b/core/repository/mastodon/src/main/java/org/mozilla/social/core/repository/mastodon/MutesRepository.kt @@ -0,0 +1,8 @@ +package org.mozilla.social.core.repository.mastodon + +import org.mozilla.social.core.network.mastodon.MutesApi +import org.mozilla.social.core.repository.mastodon.model.status.toExternalModel + +class MutesRepository(private val api: MutesApi) { + suspend fun getMutes() = api.getMutes().map { it.toExternalModel() } +} \ No newline at end of file diff --git a/core/ui/common/src/main/java/org/mozilla/social/core/ui/common/account/quickview/AccountQuickView.kt b/core/ui/common/src/main/java/org/mozilla/social/core/ui/common/account/quickview/AccountQuickView.kt index c7219407d..3bd898ea2 100644 --- a/core/ui/common/src/main/java/org/mozilla/social/core/ui/common/account/quickview/AccountQuickView.kt +++ b/core/ui/common/src/main/java/org/mozilla/social/core/ui/common/account/quickview/AccountQuickView.kt @@ -1,72 +1,60 @@ package org.mozilla.social.core.ui.common.account.quickview import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width -import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import coil.compose.AsyncImage +import org.mozilla.social.core.designsystem.component.LargeTextBody +import org.mozilla.social.core.designsystem.component.MediumTextBody +import org.mozilla.social.core.designsystem.theme.MoSoSpacing import org.mozilla.social.core.designsystem.theme.MoSoTheme -import org.mozilla.social.core.designsystem.theme.defaultTypography import org.mozilla.social.core.ui.common.utils.PreviewTheme @Composable fun AccountQuickView( uiState: AccountQuickViewUiState, - onClick: (accountId: String,) -> Unit, + modifier: Modifier = Modifier, + buttonSlot: @Composable () -> Unit = {}, ) { - Column( - modifier = Modifier - .fillMaxWidth() - .clickable { onClick(uiState.accountId) } - ) { - Row( - horizontalArrangement = Arrangement.SpaceBetween, - modifier = Modifier - .fillMaxWidth() - .padding(8.dp) - ) { - Row { - AsyncImage( - modifier = Modifier - .height(50.dp) - .width(50.dp) - .clip(CircleShape) - .background(MoSoTheme.colors.layer2), - model = uiState.avatarUrl, - contentDescription = null, - ) - Column( - modifier = Modifier - .padding(start = 16.dp) - ) { - Text( - text = uiState.displayName, - style = defaultTypography.bodyLarge - ) - Text( - text = "@${uiState.webFinger}", - style = defaultTypography.bodyMedium - ) - } - } + Row(modifier = modifier) { + CircleAvatar(uiState.avatarUrl) + + Spacer(modifier = Modifier.width(MoSoSpacing.sm)) + + Column { + LargeTextBody(text = uiState.displayName) + MediumTextBody(text = "@${uiState.webFinger}") } + + Spacer(modifier = Modifier.weight(1f)) + + buttonSlot() } } +val CIRCLE_AVATAR_SIZE = 50.dp + +@Composable +private fun CircleAvatar(avatarUrl: String, modifier: Modifier = Modifier) { + AsyncImage( + modifier = modifier + .size(CIRCLE_AVATAR_SIZE) + .clip(CircleShape) + .background(MoSoTheme.colors.layer2), + model = avatarUrl, + contentDescription = null, + ) +} + @Preview @Composable private fun AccountQuickViewPreview() { @@ -79,7 +67,6 @@ private fun AccountQuickViewPreview() { avatarUrl = "url", isFollowing = false, ), - onClick = {} ) } } \ No newline at end of file diff --git a/feature/followers/src/main/java/org/mozilla/social/feature/followers/FollowersScreen.kt b/feature/followers/src/main/java/org/mozilla/social/feature/followers/FollowersScreen.kt index 1d019cec4..4079f2896 100644 --- a/feature/followers/src/main/java/org/mozilla/social/feature/followers/FollowersScreen.kt +++ b/feature/followers/src/main/java/org/mozilla/social/feature/followers/FollowersScreen.kt @@ -1,6 +1,7 @@ package org.mozilla.social.feature.followers import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize @@ -104,6 +105,7 @@ private fun FollowersScreen( text = when (tabType) { FollowType.FOLLOWERS -> stringResource(id = R.string.followers) + FollowType.FOLLOWING -> stringResource(id = R.string.following) }, @@ -155,7 +157,10 @@ private fun FollowersList( lazyPagingItems[index]?.let { uiState -> AccountQuickView( uiState = uiState, - onClick = followersInteractions::onAccountClicked + modifier = Modifier.clickable { + followersInteractions + .onAccountClicked(accountId = uiState.accountId) + } ) } } diff --git a/feature/settings/src/main/java/org/mozilla/social/feature/settings/SettingsModule.kt b/feature/settings/src/main/java/org/mozilla/social/feature/settings/SettingsModule.kt index 1a30405f3..82c980afc 100644 --- a/feature/settings/src/main/java/org/mozilla/social/feature/settings/SettingsModule.kt +++ b/feature/settings/src/main/java/org/mozilla/social/feature/settings/SettingsModule.kt @@ -1,10 +1,13 @@ package org.mozilla.social.feature.settings import org.koin.androidx.viewmodel.dsl.viewModel +import org.koin.androidx.viewmodel.dsl.viewModelOf import org.koin.dsl.module import org.mozilla.social.feature.settings.about.AboutSettingsViewModel import org.mozilla.social.feature.settings.account.AccountSettingsViewModel import org.mozilla.social.feature.settings.contentpreferences.ContentPreferencesSettingsViewModel +import org.mozilla.social.feature.settings.contentpreferences.blockedusers.BlockedUsersViewModel +import org.mozilla.social.feature.settings.contentpreferences.mutedusers.MutedUsersSettingsViewModel import org.mozilla.social.feature.settings.privacy.PrivacySettingsViewModel val settingsModule = module { @@ -13,4 +16,6 @@ val settingsModule = module { viewModel { PrivacySettingsViewModel(get()) } viewModel { AboutSettingsViewModel(get()) } viewModel { ContentPreferencesSettingsViewModel(get()) } + viewModelOf(::BlockedUsersViewModel) + viewModelOf(::MutedUsersSettingsViewModel) } \ No newline at end of file diff --git a/feature/settings/src/main/java/org/mozilla/social/feature/settings/about/AboutSettingsScreen.kt b/feature/settings/src/main/java/org/mozilla/social/feature/settings/about/AboutSettingsScreen.kt index 5c5f833e7..fef819110 100644 --- a/feature/settings/src/main/java/org/mozilla/social/feature/settings/about/AboutSettingsScreen.kt +++ b/feature/settings/src/main/java/org/mozilla/social/feature/settings/about/AboutSettingsScreen.kt @@ -75,7 +75,7 @@ fun AboutSettingsScreen(aboutSettings: AboutSettings) { text = stringResource(id = R.string.administered_by), style = MoSoTheme.typography.titleSmall, ) - AccountQuickView(uiState = state, onClick = {}) + AccountQuickView(uiState = state) } Divider() diff --git a/feature/settings/src/main/java/org/mozilla/social/feature/settings/contentpreferences/blockedusers/BlockedUsersSettingsScreen.kt b/feature/settings/src/main/java/org/mozilla/social/feature/settings/contentpreferences/blockedusers/BlockedUsersSettingsScreen.kt index 9886c6edf..7f61ab540 100644 --- a/feature/settings/src/main/java/org/mozilla/social/feature/settings/contentpreferences/blockedusers/BlockedUsersSettingsScreen.kt +++ b/feature/settings/src/main/java/org/mozilla/social/feature/settings/contentpreferences/blockedusers/BlockedUsersSettingsScreen.kt @@ -1,16 +1,65 @@ package org.mozilla.social.feature.settings.contentpreferences.blockedusers +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import org.koin.androidx.compose.koinViewModel +import org.mozilla.social.core.designsystem.component.MoSoButtonSecondary import org.mozilla.social.core.designsystem.component.MoSoSurface +import org.mozilla.social.core.designsystem.component.SmallTextLabel +import org.mozilla.social.core.ui.common.account.quickview.AccountQuickView +import org.mozilla.social.core.ui.common.account.quickview.AccountQuickViewUiState import org.mozilla.social.feature.settings.R import org.mozilla.social.feature.settings.ui.SettingsColumn @Composable -fun BlockedUsersSettingsScreen() { +fun BlockedUsersSettingsScreen(viewModel: BlockedUsersViewModel = koinViewModel()) { + val blocks by viewModel.blocks.collectAsStateWithLifecycle(initialValue = listOf()) + BlockedUsersSettingsScreen(blocks, viewModel::onBlockButtonClicked) +} + +@Composable +fun BlockedUsersSettingsScreen( + blocks: List, + onClick: (accountId: String) -> Unit, +) { MoSoSurface { SettingsColumn(title = stringResource(id = R.string.blocked_users_title)) { - + for (block in blocks) { + BlockedUserRow(block = block, onClick = onClick) + } } } -} \ No newline at end of file +} + +@Composable +fun BlockedUserRow( + block: Block, + onClick: (accountId: String) -> Unit, +) { + Row( + Modifier + .fillMaxWidth() + .wrapContentHeight() + ) { + AccountQuickView( + uiState = block.quickViewUiState, + modifier = Modifier.fillMaxWidth(), + buttonSlot = { + MoSoButtonSecondary( + enabled = false, + onClick = { onClick(block.quickViewUiState.accountId) }, + ) { SmallTextLabel(text = stringResource(id = R.string.unblock)) } + }, + ) + } +} + +data class Block( + val quickViewUiState: AccountQuickViewUiState, +) \ No newline at end of file diff --git a/feature/settings/src/main/java/org/mozilla/social/feature/settings/contentpreferences/blockedusers/BlockedUsersViewModel.kt b/feature/settings/src/main/java/org/mozilla/social/feature/settings/contentpreferences/blockedusers/BlockedUsersViewModel.kt new file mode 100644 index 000000000..5022df5fd --- /dev/null +++ b/feature/settings/src/main/java/org/mozilla/social/feature/settings/contentpreferences/blockedusers/BlockedUsersViewModel.kt @@ -0,0 +1,33 @@ +package org.mozilla.social.feature.settings.contentpreferences.blockedusers + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.launch +import org.mozilla.social.core.model.Account +import org.mozilla.social.core.repository.mastodon.AccountRepository +import org.mozilla.social.core.repository.mastodon.BlocksRepository +import org.mozilla.social.core.ui.common.account.quickview.toQuickViewUiState + +class BlockedUsersViewModel( + private val repository: BlocksRepository, + private val accountRepository: AccountRepository, + private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO, +) : ViewModel() { + + val blocks: Flow> = flow { + emit(repository.getBlocks().map { it.toBlock() }) + } + + // TODO@DA error handling and unblock + fun onBlockButtonClicked(accountId: String) { + viewModelScope.launch(ioDispatcher) { accountRepository.muteAccount(accountId) } + } +} + +private fun Account.toBlock(): Block { + return Block(toQuickViewUiState()) +} diff --git a/feature/settings/src/main/java/org/mozilla/social/feature/settings/contentpreferences/mutedusers/MutedUsersSettingsViewModel.kt b/feature/settings/src/main/java/org/mozilla/social/feature/settings/contentpreferences/mutedusers/MutedUsersSettingsViewModel.kt new file mode 100644 index 000000000..58ed5fada --- /dev/null +++ b/feature/settings/src/main/java/org/mozilla/social/feature/settings/contentpreferences/mutedusers/MutedUsersSettingsViewModel.kt @@ -0,0 +1,31 @@ +package org.mozilla.social.feature.settings.contentpreferences.mutedusers + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.launch +import org.mozilla.social.core.model.Account +import org.mozilla.social.core.repository.mastodon.AccountRepository +import org.mozilla.social.core.repository.mastodon.MutesRepository + +class MutedUsersSettingsViewModel( + private val repository: MutesRepository, + private val accountRepository: AccountRepository, + private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO, +) : ViewModel() { + + val mutes: Flow> = flow> { + emit(repository.getMutes()) + } + + // TODO@DA hook up + fun onMuteButtonClicked( + accountId: String, + ) { + viewModelScope.launch(ioDispatcher) { accountRepository.muteAccount(accountId) } + } +} + diff --git a/feature/settings/src/main/res/values/strings.xml b/feature/settings/src/main/res/values/strings.xml index 4a59fa731..743dfae93 100644 --- a/feature/settings/src/main/res/values/strings.xml +++ b/feature/settings/src/main/res/values/strings.xml @@ -20,4 +20,5 @@ Allow technical, interaction, campaign and referral data to be sent to Mozilla. With this data you will have a personalized experience. Sign Out + Unblock \ No newline at end of file