From fa451c1317dabf96f217ccfa1eface9d0aba0b91 Mon Sep 17 00:00:00 2001 From: stslex Date: Thu, 27 Oct 2022 21:33:16 +0300 Subject: [PATCH] refactor navigation on one screen --- .idea/compiler.xml | 2 +- .idea/gradle.xml | 2 +- .idea/inspectionProfiles/Project_Default.xml | 4 + .idea/kotlinc.xml | 6 ++ .idea/misc.xml | 2 +- .../java/com/stslex/cnotes/MainActivity.kt | 44 ++++++-- .../com/stslex/cnotes/di/ActivityComponent.kt | 18 ++++ .../cnotes/navigation/NavigationHost.kt | 13 ++- .../java/com/stslex/cnotes/ui/AppCreator.kt | 8 +- .../feature_note_list/di/NoteListModule.kt | 12 ++- .../navigation/NoteListGraph.kt | 9 +- .../navigation/NoteListRouter.kt | 7 ++ .../navigation/NoteListRouterImpl.kt | 22 ++++ .../feature_note_list/ui/NoteListRoute.kt | 9 +- .../feature_note_list/ui/NotesScreen.kt | 33 ++---- .../feature_note_list/ui/NotesViewModel.kt | 55 +++++++++- .../feature_note_list/ui/core/UIObjectsExt.kt | 11 ++ .../feature_note_list/ui/fab/NotesFab.kt | 42 +------- .../ui/note/NotePagingItem.kt | 13 ++- .../ui/top_bar/NoteMediumTopAppBar.kt | 100 +++++++++++------- .../com/stslex/feature_todo/ui/ToDoContent.kt | 96 +++++++++++++++++ .../com/stslex/feature_todo/ui/TodoRoute.kt | 8 +- gradle/libs.versions.toml | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 24 files changed, 366 insertions(+), 154 deletions(-) create mode 100644 .idea/kotlinc.xml create mode 100644 app/src/main/java/com/stslex/cnotes/di/ActivityComponent.kt create mode 100644 feature-note-list/src/main/java/com/stslex/feature_note_list/navigation/NoteListRouter.kt create mode 100644 feature-note-list/src/main/java/com/stslex/feature_note_list/navigation/NoteListRouterImpl.kt create mode 100644 feature-note-list/src/main/java/com/stslex/feature_note_list/ui/core/UIObjectsExt.kt create mode 100644 feature-todo/src/main/java/com/stslex/feature_todo/ui/ToDoContent.kt diff --git a/.idea/compiler.xml b/.idea/compiler.xml index dfa6678..cfb3ffe 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -1,7 +1,7 @@ - + diff --git a/.idea/gradle.xml b/.idea/gradle.xml index cb861d9..c0ec0ba 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -19,7 +19,7 @@ - + diff --git a/app/src/main/java/com/stslex/cnotes/MainActivity.kt b/app/src/main/java/com/stslex/cnotes/MainActivity.kt index acbef0c..0f5bc88 100644 --- a/app/src/main/java/com/stslex/cnotes/MainActivity.kt +++ b/app/src/main/java/com/stslex/cnotes/MainActivity.kt @@ -3,40 +3,62 @@ package com.stslex.cnotes import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass +import androidx.compose.runtime.Composable import androidx.compose.runtime.SideEffect import androidx.compose.ui.graphics.Color import androidx.core.view.WindowCompat +import androidx.navigation.NavHostController +import com.google.accompanist.navigation.animation.rememberAnimatedNavController import com.google.accompanist.systemuicontroller.SystemUiController import com.google.accompanist.systemuicontroller.rememberSystemUiController +import com.stslex.cnotes.di.ActivityComponent import com.stslex.cnotes.ui.AppCreator import com.stslex.cnotes.utils.ShortcutBuilder import com.stslex.core_firebase.utils.abstraction.FirebaseAppInitialisationUtil import org.koin.android.ext.android.inject +import org.koin.core.context.loadKoinModules class MainActivity : ComponentActivity() { private val firebaseAppInitialisationUtil: FirebaseAppInitialisationUtil by inject() private val shortcutBuilder: ShortcutBuilder by inject() - @OptIn(ExperimentalMaterial3WindowSizeClassApi::class) + @OptIn(ExperimentalMaterial3WindowSizeClassApi::class, ExperimentalAnimationApi::class) override fun onCreate(savedInstanceState: Bundle?) { - firebaseAppInitialisationUtil.invoke() + firebaseAppInitialisationUtil() super.onCreate(savedInstanceState) WindowCompat.setDecorFitsSystemWindows(window, false) setContent { - val systemUiController: SystemUiController = rememberSystemUiController() - val iconsDark = !isSystemInDarkTheme() - SideEffect { - systemUiController.setSystemBarsColor( - color = Color.Transparent, - darkIcons = iconsDark - ) - } - AppCreator(calculateWindowSizeClass(this)) + SetUpUIEffects() + val navController = rememberAnimatedNavController() + setUpDependencies(navController) + AppCreator( + windowSizeClass = calculateWindowSizeClass(this), + navController = navController + ) } shortcutBuilder.invoke() } + + private fun setUpDependencies(navController: NavHostController) { + val component = ActivityComponent() + val moduleNavigation = component.module(navController) + loadKoinModules(moduleNavigation) + } + + @Composable + private fun SetUpUIEffects() { + val systemUiController: SystemUiController = rememberSystemUiController() + val iconsDark = !isSystemInDarkTheme() + SideEffect { + systemUiController.setSystemBarsColor( + color = Color.Transparent, + darkIcons = iconsDark + ) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/stslex/cnotes/di/ActivityComponent.kt b/app/src/main/java/com/stslex/cnotes/di/ActivityComponent.kt new file mode 100644 index 0000000..46fafd3 --- /dev/null +++ b/app/src/main/java/com/stslex/cnotes/di/ActivityComponent.kt @@ -0,0 +1,18 @@ +package com.stslex.cnotes.di + +import androidx.navigation.NavController +import androidx.navigation.NavHostController +import org.koin.core.annotation.Single +import org.koin.core.module.Module +import org.koin.dsl.module + +@Single +class ActivityComponent { + + val module: (navController: NavHostController) -> Module + get() = { navController -> + module { + single { navController } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/stslex/cnotes/navigation/NavigationHost.kt b/app/src/main/java/com/stslex/cnotes/navigation/NavigationHost.kt index 3cf9dfd..0ed2531 100644 --- a/app/src/main/java/com/stslex/cnotes/navigation/NavigationHost.kt +++ b/app/src/main/java/com/stslex/cnotes/navigation/NavigationHost.kt @@ -5,11 +5,14 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.navigation.NavGraph.Companion.findStartDestination import androidx.navigation.NavHostController +import com.google.accompanist.navigation.animation.AnimatedNavHost +import com.stslex.core_navigation.destinations.AuthCodeDestination +import com.stslex.core_navigation.destinations.NoteListDestination +import com.stslex.core_navigation.destinations.PhoneNumberDestination +import com.stslex.core_navigation.destinations.ProfileDestination import com.stslex.feature_auth_code.navigation.authCodeGraph import com.stslex.feature_auth_phonenumber.navigation.authPhoneNumberGraph import com.stslex.feature_note_list.navigation.noteListGraph -import com.google.accompanist.navigation.animation.AnimatedNavHost -import com.stslex.core_navigation.destinations.* import com.stslex.feature_profile.navigation.profileGraph import com.stslex.feature_single_note.navigation.singleNoteGraph import com.stslex.feature_todo.navigation.todoGraph @@ -26,11 +29,7 @@ fun NavigationHost( navController = navController, startDestination = startDestination ) { - noteListGraph( - openSingleNote = { navController.navigate("${SingleNoteDestination.route}/$it") }, - openProfile = { navController.navigate(ProfileDestination.route) }, - openAuthPhoneNumber = { navController.navigate(PhoneNumberDestination.route) } - ) { + noteListGraph { singleNoteGraph(popBackStack = { navController.popBackStack() }) } todoGraph() diff --git a/app/src/main/java/com/stslex/cnotes/ui/AppCreator.kt b/app/src/main/java/com/stslex/cnotes/ui/AppCreator.kt index 07bc0ce..dcda3fa 100644 --- a/app/src/main/java/com/stslex/cnotes/ui/AppCreator.kt +++ b/app/src/main/java/com/stslex/cnotes/ui/AppCreator.kt @@ -12,6 +12,8 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.navigation.NavController +import androidx.navigation.NavHostController import androidx.navigation.compose.currentBackStackEntryAsState import com.stslex.feature_note_list.navigation.noteListTopLevelDestination import com.google.accompanist.navigation.animation.rememberAnimatedNavController @@ -34,9 +36,11 @@ private val listOfDestinations = listOf( ExperimentalAnimationApi::class ) @Composable -fun AppCreator(windowSizeClass: WindowSizeClass) { +fun AppCreator( + windowSizeClass: WindowSizeClass, + navController: NavHostController +) { AppTheme(dynamicColor = Build.VERSION.SDK_INT > 30) { - val navController = rememberAnimatedNavController() val niaTopLevelNavigation = remember(navController) { AppTopLevelNavigation(navController) } diff --git a/feature-note-list/src/main/java/com/stslex/feature_note_list/di/NoteListModule.kt b/feature-note-list/src/main/java/com/stslex/feature_note_list/di/NoteListModule.kt index 0d9935c..0d3aeeb 100644 --- a/feature-note-list/src/main/java/com/stslex/feature_note_list/di/NoteListModule.kt +++ b/feature-note-list/src/main/java/com/stslex/feature_note_list/di/NoteListModule.kt @@ -1,10 +1,12 @@ package com.stslex.feature_note_list.di import androidx.paging.PagingConfig -import com.stslex.feature_note_list.ui.NotesViewModel import com.stslex.core_model.common.MapperName import com.stslex.feature_note_list.data.abstraction.NoteListRepository import com.stslex.feature_note_list.data.realisation.NoteListRepositoryImpl +import com.stslex.feature_note_list.navigation.NoteListRouter +import com.stslex.feature_note_list.navigation.NoteListRouterImpl +import com.stslex.feature_note_list.ui.NotesViewModel import org.koin.androidx.viewmodel.dsl.viewModel import org.koin.core.module.dsl.bind import org.koin.core.module.dsl.factoryOf @@ -12,15 +14,21 @@ import org.koin.core.qualifier.named import org.koin.dsl.module class NoteListModule { + val module = module { + single { PagingConfig(10) } + factoryOf(::NoteListRepositoryImpl) { bind() } + factoryOf(::NoteListRouterImpl) { bind() } + viewModel { NotesViewModel( noteRepository = get(), noteMapper = get(named(MapperName.PAGING_ENTITY_DYNAMIC)), - dispatchers = get() + dispatchers = get(), + router = get() ) } } diff --git a/feature-note-list/src/main/java/com/stslex/feature_note_list/navigation/NoteListGraph.kt b/feature-note-list/src/main/java/com/stslex/feature_note_list/navigation/NoteListGraph.kt index d78227d..551aed9 100644 --- a/feature-note-list/src/main/java/com/stslex/feature_note_list/navigation/NoteListGraph.kt +++ b/feature-note-list/src/main/java/com/stslex/feature_note_list/navigation/NoteListGraph.kt @@ -14,9 +14,6 @@ import com.stslex.core_navigation.TopLevelDestination @OptIn(ExperimentalAnimationApi::class) fun NavGraphBuilder.noteListGraph( - openSingleNote: (Int) -> Unit, - openProfile: () -> Unit, - openAuthPhoneNumber: () -> Unit, nestedGraphs: NavGraphBuilder.() -> Unit ) { navigation( @@ -30,11 +27,7 @@ fun NavGraphBuilder.noteListGraph( popEnterTransition = NoteListTransitions.popEnterTransition, popExitTransition = NoteListTransitions.popExitTransition ) { - NoteListRoute( - openSingleNote = openSingleNote, - openProfile = openProfile, - openAuthPhoneNumber = openAuthPhoneNumber - ) + NoteListRoute() } nestedGraphs() } diff --git a/feature-note-list/src/main/java/com/stslex/feature_note_list/navigation/NoteListRouter.kt b/feature-note-list/src/main/java/com/stslex/feature_note_list/navigation/NoteListRouter.kt new file mode 100644 index 0000000..61b22b7 --- /dev/null +++ b/feature-note-list/src/main/java/com/stslex/feature_note_list/navigation/NoteListRouter.kt @@ -0,0 +1,7 @@ +package com.stslex.feature_note_list.navigation + +interface NoteListRouter { + val openSingleNote: (noteId: Int) -> Unit + val openProfile: () -> Unit + val openAuthPhoneNumber: () -> Unit +} \ No newline at end of file diff --git a/feature-note-list/src/main/java/com/stslex/feature_note_list/navigation/NoteListRouterImpl.kt b/feature-note-list/src/main/java/com/stslex/feature_note_list/navigation/NoteListRouterImpl.kt new file mode 100644 index 0000000..520c36e --- /dev/null +++ b/feature-note-list/src/main/java/com/stslex/feature_note_list/navigation/NoteListRouterImpl.kt @@ -0,0 +1,22 @@ +package com.stslex.feature_note_list.navigation + +import androidx.navigation.NavController +import com.stslex.core_navigation.destinations.PhoneNumberDestination +import com.stslex.core_navigation.destinations.ProfileDestination +import com.stslex.core_navigation.destinations.SingleNoteDestination + +class NoteListRouterImpl( + private val navController: NavController +) : NoteListRouter { + + override val openSingleNote: (Int) -> Unit + get() = { noteId -> + navController.navigate("${SingleNoteDestination.route}/$noteId") + } + + override val openProfile: () -> Unit + get() = { navController.navigate(ProfileDestination.route) } + + override val openAuthPhoneNumber: () -> Unit + get() = { navController.navigate(PhoneNumberDestination.route) } +} \ No newline at end of file diff --git a/feature-note-list/src/main/java/com/stslex/feature_note_list/ui/NoteListRoute.kt b/feature-note-list/src/main/java/com/stslex/feature_note_list/ui/NoteListRoute.kt index f99994c..0e58647 100644 --- a/feature-note-list/src/main/java/com/stslex/feature_note_list/ui/NoteListRoute.kt +++ b/feature-note-list/src/main/java/com/stslex/feature_note_list/ui/NoteListRoute.kt @@ -7,15 +7,8 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @Composable -fun NoteListRoute( - openSingleNote: (Int) -> Unit, - openProfile: () -> Unit, - openAuthPhoneNumber: () -> Unit -) { +fun NoteListRoute() { NotesScreen( - openSingleNote = openSingleNote, - openProfile = openProfile, - openAuthPhoneNumber = openAuthPhoneNumber, modifier = Modifier.windowInsetsPadding(WindowInsets.safeDrawing) ) } \ No newline at end of file diff --git a/feature-note-list/src/main/java/com/stslex/feature_note_list/ui/NotesScreen.kt b/feature-note-list/src/main/java/com/stslex/feature_note_list/ui/NotesScreen.kt index e88eab3..79752e6 100644 --- a/feature-note-list/src/main/java/com/stslex/feature_note_list/ui/NotesScreen.kt +++ b/feature-note-list/src/main/java/com/stslex/feature_note_list/ui/NotesScreen.kt @@ -5,12 +5,14 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.material3.* +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.FabPosition +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Surface import androidx.compose.material3.TopAppBarDefaults.enterAlwaysScrollBehavior +import androidx.compose.material3.TopAppBarScrollState +import androidx.compose.material3.rememberTopAppBarScrollState import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateListOf -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.tooling.preview.Preview @@ -19,44 +21,33 @@ import androidx.paging.compose.items import com.stslex.feature_note_list.ui.fab.NotesFab import com.stslex.feature_note_list.ui.note.NotePagingItem import com.stslex.feature_note_list.ui.top_bar.NoteMediumTopAppBar -import com.stslex.core_model.model.NoteDynamicUI import org.koin.androidx.compose.getViewModel @OptIn(ExperimentalMaterial3Api::class) @Composable fun NotesScreen( - openSingleNote: (Int) -> Unit, - openProfile: () -> Unit, - openAuthPhoneNumber: () -> Unit, modifier: Modifier = Modifier, viewModel: NotesViewModel = getViewModel(), lazyListState: LazyListState = rememberLazyListState(), scrollState: TopAppBarScrollState = rememberTopAppBarScrollState() ) { val pagingItems = viewModel.allNotes.collectAsLazyPagingItems() - val selectedNotes = remember { mutableStateListOf() } - val deleteNotesFunction = viewModel::deleteNotesById val scrollBehavior = enterAlwaysScrollBehavior(scrollState) { pagingItems.itemCount >= 6 } - val isButtonVisible = remember { mutableStateOf(true) } Scaffold( modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection), floatingActionButton = { - AnimatedVisibility(visible = isButtonVisible.value) { + AnimatedVisibility(visible = viewModel.isCreateButtonVisible.value) { NotesFab( lazyListState = lazyListState, - selectedNotes = selectedNotes, - deleteNotesFunction = deleteNotesFunction, - openSingleNote = openSingleNote, - isButtonVisible = isButtonVisible + viewModel = viewModel ) } }, topBar = { NoteMediumTopAppBar( scrollBehavior = scrollBehavior, - openAccount = if (viewModel.isUserAuth) openProfile else openAuthPhoneNumber, - selectedNotes = selectedNotes + viewModel = viewModel ) }, floatingActionButtonPosition = FabPosition.End @@ -71,9 +62,7 @@ fun NotesScreen( item?.let { NotePagingItem( note = item, - selectedNotes = selectedNotes, - openSingleNote = openSingleNote, - isButtonVisible = isButtonVisible + viewModel = viewModel ) } } @@ -86,5 +75,5 @@ fun NotesScreen( @Preview @Composable fun NotesPreview() { - NotesScreen({}, {}, {}) + NotesScreen() } \ No newline at end of file diff --git a/feature-note-list/src/main/java/com/stslex/feature_note_list/ui/NotesViewModel.kt b/feature-note-list/src/main/java/com/stslex/feature_note_list/ui/NotesViewModel.kt index f2c6830..45cc245 100644 --- a/feature-note-list/src/main/java/com/stslex/feature_note_list/ui/NotesViewModel.kt +++ b/feature-note-list/src/main/java/com/stslex/feature_note_list/ui/NotesViewModel.kt @@ -1,5 +1,9 @@ package com.stslex.feature_note_list.ui +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.snapshots.SnapshotStateList import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.paging.PagingData @@ -9,27 +13,68 @@ import com.stslex.core.Mapper import com.stslex.core_model.model.NoteDynamicUI import com.stslex.core_model.model.NoteEntity import com.stslex.feature_note_list.data.abstraction.NoteListRepository -import kotlinx.coroutines.flow.* +import com.stslex.feature_note_list.navigation.NoteListRouter +import com.stslex.feature_note_list.ui.core.UIObjectsExt.clearSelection +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.mapLatest +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch class NotesViewModel( private val noteRepository: NoteListRepository, private val noteMapper: Mapper.Data, PagingData>, - private val dispatchers: AppDispatchers + private val dispatchers: AppDispatchers, + private val router: NoteListRouter ) : ViewModel() { + private var deleteNotesJob: Job? = null + + val selectedNotes: SnapshotStateList = mutableStateListOf() + + val isCreateButtonVisible: MutableState = mutableStateOf(true) + val allNotes: StateFlow> = noteRepository.allNotes .mapLatest(noteMapper::map) .cachedIn(viewModelScope) .flowOn(dispatchers.io) - .stateIn(viewModelScope, SharingStarted.Lazily, PagingData.empty()) + .stateIn( + scope = viewModelScope, + started = + SharingStarted.Lazily, + initialValue = PagingData.empty() + ) - fun deleteNotesById(ids: List) { - viewModelScope.launch(context = dispatchers.io) { + fun onCreateButtonClicked() { + selectedNotes.apply { + if (isNotEmpty()) { + deleteNotesById(map { it.id }) + clearSelection() + } else { + isCreateButtonVisible.value = false + openSingleNote(-1) + } + } + } + + private fun deleteNotesById(ids: List) { + deleteNotesJob?.cancel() + deleteNotesJob = viewModelScope.launch(context = dispatchers.io) { noteRepository.deleteNotesById(ids) } } val isUserAuth: Boolean get() = noteRepository.isUserAuth + + val openSingleNote: (noteId: Int) -> Unit + get() = router.openSingleNote + + val openProfile: () -> Unit + get() = router.openProfile + + val openAuthPhoneNumber: () -> Unit + get() = router.openAuthPhoneNumber } \ No newline at end of file diff --git a/feature-note-list/src/main/java/com/stslex/feature_note_list/ui/core/UIObjectsExt.kt b/feature-note-list/src/main/java/com/stslex/feature_note_list/ui/core/UIObjectsExt.kt new file mode 100644 index 0000000..7777060 --- /dev/null +++ b/feature-note-list/src/main/java/com/stslex/feature_note_list/ui/core/UIObjectsExt.kt @@ -0,0 +1,11 @@ +package com.stslex.feature_note_list.ui.core + +import androidx.compose.runtime.snapshots.SnapshotStateList +import com.stslex.core_model.model.NoteDynamicUI + +object UIObjectsExt { + fun SnapshotStateList.clearSelection() { + forEach { it.setSelect(false) } + clear() + } +} \ No newline at end of file diff --git a/feature-note-list/src/main/java/com/stslex/feature_note_list/ui/fab/NotesFab.kt b/feature-note-list/src/main/java/com/stslex/feature_note_list/ui/fab/NotesFab.kt index dc7fe98..bc91078 100644 --- a/feature-note-list/src/main/java/com/stslex/feature_note_list/ui/fab/NotesFab.kt +++ b/feature-note-list/src/main/java/com/stslex/feature_note_list/ui/fab/NotesFab.kt @@ -4,58 +4,31 @@ import androidx.compose.foundation.lazy.LazyListState import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.MutableState -import androidx.compose.runtime.snapshots.SnapshotStateList import androidx.compose.ui.platform.LocalContext -import com.stslex.core_model.model.NoteDynamicUI import com.stslex.core_ui.components.fabs.ExtendableFloatingActionButton +import com.stslex.feature_note_list.ui.NotesViewModel @Composable fun NotesFab( lazyListState: LazyListState, - selectedNotes: SnapshotStateList, - deleteNotesFunction: (List) -> Unit, - openSingleNote: (Int) -> Unit, - isButtonVisible: MutableState + viewModel: NotesViewModel ) { ExtendableFloatingActionButton( extended = !lazyListState.isScrollInProgress, text = animatedButtonContent( - snapshotStateList = selectedNotes, + snapshotStateList = viewModel.selectedNotes, targetContent = fabTestField(NotesFabResources.Delete), content = fabTestField(NotesFabResources.Create) ), icon = animatedButtonContent( - snapshotStateList = selectedNotes, + snapshotStateList = viewModel.selectedNotes, targetContent = fabIcon(NotesFabResources.Delete), content = fabIcon(NotesFabResources.Create) ), - onClick = fabClickListener( - selectedNotes = selectedNotes, - deleteNotesFunction = deleteNotesFunction, - openSingleNote = openSingleNote, - isButtonVisible = isButtonVisible - ) + onClick = viewModel::onCreateButtonClicked ) } -private fun fabClickListener( - selectedNotes: SnapshotStateList, - deleteNotesFunction: (List) -> Unit, - openSingleNote: (Int) -> Unit, - isButtonVisible: MutableState -): () -> Unit = { - selectedNotes.apply { - if (isNotEmpty()) { - deleteNotesFunction(map { it.id }) - clearSelection() - } else { - isButtonVisible.value = false - openSingleNote(-1) - } - } -} - private fun fabTestField(resources: NotesFabResources): @Composable () -> Unit = { Text( text = LocalContext.current.getString(resources.label) @@ -67,9 +40,4 @@ private fun fabIcon(resources: NotesFabResources): @Composable () -> Unit = { imageVector = resources.icon, contentDescription = LocalContext.current.getString(resources.label) ) -} - -fun SnapshotStateList.clearSelection() { - forEach { it.setSelect(false) } - clear() } \ No newline at end of file diff --git a/feature-note-list/src/main/java/com/stslex/feature_note_list/ui/note/NotePagingItem.kt b/feature-note-list/src/main/java/com/stslex/feature_note_list/ui/note/NotePagingItem.kt index c3f1ab1..eb86776 100644 --- a/feature-note-list/src/main/java/com/stslex/feature_note_list/ui/note/NotePagingItem.kt +++ b/feature-note-list/src/main/java/com/stslex/feature_note_list/ui/note/NotePagingItem.kt @@ -17,15 +17,14 @@ import androidx.compose.runtime.snapshots.SnapshotStateList import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import com.stslex.core_model.model.NoteDynamicUI +import com.stslex.feature_note_list.ui.NotesViewModel @OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class) @Composable fun NotePagingItem( note: NoteDynamicUI, - selectedNotes: SnapshotStateList, - openSingleNote: (Int) -> Unit, - isButtonVisible: MutableState + viewModel: NotesViewModel ) { val colorUnselect = MaterialTheme.colorScheme.surface val colorSelect = MaterialTheme.colorScheme.primaryContainer @@ -42,13 +41,13 @@ fun NotePagingItem( .combinedClickable( onClick = noteItemClick( note = note, - openSingleNote = openSingleNote, - selectedNotes = selectedNotes, - isButtonVisible = isButtonVisible + openSingleNote = viewModel.openSingleNote, + selectedNotes = viewModel.selectedNotes, + isButtonVisible = viewModel.isCreateButtonVisible ), onLongClick = noteItemLongClick( note = note, - selectedNotes = selectedNotes + selectedNotes = viewModel.selectedNotes ) ) .fillMaxWidth() diff --git a/feature-note-list/src/main/java/com/stslex/feature_note_list/ui/top_bar/NoteMediumTopAppBar.kt b/feature-note-list/src/main/java/com/stslex/feature_note_list/ui/top_bar/NoteMediumTopAppBar.kt index 0f628db..28148c8 100644 --- a/feature-note-list/src/main/java/com/stslex/feature_note_list/ui/top_bar/NoteMediumTopAppBar.kt +++ b/feature-note-list/src/main/java/com/stslex/feature_note_list/ui/top_bar/NoteMediumTopAppBar.kt @@ -3,67 +3,91 @@ package com.stslex.feature_note_list.ui.top_bar import android.content.Context import android.content.Intent import androidx.compose.animation.AnimatedVisibility -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyRow import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Person import androidx.compose.material.icons.filled.Share -import androidx.compose.material3.* -import androidx.compose.runtime.* +import androidx.compose.material3.Button +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.MediumTopAppBar +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBarScrollBehavior +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshots.SnapshotStateList import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import com.stslex.core_model.model.NoteDynamicUI import com.stslex.core_ui.R -import com.stslex.feature_note_list.ui.fab.clearSelection +import com.stslex.feature_note_list.ui.NotesViewModel +import com.stslex.feature_note_list.ui.core.UIObjectsExt.clearSelection @Composable fun NoteMediumTopAppBar( + modifier: Modifier = Modifier, scrollBehavior: TopAppBarScrollBehavior, - openAccount: () -> Unit, - selectedNotes: SnapshotStateList, context: Context = LocalContext.current, - modifier: Modifier = Modifier + viewModel: NotesViewModel ) { var profileButtonClicked by remember { mutableStateOf(false) } - MediumTopAppBar(modifier = modifier.fillMaxWidth(), title = { - Text( - text = context.getString(R.string.app_name), - style = MaterialTheme.typography.titleLarge - ) - }, navigationIcon = { - AnimatedVisibility( - visible = selectedNotes.isEmpty(), - ) { - Row() { - IconButton(onClick = { profileButtonClicked = !profileButtonClicked }) { - Icon( - imageVector = Icons.Default.Person, - contentDescription = context.getString(R.string.lb_account) - ) - } - AnimatedVisibility(visible = profileButtonClicked) { - LazyRow { - item { - Spacer(modifier = Modifier.padding(4.dp)) - Button( - modifier = Modifier.padding(end = 4.dp), onClick = openAccount - ) { - Text(text = context.getString(R.string.lb_short_shortcut_profile)) + MediumTopAppBar( + modifier = modifier.fillMaxWidth(), + title = { + Text( + text = context.getString(R.string.app_name), + style = MaterialTheme.typography.titleLarge + ) + }, + navigationIcon = { + AnimatedVisibility( + visible = viewModel.selectedNotes.isEmpty(), + ) { + Row() { + IconButton(onClick = { profileButtonClicked = !profileButtonClicked }) { + Icon( + imageVector = Icons.Default.Person, + contentDescription = context.getString(R.string.lb_account) + ) + } + AnimatedVisibility(visible = profileButtonClicked) { + LazyRow { + item { + Spacer(modifier = Modifier.padding(4.dp)) + Button( + modifier = Modifier.padding(end = 4.dp), + onClick = if (viewModel.isUserAuth) { + viewModel.openProfile + } else { + viewModel.openAuthPhoneNumber + } + ) { + Text(text = context.getString(R.string.lb_short_shortcut_profile)) + } } - } - item { - Spacer(modifier = Modifier.padding(4.dp)) - Button(modifier = Modifier.padding(end = 4.dp), onClick = {}) { - Text(text = "Settings") + item { + Spacer(modifier = Modifier.padding(4.dp)) + Button(modifier = Modifier.padding(end = 4.dp), onClick = {}) { + Text(text = "Settings") + } } } } } } - } - }, actions = noteListActions(selectedNotes = selectedNotes), scrollBehavior = scrollBehavior + }, + actions = noteListActions(selectedNotes = viewModel.selectedNotes), + scrollBehavior = scrollBehavior ) } diff --git a/feature-todo/src/main/java/com/stslex/feature_todo/ui/ToDoContent.kt b/feature-todo/src/main/java/com/stslex/feature_todo/ui/ToDoContent.kt new file mode 100644 index 0000000..71c1742 --- /dev/null +++ b/feature-todo/src/main/java/com/stslex/feature_todo/ui/ToDoContent.kt @@ -0,0 +1,96 @@ +package com.stslex.feature_todo.ui + +import android.content.res.Configuration +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.wrapContentSize +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.tooling.preview.Preview +import com.stslex.core_ui.theme.AppTheme +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlin.math.cos +import kotlin.math.sin + +/** + * Just screen for trying + * some cool animations + * */ +@Composable +fun ToDoContent( + modifier: Modifier = Modifier +) { + Surface( + modifier = modifier.fillMaxSize() + ) { + Animation() + } +} + +@Composable +private fun Animation( + modifier: Modifier = Modifier, +) { + val deltaSize = remember { mutableStateOf(0f) } + val color = MaterialTheme.colorScheme.onBackground + val circleNum = 6 + val circleConst = 360f / circleNum + val circleSpeed = 0.01f + val ellipseSpeed = 3.375f + val ellipseRadiusDeltaStart = 7 + val ellipseRadius = 4 * 20 + val circleRadius = 100 + Canvas( + modifier = modifier.wrapContentSize() + ) { + val currentTime = deltaSize.value + val startOffset = Offset( + x = (1 * ellipseRadiusDeltaStart * sin(currentTime) + sin(currentTime * ellipseSpeed)) * ellipseRadius, + y = (5 * ellipseRadiusDeltaStart * sin(currentTime) + cos(currentTime * ellipseSpeed)) * ellipseRadius + ) + for (n in 1 until circleNum) { + val currentOffset = Offset( + x = startOffset.x + circleRadius * cos(currentTime * n * circleConst * circleSpeed), + y = startOffset.y + circleRadius * sin(currentTime * n * circleConst * circleSpeed) + ) + drawCircle( + center = currentOffset, + color = color, + radius = 3f + ) + } + } + + LaunchedEffect(true) { + launch { + while (true) { + deltaSize.value = deltaSize.value + 0.001f + delay(1) + } + } + } +} + + +@Preview( + uiMode = Configuration.UI_MODE_NIGHT_YES or Configuration.UI_MODE_TYPE_NORMAL, + showSystemUi = true, showBackground = true, device = "id:pixel_6" +) +@Composable +fun ToDoContentPreview() { + AppTheme( + isSystemInDarkTheme(), + dynamicColor = true + ) { + ToDoContent() + } + +} \ No newline at end of file diff --git a/feature-todo/src/main/java/com/stslex/feature_todo/ui/TodoRoute.kt b/feature-todo/src/main/java/com/stslex/feature_todo/ui/TodoRoute.kt index 32a1790..862fc26 100644 --- a/feature-todo/src/main/java/com/stslex/feature_todo/ui/TodoRoute.kt +++ b/feature-todo/src/main/java/com/stslex/feature_todo/ui/TodoRoute.kt @@ -4,7 +4,11 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Create -import androidx.compose.material3.* +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.Icon +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Surface import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -29,7 +33,7 @@ fun ToDoScreen() { .fillMaxSize() .padding(it) ) { - + ToDoContent() } } } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d2fb8ff..617a818 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ accompanist = "0.24.13-rc" activityCompose = "1.6.0-alpha05" androidCore = "1.9.0-alpha05" androidDesugarJdkLibs = "1.2.0" -androidGradlePlugin = "7.4.0-alpha08" +androidGradlePlugin = "8.0.0-alpha04" androidJunit = "1.1.4-alpha07" androidxCompose = "1.3.0-alpha01" composeCompiler = "1.2.0" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 35ca3da..ac845cc 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Tue Mar 29 18:16:11 MSK 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-rc-3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME