Skip to content

Commit

Permalink
Add force migration screen
Browse files Browse the repository at this point in the history
  • Loading branch information
egorikftp committed Sep 21, 2024
1 parent d1779e0 commit 9dfb374
Show file tree
Hide file tree
Showing 11 changed files with 287 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.arkivanov.essenty.backhandler.BackHandlerOwner
import com.egoriku.grodnoroads.eventreporting.domain.model.ReportParams
import com.egoriku.grodnoroads.extensions.common.StateData
import com.egoriku.grodnoroads.screen.main.MainComponent
import com.egoriku.grodnoroads.screen.root.store.RootStoreFactory.MigrationModel
import com.egoriku.grodnoroads.screen.root.store.headlamp.HeadLampType
import com.egoriku.grodnoroads.setting.alerts.domain.component.AlertsComponent
import com.egoriku.grodnoroads.setting.appearance.domain.component.AppearanceComponent
Expand All @@ -22,6 +23,7 @@ interface RoadsRootComponent : BackHandlerOwner {

val childStack: Value<ChildStack<*, Child>>
val childSlot: Value<ChildSlot<*, Any>>
val migrationModel: Flow<MigrationModel>

fun closeReporting()
fun processReporting(params: ReportParams)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import com.egoriku.grodnoroads.screen.main.buildMainComponent
import com.egoriku.grodnoroads.screen.root.RoadsRootComponent.Child
import com.egoriku.grodnoroads.screen.root.store.RootStore
import com.egoriku.grodnoroads.screen.root.store.RootStoreFactory.Intent
import com.egoriku.grodnoroads.screen.root.store.RootStoreFactory.MigrationModel
import com.egoriku.grodnoroads.screen.root.store.headlamp.HeadLampType
import com.egoriku.grodnoroads.setting.alerts.domain.component.buildAlertsComponent
import com.egoriku.grodnoroads.setting.appearance.domain.component.buildAppearanceComponent
Expand Down Expand Up @@ -63,6 +64,8 @@ class RoadsRootComponentImpl(
}
}

override val migrationModel: Flow<MigrationModel> = rootStore.states.map { it.migrationModel }

override val headlampDialogState: Flow<HeadLampType> = rootStore.states.map { it.headLampType }

override fun closeHeadlampDialog() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,96 +1,230 @@
package com.egoriku.grodnoroads.screen.root

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import android.content.ActivityNotFoundException
import android.content.Intent
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowForward
import androidx.compose.material3.Icon
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.core.net.toUri
import com.arkivanov.decompose.ExperimentalDecomposeApi
import com.arkivanov.decompose.FaultyDecomposeApi
import com.arkivanov.decompose.extensions.compose.jetpack.stack.Children
import com.arkivanov.decompose.extensions.compose.jetpack.stack.animation.*
import com.arkivanov.decompose.extensions.compose.jetpack.stack.animation.predictiveback.predictiveBackAnimation
import com.arkivanov.decompose.extensions.compose.jetpack.subscribeAsState
import com.arkivanov.decompose.router.slot.ChildSlot
import com.egoriku.grodnoroads.R
import com.egoriku.grodnoroads.eventreporting.EventReportingScreen
import com.egoriku.grodnoroads.foundation.uikit.button.PrimaryButton
import com.egoriku.grodnoroads.resources.R.drawable
import com.egoriku.grodnoroads.resources.R.string
import com.egoriku.grodnoroads.screen.main.MainUi
import com.egoriku.grodnoroads.screen.root.RoadsRootComponent.Child
import com.egoriku.grodnoroads.screen.root.store.RootStoreFactory.MigrationModel
import com.egoriku.grodnoroads.screen.root.store.headlamp.HeadLampType
import com.egoriku.grodnoroads.screen.root.ui.HeadLampDialog
import com.egoriku.grodnoroads.setting.alerts.AlertsScreen
import com.egoriku.grodnoroads.setting.appearance.screen.AppearanceScreen
import com.egoriku.grodnoroads.setting.changelog.screen.ChangelogScreen
import com.egoriku.grodnoroads.setting.faq.screen.FaqScreen
import com.egoriku.grodnoroads.setting.map.MapSettingsScreen
import com.egoriku.grodnoroads.setting.screen.ui.foundation.SocialNetwork

@OptIn(FaultyDecomposeApi::class, ExperimentalDecomposeApi::class)
@Composable
fun RootContent(rootComponent: RoadsRootComponent) {
Surface(modifier = Modifier.fillMaxSize()) {
val dialogState by rootComponent.headlampDialogState.collectAsState(initial = HeadLampType.None)

if (dialogState != HeadLampType.None) {
HeadLampDialog(
headlampType = dialogState,
onClose = rootComponent::closeHeadlampDialog
)
}
Box {
Children(
stack = rootComponent.childStack,
animation = predictiveBackAnimation(
backHandler = rootComponent.backHandler,
animation = stackAnimation { _, _, direction ->
if (direction.isFront) {
slide() + fade()
} else {
scale(frontFactor = 1F, backFactor = 0.7F) + fade()
}
},
onBack = rootComponent::onBack,
),

) {
when (val child = it.instance) {
is Child.Main -> MainUi(component = child.component)
is Child.Appearance -> AppearanceScreen(
appearanceComponent = child.appearanceComponent,
onBack = rootComponent::onBack
)
val migrationModel by rootComponent.migrationModel.collectAsState(MigrationModel())

is Child.Map -> MapSettingsScreen(
mapSettingsComponent = child.mapSettingsComponent,
onBack = rootComponent::onBack
)
if (migrationModel.enabled) {
MigrationScreen(migrationModel)
} else {
if (dialogState != HeadLampType.None) {
HeadLampDialog(
headlampType = dialogState,
onClose = rootComponent::closeHeadlampDialog
)
}
Box {
Children(
stack = rootComponent.childStack,
animation = predictiveBackAnimation(
backHandler = rootComponent.backHandler,
animation = stackAnimation { _, _, direction ->
if (direction.isFront) {
slide() + fade()
} else {
scale(frontFactor = 1F, backFactor = 0.7F) + fade()
}
},
onBack = rootComponent::onBack,
),

is Child.Alerts -> AlertsScreen(
alertsComponent = child.alertsComponent,
onBack = rootComponent::onBack
)
) {
when (val child = it.instance) {
is Child.Main -> MainUi(component = child.component)
is Child.Appearance -> AppearanceScreen(
appearanceComponent = child.appearanceComponent,
onBack = rootComponent::onBack
)

is Child.Changelog -> ChangelogScreen(
changelogComponent = child.changelogComponent,
onBack = rootComponent::onBack,
)
is Child.Map -> MapSettingsScreen(
mapSettingsComponent = child.mapSettingsComponent,
onBack = rootComponent::onBack
)

is Child.Alerts -> AlertsScreen(
alertsComponent = child.alertsComponent,
onBack = rootComponent::onBack
)

is Child.NextFeatures -> TODO()
is Child.BetaFeatures -> TODO()
is Child.FAQ -> FaqScreen(
faqComponent = child.faqComponent,
onBack = rootComponent::onBack
is Child.Changelog -> ChangelogScreen(
changelogComponent = child.changelogComponent,
onBack = rootComponent::onBack,
)

is Child.NextFeatures -> TODO()
is Child.BetaFeatures -> TODO()
is Child.FAQ -> FaqScreen(
faqComponent = child.faqComponent,
onBack = rootComponent::onBack
)
}
}

val childSlot by rootComponent.childSlot.subscribeAsState()
childSlot.onChild {
EventReportingScreen(
onClose = rootComponent::closeReporting,
onReport = rootComponent::processReporting
)
}
}
}
}
}

@Composable
fun MigrationScreen(migrationModel: MigrationModel) {
val context = LocalContext.current

val onOpenBrowser = { url: String ->
val intent = Intent(Intent.ACTION_VIEW).apply {
data = url.toUri()
}
context.startActivity(intent)
}

val openPlayStore = { newPackage: String ->
try {
context.startActivity(
Intent(
Intent.ACTION_VIEW,
"market://details?id=$newPackage".toUri()
)
)
} catch (exception: ActivityNotFoundException) {
context.startActivity(
Intent(
Intent.ACTION_VIEW,
"https://play.google.com/store/apps/details?id=$newPackage".toUri()
)
)
}
}

Column(
modifier = Modifier
.fillMaxSize()
.systemBarsPadding()
.padding(vertical = 16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Column(
modifier = Modifier
.weight(1f)
.padding(horizontal = 24.dp)
.verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.spacedBy(24.dp)
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
.height(80.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp, Alignment.CenterHorizontally),
verticalAlignment = Alignment.CenterVertically
) {
Image(
painter = painterResource(id = R.drawable.ic_old),
contentDescription = null
)
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowForward,
contentDescription = null
)
Image(
painter = painterResource(id = R.drawable.ic_new),
contentDescription = null
)
}
Text(
modifier = Modifier.fillMaxWidth(),
text = """
Уважаемые пользователи!
К сожалению, из-за санкций и нововведений Google, приложение было безвозвратно удалено.
Текущее приложение больше не поддерживается, а новое уже доступно для скачивания ниже.
Присоединяйтесь к каналу в Telegram, чтобы быть в курсе всех новостей и обновлений.
С уважением,
команда За Рулем Гродно
""".trimIndent()
)

val childSlot by rootComponent.childSlot.subscribeAsState()
childSlot.onChild {
EventReportingScreen(
onClose = rootComponent::closeReporting,
onReport = rootComponent::processReporting
val channelUrl = stringResource(string.tg_channel_link)

SocialNetwork(
modifier = Modifier.align(Alignment.CenterHorizontally),
title = "Канал в Telegram",
onClick = { onOpenBrowser(channelUrl) }
) {
Icon(
painter = painterResource(drawable.ic_telegram),
contentDescription = stringResource(string.social_telegram_channel)
)
}

}
PrimaryButton(
onClick = {
if (migrationModel.newPackage.isNotEmpty()) {
openPlayStore(migrationModel.newPackage)
} else {
onOpenBrowser(migrationModel.link)
}
}
) {
Text("Скачать новое приложение")
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
package com.egoriku.grodnoroads.screen.root.koin

import com.egoriku.grodnoroads.screen.root.migration.MigrationRepository
import com.egoriku.grodnoroads.screen.root.store.RootStoreFactory
import org.koin.core.module.dsl.factoryOf
import org.koin.dsl.module

val rootModule = module {
factory {
RootStoreFactory(
storeFactory = get(),
dataStore = get()
dataStore = get(),
migrationRepository = get()
).create()
}

factoryOf(::MigrationRepository)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.egoriku.grodnoroads.screen.root.migration

import androidx.annotation.Keep
import com.google.firebase.database.PropertyName

@Keep
class MigrationDTO(

@PropertyName("enabled")
@JvmField
val enabled: Boolean = false,

@PropertyName("link")
@JvmField
val link: String = "",

@PropertyName("newPackage")
@JvmField
val newPackage: String = ""
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.egoriku.grodnoroads.screen.root.migration

import com.egoriku.grodnoroads.extensions.awaitValueEventListener
import com.egoriku.grodnoroads.extensions.common.ResultOf
import com.egoriku.grodnoroads.screen.root.store.RootStoreFactory.MigrationModel
import com.google.firebase.database.DatabaseReference
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map

class MigrationRepository(private val databaseReference: DatabaseReference) {

fun loadAsFlow(): Flow<ResultOf<MigrationModel>> = databaseReference
.child("migration")
.awaitValueEventListener<MigrationDTO>()
.map { resultOf ->
when (resultOf) {
is ResultOf.Failure -> {
ResultOf.Failure(resultOf.exception)
}
is ResultOf.Success -> {
val dto = resultOf.value.firstOrNull() ?: MigrationDTO()

ResultOf.Success(
MigrationModel(
enabled = dto.enabled,
link = dto.link,
newPackage = dto.newPackage
)
)
}
}
}
.flowOn(Dispatchers.IO)
}
Loading

0 comments on commit 9dfb374

Please sign in to comment.