Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make the description of achivements operable by Firebase Remote Config #987

Merged
merged 15 commits into from
Aug 30, 2023
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ class EntryPointTest {
override suspend fun getBoolean(key: String): Boolean {
return true
}

override suspend fun getString(key: String): String {
return "default"
}
},
authenticator = object : Authenticator {
override suspend fun currentUser(): User? {
Expand All @@ -50,4 +54,4 @@ class EntryPointTest {
assertNotNull(kmpEntryPoint.get<SponsorsRepository>())
assertNotNull(kmpEntryPoint.get<StaffRepository>())
}
}
}
3 changes: 3 additions & 0 deletions app-ios/Modules/Sources/RemoteConfig/RemoteConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,7 @@ public class RemoteConfigApiImpl: RemoteConfigApi {
bool: RemoteConfig.remoteConfig().configValue(forKey: key).boolValue
)
}
public func getString(key: String) async throws -> String {
return RemoteConfig.remoteConfig().configValue(forKey: key).stringValue ?? ""
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,10 @@ class AuthenticatorModule {
@InstallIn(SingletonComponent::class)
@Module
class RemoteConfigModule {

@Provides
@Singleton
fun provideFirebaseRemoteConfig(): RemoteConfigApi {
fun provideRemoteConfigApi(): RemoteConfigApi {
return DefaultRemoteConfigApi()
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.github.droidkaigi.confsched2023.data.remoteconfig

import co.touchlab.kermit.Logger
import dev.gitlive.firebase.Firebase
import dev.gitlive.firebase.remoteconfig.get
import dev.gitlive.firebase.remoteconfig.remoteConfig
Expand All @@ -14,13 +15,26 @@ class DefaultRemoteConfigApi : RemoteConfigApi {
// init {
// CoroutineScope(Dispatchers.IO).launch {
// firebaseRemoteConfig.settings {
// minimumFetchIntervalInSeconds = 12 * 3600L
// minimumFetchIntervalInSeconds = 1 * 60
// }
// }
// }

override suspend fun getBoolean(key: String): Boolean {
firebaseRemoteConfig.fetchAndActivate()
fetchConfig()
return firebaseRemoteConfig[key]
}

override suspend fun getString(key: String): String {
fetchConfig()
return firebaseRemoteConfig[key]
}

private suspend fun fetchConfig() {
try {
firebaseRemoteConfig.fetchAndActivate()
} catch (e: Exception) {
Logger.e(e.message ?: "FirebaseRemoteConfig fetchAndActivate failed")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,27 @@ class DefaultStampRepository(
private val remoteConfigApi: RemoteConfigApi,
) : StampRepository {
private val isStampsEnabledStateFlow: MutableStateFlow<Boolean> = MutableStateFlow(false)
private val stampDetailDescriptionStateFlow: MutableStateFlow<String> = MutableStateFlow("")

private suspend fun fetchStampsEnabled() {
isStampsEnabledStateFlow.value = remoteConfigApi.getBoolean(IS_STAMPS_ENABLED_KEY)
}

private suspend fun fetchStampDetailDescription() {
stampDetailDescriptionStateFlow.value =
remoteConfigApi.getString(STAMP_DETAIL_DESCRIPTION_KEY)
}

override fun getStampEnabledStream(): Flow<Boolean> {
return isStampsEnabledStateFlow.onStart { fetchStampsEnabled() }
}

override fun getStampDetailDescriptionStream(): Flow<String> {
return stampDetailDescriptionStateFlow.onStart { fetchStampDetailDescription() }
}

companion object {
const val IS_STAMPS_ENABLED_KEY = "is_stamps_enable"
const val STAMP_DETAIL_DESCRIPTION_KEY = "achievements_detail_description"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,24 @@ import androidx.datastore.core.IOException
class FakeRemoteConfigApi : RemoteConfigApi {

sealed interface Status : RemoteConfigApi {
object Default : Status {
data object Default : Status {
override suspend fun getBoolean(key: String): Boolean {
return true
}

override suspend fun getString(key: String): String {
return "default"
}
}

object ThrowException : Status {
data object ThrowException : Status {
override suspend fun getBoolean(key: String): Boolean {
throw IOException("FakeRemoteConfigApi throws exception")
}

override suspend fun getString(key: String): String {
throw IOException("FakeRemoteConfigApi throws exception")
}
}
}

Expand All @@ -27,4 +35,8 @@ class FakeRemoteConfigApi : RemoteConfigApi {
override suspend fun getBoolean(key: String): Boolean {
return status.getBoolean(key)
}

override suspend fun getString(key: String): String {
return status.getString(key)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ package io.github.droidkaigi.confsched2023.data.remoteconfig

interface RemoteConfigApi {
suspend fun getBoolean(key: String): Boolean
suspend fun getString(key: String): String
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ import kotlinx.coroutines.flow.Flow
interface StampRepository {

fun getStampEnabledStream(): Flow<Boolean>
fun getStampDetailDescriptionStream(): Flow<String>
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.composable
import io.github.droidkaigi.confsched2023.model.Stamp
import io.github.droidkaigi.confsched2023.stamps.section.StampList
import io.github.droidkaigi.confsched2023.stamps.section.StampListUiState
import io.github.droidkaigi.confsched2023.ui.SnackbarMessageEffect
import kotlinx.collections.immutable.ImmutableList

const val stampsScreenRoute = "stamps"
fun NavGraphBuilder.nestedStampsScreen(
Expand Down Expand Up @@ -69,7 +69,7 @@ fun StampsScreen(

data class StampsScreenUiState(
val lottieRawRes: Int?,
val stamps: ImmutableList<Stamp>,
val stampListUiState: StampListUiState,
)

@Composable
Expand All @@ -88,7 +88,7 @@ private fun StampsScreen(
val layoutDirection = LocalLayoutDirection.current

StampList(
stamps = uiState.stamps,
uiState = uiState.stampListUiState,
onStampsClick = onStampsClick,
onReachAnimationEnd = onReachAnimationEnd,
stampLottieRawId = stampLottieRawId,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,49 @@
package io.github.droidkaigi.confsched2023.stamps

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import io.github.droidkaigi.confsched2023.data.contributors.StampRepository
import io.github.droidkaigi.confsched2023.designsystem.strings.AppStrings
import io.github.droidkaigi.confsched2023.feature.stamps.R
import io.github.droidkaigi.confsched2023.model.Stamp
import io.github.droidkaigi.confsched2023.stamps.section.StampListUiState
import io.github.droidkaigi.confsched2023.ui.UserMessageStateHolder
import io.github.droidkaigi.confsched2023.ui.buildUiState
import io.github.droidkaigi.confsched2023.ui.handleErrorAndRetry
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.stateIn
import javax.inject.Inject

@HiltViewModel
class StampsScreenViewModel @Inject constructor(
val userMessageStateHolder: UserMessageStateHolder,
stampRepository: StampRepository,
) : ViewModel(),
UserMessageStateHolder by userMessageStateHolder {

private val stampDetailDescriptionStateFlow: StateFlow<String> =
stampRepository.getStampDetailDescriptionStream()
.handleErrorAndRetry(
AppStrings.Retry,
userMessageStateHolder,
)
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = "",
)

private val stampLottieRawResStateFlow: MutableStateFlow<Int?> =
MutableStateFlow(null)

val uiState = buildUiState(
stampLottieRawResStateFlow,
) { rawRes ->
StampsScreenUiState(
lottieRawRes = rawRes,
private val stampListState = buildUiState(
stampDetailDescriptionStateFlow,
) { detailDescription ->
StampListUiState(
stamps = persistentListOf(
Stamp(
hasDrawableResId = R.drawable.img_stamp_a_on,
Expand Down Expand Up @@ -56,6 +76,17 @@ class StampsScreenViewModel @Inject constructor(
contentDescription = "StampE image",
),
),
detailDescription = detailDescription,
)
}

val uiState = buildUiState(
stampLottieRawResStateFlow,
stampListState,
) { rawRes, stampListUiState ->
StampsScreenUiState(
lottieRawRes = rawRes,
stampListUiState = stampListUiState,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,18 @@ sealed class StampsStrings : Strings<StampsStrings>(Bindings) {

data object Title : StampsStrings()

data object Description : StampsStrings()

data object DescriptionNotes : StampsStrings()

private object Bindings : StringsBindings<StampsStrings>(
Lang.Japanese to { item, _ ->
when (item) {
Title -> "Stamps"
Description -> "会場の各部屋に設置されたNFCタグにスマホをかざしてスタンプを集めてみましょう。"
DescriptionNotes -> "※ この企画は変更または中止になる可能性があります"
}
},
Lang.English to { item, _ ->
when (item) {
Title -> "Stamps"
Description -> "Let's collect stamps by holding your smartphone over the NFC tags placed in each room of the venue."
DescriptionNotes -> "※ This program is subject to change or cancellation."
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import io.github.droidkaigi.confsched2023.stamps.StampsStrings

@Composable
fun StampsDetail(
description: String,
modifier: Modifier = Modifier,
) {
Column(modifier = modifier) {
Expand All @@ -25,7 +26,7 @@ fun StampsDetail(
Spacer(modifier = Modifier.height(16.dp))

Text(
text = StampsStrings.Description.asString(),
text = description,
style = MaterialTheme.typography.bodyLarge,
modifier = Modifier.fillMaxWidth(),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,14 @@ private const val StampListColumns = 2
private const val SingleItemSpanCount = 2
private const val DoubleItemSpanCount = 2 / 2

data class StampListUiState(
val stamps: ImmutableList<Stamp>,
val detailDescription: String,
Copy link
Member

@takahirom takahirom Aug 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hoped it includes stamps: ImmutableList<Stamp> as well. But it is ok

)

@Composable
fun StampList(
stamps: ImmutableList<Stamp>,
uiState: StampListUiState,
@androidx.annotation.RawRes
stampLottieRawId: Int?,
onStampsClick: (Stamp) -> Unit,
Expand Down Expand Up @@ -86,14 +91,14 @@ fun StampList(
key = "stamps_header",
span = { GridItemSpan(SingleItemSpanCount) },
) {
StampsDetail()
StampsDetail(uiState.detailDescription)
}
items(
items = stamps,
items = uiState.stamps,
key = { stamp -> stamp.hasDrawableResId },
span = { stamp ->
GridItemSpan(
if (stamp == stamps.last() && stamps.size % StampListColumns != 0) {
if (stamp == uiState.stamps.last() && uiState.stamps.size % StampListColumns != 0) {
SingleItemSpanCount
} else {
DoubleItemSpanCount
Expand Down
Loading