1
0
Fork 0
mirror of https://github.com/anyproto/anytype-kotlin.git synced 2025-06-08 05:47:05 +09:00

DROID-2320 Gallery Installation | Tech | MVVM components (#1009)

This commit is contained in:
Konstantin Ivanov 2024-03-15 14:33:51 +01:00 committed by GitHub
parent c0ee5ef7eb
commit fa08c1325b
Signed by: github
GPG key ID: B5690EEEBB952194
14 changed files with 324 additions and 1 deletions

View file

@ -171,6 +171,7 @@ dependencies {
implementation project(':ui-settings')
implementation project(':crash-reporting')
implementation project(':payments')
implementation project(':gallery-experience')
//Compile time dependencies
kapt libs.daggerCompiler

View file

@ -60,6 +60,7 @@ import com.anytypeio.anytype.di.feature.ViewerFilterModule
import com.anytypeio.anytype.di.feature.ViewerSortModule
import com.anytypeio.anytype.di.feature.auth.DaggerDeletedAccountComponent
import com.anytypeio.anytype.di.feature.cover.UnsplashModule
import com.anytypeio.anytype.di.feature.gallery.DaggerGalleryInstallationComponent
import com.anytypeio.anytype.di.feature.home.DaggerHomeScreenComponent
import com.anytypeio.anytype.di.feature.library.DaggerLibraryComponent
import com.anytypeio.anytype.di.feature.multiplayer.DaggerRequestJoinSpaceComponent
@ -1075,6 +1076,10 @@ class ComponentManager(
DaggerPaymentsComponent.factory().create(findComponentDependencies())
}
val galleryInstallationsComponent = Component {
DaggerGalleryInstallationComponent.factory().create(findComponentDependencies())
}
class Component<T>(private val builder: () -> T) {
private var instance: T? = null

View file

@ -0,0 +1,48 @@
package com.anytypeio.anytype.di.feature.gallery
import androidx.lifecycle.ViewModelProvider
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.core_utils.di.scope.PerScreen
import com.anytypeio.anytype.di.common.ComponentDependencies
import com.anytypeio.anytype.gallery_experience.viewmodel.GalleryInstallationViewModelFactory
import com.anytypeio.anytype.ui.gallery.GalleryInstallationFragment
import dagger.Binds
import dagger.Component
import dagger.Module
@Component(
dependencies = [GalleryInstallationComponentDependencies::class],
modules = [
GalleryInstallationModule::class,
GalleryInstallationModule.Declarations::class
]
)
@PerScreen
interface GalleryInstallationComponent {
@Component.Factory
interface Factory {
fun create(dependencies: GalleryInstallationComponentDependencies): GalleryInstallationComponent
}
fun inject(fragment: GalleryInstallationFragment)
}
@Module
object GalleryInstallationModule {
@Module
interface Declarations {
@PerScreen
@Binds
fun bindViewModelFactory(
factory: GalleryInstallationViewModelFactory
): ViewModelProvider.Factory
}
}
interface GalleryInstallationComponentDependencies : ComponentDependencies {
fun analytics(): Analytics
}

View file

@ -19,6 +19,7 @@ import com.anytypeio.anytype.di.feature.ObjectTypeChangeSubComponent
import com.anytypeio.anytype.di.feature.PersonalizationSettingsSubComponent
import com.anytypeio.anytype.di.feature.SplashDependencies
import com.anytypeio.anytype.di.feature.auth.DeletedAccountDependencies
import com.anytypeio.anytype.di.feature.gallery.GalleryInstallationComponentDependencies
import com.anytypeio.anytype.di.feature.home.HomeScreenDependencies
import com.anytypeio.anytype.di.feature.library.LibraryDependencies
import com.anytypeio.anytype.di.feature.multiplayer.RequestJoinSpaceDependencies
@ -112,7 +113,8 @@ interface MainComponent :
ShareSpaceDependencies,
SpaceJoinRequestDependencies,
RequestJoinSpaceDependencies,
PaymentsComponentDependencies
PaymentsComponentDependencies,
GalleryInstallationComponentDependencies
{
fun inject(app: AndroidApplication)
@ -308,4 +310,9 @@ private abstract class ComponentDependenciesModule private constructor() {
@IntoMap
@ComponentDependenciesKey(RequestJoinSpaceDependencies::class)
abstract fun provideRequestToJoinSpaceDependencies(component: MainComponent): ComponentDependencies
@Binds
@IntoMap
@ComponentDependenciesKey(GalleryInstallationComponentDependencies::class)
abstract fun provideGalleryInstallationDependencies(component: MainComponent): ComponentDependencies
}

View file

@ -0,0 +1,119 @@
package com.anytypeio.anytype.ui.gallery
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.fragment.app.viewModels
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.anytypeio.anytype.core_ui.common.ComposeDialogView
import com.anytypeio.anytype.core_utils.ext.subscribe
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment
import com.anytypeio.anytype.di.common.componentManager
import com.anytypeio.anytype.gallery_experience.models.GalleryInstallationNavigation
import com.anytypeio.anytype.gallery_experience.screens.GalleryInstallationScreen
import com.anytypeio.anytype.gallery_experience.screens.GalleryInstallationSpacesScreen
import com.anytypeio.anytype.gallery_experience.viewmodel.GalleryInstallationViewModel
import com.anytypeio.anytype.gallery_experience.viewmodel.GalleryInstallationViewModelFactory
import com.google.accompanist.navigation.material.BottomSheetNavigator
import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi
import com.google.accompanist.navigation.material.ModalBottomSheetLayout
import com.google.accompanist.navigation.material.bottomSheet
import com.google.accompanist.navigation.material.rememberBottomSheetNavigator
import javax.inject.Inject
class GalleryInstallationFragment : BaseBottomSheetComposeFragment() {
@Inject
lateinit var factory: GalleryInstallationViewModelFactory
private val vm by viewModels<GalleryInstallationViewModel> { factory }
private lateinit var navController: NavHostController
@OptIn(ExperimentalMaterialNavigationApi::class)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return ComposeDialogView(context = requireContext(), dialog = requireDialog()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
MaterialTheme {
val bottomSheetNavigator = rememberBottomSheetNavigator()
navController = rememberNavController(bottomSheetNavigator)
SetupNavigation(bottomSheetNavigator, navController)
}
}
}
}
override fun onStart() {
super.onStart()
jobs += subscribe(vm.command) { command ->
when (command) {
GalleryInstallationNavigation.Main -> navController.navigate(
GalleryInstallationNavigation.Main.route
)
GalleryInstallationNavigation.Dismiss -> navController.popBackStack()
else -> {}
}
}
}
@OptIn(ExperimentalMaterialNavigationApi::class)
@Composable
private fun SetupNavigation(
bottomSheetNavigator: BottomSheetNavigator,
navController: NavHostController
) {
ModalBottomSheetLayout(bottomSheetNavigator = bottomSheetNavigator) {
NavigationGraph(navController = navController)
}
}
@OptIn(ExperimentalMaterialNavigationApi::class)
@Composable
private fun NavigationGraph(navController: NavHostController) {
NavHost(
navController = navController,
startDestination = GalleryInstallationNavigation.Main.route
) {
composable(GalleryInstallationNavigation.Main.route) {
InitMainScreen()
}
bottomSheet(GalleryInstallationNavigation.Spaces.route) {
InitSpacesScreen()
}
}
}
@Composable
private fun InitMainScreen() {
skipCollapsed()
expand()
GalleryInstallationScreen(
state = vm.mainState.collectAsStateWithLifecycle().value
)
}
@Composable
private fun InitSpacesScreen() {
GalleryInstallationSpacesScreen()
}
override fun injectDependencies() {
componentManager().galleryInstallationsComponent.get().inject(this)
}
override fun releaseDependencies() {
componentManager().galleryInstallationsComponent.release()
}
}

View file

@ -0,0 +1,57 @@
plugins {
id "com.android.library"
id "kotlin-android"
}
android {
def config = rootProject.extensions.getByName("ext")
buildFeatures {
compose true
}
composeOptions {
kotlinCompilerExtensionVersion libs.versions.composeKotlinCompilerVersion.get()
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlin {
jvmToolchain(17)
}
namespace 'com.anytypeio.anytype.galleryexperience'
}
dependencies {
implementation project(':domain')
implementation project(':core-ui')
implementation project(':analytics')
implementation project(':core-models')
implementation project(':core-utils')
implementation project(':localization')
implementation project(':presentation')
implementation project(':library-emojifier')
compileOnly libs.javaxInject
implementation libs.lifecycleViewModel
implementation libs.lifecycleRuntime
implementation libs.appcompat
implementation libs.compose
implementation libs.composeFoundation
implementation libs.composeToolingPreview
implementation libs.composeMaterial3
implementation libs.coilCompose
debugImplementation libs.composeTooling
implementation libs.timber
testImplementation libs.junit
testImplementation libs.kotlinTest
}

View file

@ -0,0 +1 @@
<manifest />

View file

@ -0,0 +1,15 @@
package com.anytypeio.anytype.gallery_experience.models
sealed class GalleryInstallationState {
object Hidden : GalleryInstallationState()
object Loading : GalleryInstallationState()
object Success : GalleryInstallationState()
}
sealed class GalleryInstallationNavigation(val route: String) {
object Main : GalleryInstallationNavigation("main")
object Spaces : GalleryInstallationNavigation("spaces")
object Success : GalleryInstallationNavigation("success")
object Error : GalleryInstallationNavigation("error")
object Dismiss : GalleryInstallationNavigation("")
}

View file

@ -0,0 +1,16 @@
package com.anytypeio.anytype.gallery_experience.screens
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import com.anytypeio.anytype.gallery_experience.models.GalleryInstallationState
@Composable
fun GalleryInstallationScreen(state: GalleryInstallationState) {
}
@Preview
@Composable
private fun GalleryInstallationScreenPreview() {
GalleryInstallationScreen(GalleryInstallationState.Success)
}

View file

@ -0,0 +1,14 @@
package com.anytypeio.anytype.gallery_experience.screens
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
@Composable
fun GalleryInstallationSpacesScreen() {
}
@Preview
@Composable
private fun GallerySpacesScreenPreview() {
GalleryInstallationSpacesScreen()
}

View file

@ -0,0 +1,20 @@
package com.anytypeio.anytype.gallery_experience.viewmodel
import androidx.lifecycle.ViewModel
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.gallery_experience.models.GalleryInstallationNavigation
import com.anytypeio.anytype.gallery_experience.models.GalleryInstallationState
import kotlinx.coroutines.flow.MutableStateFlow
import timber.log.Timber
class GalleryInstallationViewModel(
private val analytics: Analytics,
) : ViewModel() {
val mainState = MutableStateFlow<GalleryInstallationState>(GalleryInstallationState.Hidden)
val command = MutableStateFlow<GalleryInstallationNavigation?>(null)
init {
Timber.d("GalleryInstallationViewModel init")
}
}

View file

@ -0,0 +1,17 @@
package com.anytypeio.anytype.gallery_experience.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.anytypeio.anytype.analytics.base.Analytics
import javax.inject.Inject
class GalleryInstallationViewModelFactory@Inject constructor(
private val analytics: Analytics,
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return GalleryInstallationViewModel(
analytics = analytics,
) as T
}
}

View file

@ -0,0 +1,2 @@
<resources>
</resources>

View file

@ -59,3 +59,4 @@ include ':ui-settings'
include ':crash-reporting'
include ':localization'
include ':payments'
include ':gallery-experience'