Skip to content

Commit

Permalink
Merge pull request #20846 from wordpress-mobile/issue/20621-tags-feed…
Browse files Browse the repository at this point in the history
…-onboarding

[Reader] Implement announcement card
  • Loading branch information
RenanLukas committed May 21, 2024
2 parents 5bbddf0 + 681ab48 commit 5dde4db
Show file tree
Hide file tree
Showing 14 changed files with 419 additions and 14 deletions.
1 change: 1 addition & 0 deletions WordPress/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ android {
buildConfigField "boolean", "READER_READING_PREFERENCES", "false"
buildConfigField "boolean", "READER_READING_PREFERENCES_FEEDBACK", "false"
buildConfigField "boolean", "READER_TAGS_FEED", "false"
buildConfigField "boolean", "READER_ANNOUNCEMENT_CARD", "false"

// Override these constants in jetpack product flavor to enable/ disable features
buildConfigField "boolean", "ENABLE_SITE_CREATION", "true"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ public enum DeletablePrefKey implements PrefKey {
SHOULD_HIDE_DYNAMIC_CARD,
PINNED_SITE_IDS,
READER_READING_PREFERENCES_JSON,
SHOULD_SHOW_READER_ANNOUNCEMENT_CARD,
}

/**
Expand Down Expand Up @@ -1780,6 +1781,14 @@ public static void setPinnedSiteLocalIds(@NonNull final String ids) {
setString(DeletablePrefKey.PINNED_SITE_IDS, ids);
}

public static boolean getShouldShowReaderAnnouncementCard() {
return prefs().getBoolean(DeletablePrefKey.SHOULD_SHOW_READER_ANNOUNCEMENT_CARD.name(), true);
}

public static void setShouldShowReaderAnnouncementCard(final boolean shouldShow) {
prefs().edit().putBoolean(DeletablePrefKey.SHOULD_SHOW_READER_ANNOUNCEMENT_CARD.name(), shouldShow).apply();
}

@Nullable
public static String getReaderReadingPreferencesJson() {
return getString(DeletablePrefKey.READER_READING_PREFERENCES_JSON, null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,11 @@ class AppPrefsWrapper @Inject constructor() {

fun setBookmarkPostsPseudoIdsUpdated() = AppPrefs.setBookmarkPostsPseudoIdsUpdated()

fun shouldShowReaderAnnouncementCard(): Boolean = AppPrefs.getShouldShowReaderAnnouncementCard()

fun setShouldShowReaderAnnouncementCard(shouldShow: Boolean) =
AppPrefs.setShouldShowReaderAnnouncementCard(shouldShow)

fun getAllPrefs(): Map<String, Any?> = AppPrefs.getAllPrefs()

fun setString(prefKey: PrefKey, value: String) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import org.wordpress.android.ui.reader.subfilter.SubfilterCategory
import org.wordpress.android.ui.reader.subfilter.SubfilterListItem
import org.wordpress.android.ui.reader.viewmodels.ReaderViewModel
import org.wordpress.android.ui.reader.viewmodels.ReaderViewModel.ReaderUiState.ContentUiState
import org.wordpress.android.ui.reader.views.compose.ReaderAnnouncementCard
import org.wordpress.android.ui.reader.views.compose.ReaderTopAppBar
import org.wordpress.android.ui.reader.views.compose.filter.ReaderFilterType
import org.wordpress.android.ui.utils.UiHelpers
Expand Down Expand Up @@ -180,6 +181,7 @@ class ReaderFragment : Fragment(R.layout.reader_fragment_layout), ScrollableView
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
binding = ReaderFragmentLayoutBinding.bind(view).apply {
initTopAppBar()
initAnnouncementCard()
initViewModel(savedInstanceState)
}
}
Expand Down Expand Up @@ -262,6 +264,23 @@ class ReaderFragment : Fragment(R.layout.reader_fragment_layout), ScrollableView
}
}

private fun ReaderFragmentLayoutBinding.initAnnouncementCard() {
readerAnnouncementCardComposeView.apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
val announcementCardUiState by viewModel.announcementCardState.observeAsState()
val state = announcementCardUiState ?: return@setContent
AppTheme {
ReaderAnnouncementCard(
shouldShow = state.shouldShow,
items = state.items,
onAnnouncementCardDoneClick = { viewModel.onAnnouncementCardDoneClick() }
)
}
}
}
}

private fun ReaderFragmentLayoutBinding.initViewModel(savedInstanceState: Bundle?) {
viewModel = ViewModelProvider(this@ReaderFragment, viewModelFactory)[ReaderViewModel::class.java]
startReaderViewModel(savedInstanceState)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode.MAIN
import org.wordpress.android.BuildConfig
import org.wordpress.android.R
import org.wordpress.android.analytics.AnalyticsTracker
import org.wordpress.android.fluxc.store.AccountStore
import org.wordpress.android.fluxc.store.QuickStartStore
import org.wordpress.android.fluxc.store.QuickStartStore.QuickStartTask
Expand All @@ -43,6 +44,7 @@ import org.wordpress.android.ui.reader.utils.DateProvider
import org.wordpress.android.ui.reader.utils.ReaderTopBarMenuHelper
import org.wordpress.android.ui.reader.viewmodels.ReaderViewModel.ReaderUiState.ContentUiState
import org.wordpress.android.ui.reader.viewmodels.ReaderViewModel.ReaderUiState.ContentUiState.TabUiState
import org.wordpress.android.ui.reader.views.compose.ReaderAnnouncementCardItemData
import org.wordpress.android.ui.reader.views.compose.filter.ReaderFilterSelectedItem
import org.wordpress.android.ui.reader.views.compose.filter.ReaderFilterType
import org.wordpress.android.ui.utils.UiString
Expand All @@ -51,6 +53,7 @@ import org.wordpress.android.util.JetpackBrandingUtils
import org.wordpress.android.util.QuickStartUtils
import org.wordpress.android.util.SnackbarSequencer
import org.wordpress.android.util.UrlUtilsWrapper
import org.wordpress.android.util.config.ReaderAnnouncementCardFeatureConfig
import org.wordpress.android.util.config.ReaderTagsFeedFeatureConfig
import org.wordpress.android.util.distinct
import org.wordpress.android.viewmodel.Event
Expand Down Expand Up @@ -81,6 +84,7 @@ class ReaderViewModel @Inject constructor(
private val urlUtilsWrapper: UrlUtilsWrapper,
private val readerTagsFeedFeatureConfig: ReaderTagsFeedFeatureConfig,
// todo: annnmarie removed this private val getFollowedTagsUseCase: GetFollowedTagsUseCase
private val readerAnnouncementCardFeatureConfig: ReaderAnnouncementCardFeatureConfig,
) : ScopedViewModel(mainDispatcher) {
private var initialized: Boolean = false
private var wasPaused: Boolean = false
Expand All @@ -93,6 +97,9 @@ class ReaderViewModel @Inject constructor(
private val _topBarUiState = MutableLiveData<TopBarUiState>()
val topBarUiState: LiveData<TopBarUiState> = _topBarUiState.distinct()

private val _announcementCardState = MutableLiveData<AnnouncementCardUiState>()
val announcementCardState: LiveData<AnnouncementCardUiState> = _announcementCardState

private val _updateTags = MutableLiveData<Event<Unit>>()
val updateTags: LiveData<Event<Unit>> = _updateTags

Expand Down Expand Up @@ -125,6 +132,7 @@ class ReaderViewModel @Inject constructor(
if (initialized) return
loadTabs(savedInstanceState)
if (jetpackBrandingUtils.shouldShowJetpackPoweredBottomSheet()) showJetpackPoweredBottomSheet()
updateAnnouncementCard()
}

fun onSaveInstanceState(out: Bundle) {
Expand All @@ -138,6 +146,40 @@ class ReaderViewModel @Inject constructor(
// _showJetpackPoweredBottomSheet.value = Event(true)
}

private fun updateAnnouncementCard() {
val items = mutableListOf<ReaderAnnouncementCardItemData>()

if (readerTagsFeedFeatureConfig.isEnabled()) {
items.add(
ReaderAnnouncementCardItemData(
iconRes = R.drawable.ic_reader_tag,
titleRes = R.string.reader_announcement_card_tags_stream_title,
descriptionRes = R.string.reader_announcement_card_tags_stream_description,
)
)
}

items.add(
ReaderAnnouncementCardItemData(
iconRes = R.drawable.ic_reader_preferences,
titleRes = R.string.reader_announcement_card_reading_preferences_title,
descriptionRes = R.string.reader_announcement_card_reading_preferences_description,
)
)

_announcementCardState.value = AnnouncementCardUiState(
shouldShow = readerAnnouncementCardFeatureConfig.isEnabled() &&
appPrefsWrapper.shouldShowReaderAnnouncementCard(),
items = items,
)
}

fun onAnnouncementCardDoneClick() {
readerTracker.track(AnalyticsTracker.Stat.READER_ANNOUNCEMENT_CARD_DISMISSED)
appPrefsWrapper.setShouldShowReaderAnnouncementCard(false)
updateAnnouncementCard()
}

@JvmOverloads
fun loadTabs(savedInstanceState: Bundle? = null) {
launch {
Expand Down Expand Up @@ -563,6 +605,11 @@ class ReaderViewModel @Inject constructor(
val duration: Int = QUICK_START_PROMPT_DURATION
)

data class AnnouncementCardUiState(
val shouldShow: Boolean,
val items: List<ReaderAnnouncementCardItemData>,
)

companion object {
private const val QUICK_START_PROMPT_DURATION = 5000
private const val FILTER_UPDATE_DELAY = 50L
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
package org.wordpress.android.ui.reader.views.compose

import android.content.res.Configuration
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.expandIn
import androidx.compose.animation.shrinkOut
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.wordpress.android.R
import org.wordpress.android.designsystem.footnote
import org.wordpress.android.ui.compose.theme.AppColor
import org.wordpress.android.ui.compose.theme.AppTheme
import org.wordpress.android.ui.compose.unit.Margin

@Composable
fun ReaderAnnouncementCard(
shouldShow: Boolean,
items: List<ReaderAnnouncementCardItemData>,
onAnnouncementCardDoneClick: () -> Unit,
) {
val primaryColor = if (isSystemInDarkTheme()) AppColor.White else AppColor.Black
val secondaryColor = if (isSystemInDarkTheme()) AppColor.Black else AppColor.White
AnimatedVisibility(
visible = shouldShow,
enter = expandIn(),
exit = shrinkOut(
shrinkTowards = Alignment.TopCenter,
),
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(Margin.ExtraLarge.value),
verticalArrangement = Arrangement.spacedBy(Margin.ExtraLarge.value),
) {
// Title
Text(
text = stringResource(R.string.reader_announcement_card_title),
style = MaterialTheme.typography.labelLarge,
color = primaryColor,
)
// Items
Column(
modifier = Modifier
.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(Margin.ExtraLarge.value)
) {
items.forEach {
ReaderAnnouncementCardItem(it)
}
}
// Done button
Button(
modifier = Modifier
.fillMaxWidth(),
onClick = { onAnnouncementCardDoneClick() },
elevation = ButtonDefaults.elevation(0.dp),
colors = ButtonDefaults.buttonColors(
backgroundColor = primaryColor,
),
) {
Text(
text = stringResource(id = R.string.reader_btn_done),
color = secondaryColor,
style = MaterialTheme.typography.labelLarge,
)
}
}
}
}

@Composable
private fun ReaderAnnouncementCardItem(data: ReaderAnnouncementCardItemData) {
val primaryColor = if (isSystemInDarkTheme()) AppColor.White else AppColor.Black
val secondaryColor = if (isSystemInDarkTheme()) AppColor.Black else AppColor.White
Row(
modifier = Modifier
.fillMaxWidth()
.defaultMinSize(minWidth = 54.dp, minHeight = 54.dp),
verticalAlignment = Alignment.CenterVertically,
) {
val iconBackgroundColor = primaryColor
Icon(
modifier = Modifier
.padding(
start = Margin.Large.value,
end = Margin.Large.value
)
.drawBehind {
drawCircle(
color = iconBackgroundColor,
radius = this.size.maxDimension,
)
},
painter = painterResource(data.iconRes),
tint = secondaryColor,
contentDescription = null
)
Column(verticalArrangement = Arrangement.Center) {
Text(
modifier = Modifier.padding(
start = Margin.Large.value,
),
text = stringResource(data.titleRes),
style = MaterialTheme.typography.labelLarge,
color = primaryColor,
)
val secondaryElementColor = primaryColor.copy(
alpha = 0.6F
)
Text(
modifier = Modifier.padding(
start = Margin.Large.value,
),
text = stringResource(data.descriptionRes),
style = MaterialTheme.typography.footnote,
color = secondaryElementColor,
)
}
}
}

data class ReaderAnnouncementCardItemData(
@DrawableRes val iconRes: Int,
@StringRes val titleRes: Int,
@StringRes val descriptionRes: Int,
)


@Preview
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun ReaderTagsFeedPostListItemPreview() {
AppTheme {
Box(
modifier = Modifier
.fillMaxWidth()
) {
ReaderAnnouncementCard(
shouldShow = false,
items = listOf(
ReaderAnnouncementCardItemData(
iconRes = R.drawable.ic_wifi_off_24px,
titleRes = R.string.reader_tags_display_name,
descriptionRes = R.string.reader_tags_feed_loading_error_description,
),
ReaderAnnouncementCardItemData(
iconRes = R.drawable.ic_wifi_off_24px,
titleRes = R.string.reader_tags_display_name,
descriptionRes = R.string.reader_tags_feed_loading_error_description,
),
ReaderAnnouncementCardItemData(
iconRes = R.drawable.ic_wifi_off_24px,
titleRes = R.string.reader_tags_display_name,
descriptionRes = R.string.reader_tags_feed_loading_error_description,
),
),
onAnnouncementCardDoneClick = {},
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ class TagsAndCategoriesUseCase
}

private fun getIcon(type: String) =
if (type == "tag") R.drawable.ic_tag_white_24dp else R.drawable.ic_folder_white_24dp
if (type == "tag") R.drawable.ic_reader_tag else R.drawable.ic_folder_white_24dp

private fun onLinkClick() {
analyticsTracker.track(AnalyticsTracker.Stat.STATS_TAGS_AND_CATEGORIES_VIEW_MORE_TAPPED)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.wordpress.android.util.config

import org.wordpress.android.BuildConfig
import org.wordpress.android.annotation.Feature
import javax.inject.Inject

private const val READER_ANNOUNCEMENT_CARD_REMOTE_FIELD = "reader_announcement_card"
@Feature(remoteField = READER_ANNOUNCEMENT_CARD_REMOTE_FIELD, defaultValue = false)
class ReaderAnnouncementCardFeatureConfig @Inject constructor(
appConfig: AppConfig
) : FeatureConfig(
appConfig,
BuildConfig.READER_ANNOUNCEMENT_CARD,
READER_ANNOUNCEMENT_CARD_REMOTE_FIELD,
)
Loading

0 comments on commit 5dde4db

Please sign in to comment.