From ae070bc42a026a07066725afd7ad6229b72b4249 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Fri, 28 Apr 2023 18:54:20 +0200 Subject: [PATCH 01/82] start --- .../nl/tiebe/otarium/ui/home/BottomBar.kt | 4 + .../nl/tiebe/otarium/ui/home/HomeComponent.kt | 13 ++ .../tiebe/otarium/ui/home/elo/ELOComponent.kt | 65 ++++++++++ .../nl/tiebe/otarium/ui/home/elo/ELOScreen.kt | 113 ++++++++++++++++++ .../assignments/AssignmentsChildComponent.kt | 12 ++ .../assignments/AssignmentsChildScreen.kt | 9 ++ .../LearningResourcesChildComponent.kt | 12 ++ .../LearningResourcesChildScreen.kt | 9 ++ .../studyguides/StudyGuidesChildComponent.kt | 12 ++ .../studyguides/StudyGuidesChildScreen.kt | 9 ++ .../commonMain/resources/MR/base/strings.xml | 5 + .../icons/bottombar/book_open_filled.svg | 1 + .../icons/bottombar/book_open_outline.svg | 1 + .../commonMain/resources/MR/nl/strings.xml | 5 + 14 files changed, 270 insertions(+) create mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/ELOComponent.kt create mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/ELOScreen.kt create mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/AssignmentsChildComponent.kt create mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/AssignmentsChildScreen.kt create mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/learningresources/LearningResourcesChildComponent.kt create mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/learningresources/LearningResourcesChildScreen.kt create mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/StudyGuidesChildComponent.kt create mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/StudyGuidesChildScreen.kt create mode 100644 shared/src/commonMain/resources/MR/images/icons/bottombar/book_open_filled.svg create mode 100644 shared/src/commonMain/resources/MR/images/icons/bottombar/book_open_outline.svg diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/BottomBar.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/BottomBar.kt index a777dd4f..5bbad635 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/BottomBar.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/BottomBar.kt @@ -9,6 +9,8 @@ import androidx.compose.ui.Modifier import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState import nl.tiebe.otarium.ui.home.debug.DebugComponent import nl.tiebe.otarium.ui.home.debug.DebugScreen +import nl.tiebe.otarium.ui.home.elo.ELOComponent +import nl.tiebe.otarium.ui.home.elo.ELOScreen import nl.tiebe.otarium.ui.home.grades.GradesComponent import nl.tiebe.otarium.ui.home.grades.GradesScreen import nl.tiebe.otarium.ui.home.messages.MessagesComponent @@ -32,6 +34,7 @@ internal fun BottomBar( HomeComponent.MenuItem.Timetable, HomeComponent.MenuItem.Grades, HomeComponent.MenuItem.Messages, + HomeComponent.MenuItem.ELO, HomeComponent.MenuItem.Settings ) @@ -56,6 +59,7 @@ internal fun BottomBar( is TimetableComponent -> TimetableScreen(dialogComponent) is GradesComponent -> GradesScreen(dialogComponent) is MessagesComponent -> MessagesScreen(dialogComponent) + is ELOComponent -> ELOScreen(dialogComponent) is SettingsComponent -> SettingsScreen(dialogComponent) is DebugComponent -> DebugScreen(dialogComponent) } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/HomeComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/HomeComponent.kt index 344d73eb..62781444 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/HomeComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/HomeComponent.kt @@ -14,6 +14,7 @@ import dev.icerock.moko.resources.StringResource import dev.icerock.moko.resources.compose.painterResource import nl.tiebe.otarium.MR import nl.tiebe.otarium.ui.home.debug.DefaultDebugComponent +import nl.tiebe.otarium.ui.home.elo.DefaultELOComponent import nl.tiebe.otarium.ui.home.grades.DefaultGradesComponent import nl.tiebe.otarium.ui.home.messages.DefaultMessagesComponent import nl.tiebe.otarium.ui.home.settings.DefaultSettingsComponent @@ -43,6 +44,12 @@ interface HomeComponent { { Icon(painterResource(MR.images.email_filled), "Messages") } ) + object ELO: MenuItem( + MR.strings.eloItem, + { Icon(painterResource(MR.images.book_open_outline), "ELO") }, + { Icon(painterResource(MR.images.book_open_filled), "ELO") } + ) + object Settings: MenuItem( MR.strings.settings_title, { Icon(painterResource(MR.images.cog_outline), "Settings") }, @@ -73,6 +80,7 @@ class DefaultHomeComponent(componentContext: ComponentContext, override val navi is HomeComponent.MenuItem.Timetable -> timetableComponent(componentContext) is HomeComponent.MenuItem.Grades -> gradesComponent(componentContext) is HomeComponent.MenuItem.Messages -> messagesComponent(componentContext) + is HomeComponent.MenuItem.ELO -> eloComponent(componentContext) is HomeComponent.MenuItem.Settings -> settingsComponent(componentContext) is HomeComponent.MenuItem.Debug -> debugComponent(componentContext) } @@ -94,6 +102,11 @@ class DefaultHomeComponent(componentContext: ComponentContext, override val navi componentContext = componentContext ) + private fun eloComponent(componentContext: ComponentContext) = + DefaultELOComponent( + componentContext = componentContext + ) + private fun settingsComponent(componentContext: ComponentContext) = DefaultSettingsComponent( componentContext = componentContext, diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/ELOComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/ELOComponent.kt new file mode 100644 index 00000000..682b10bb --- /dev/null +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/ELOComponent.kt @@ -0,0 +1,65 @@ +package nl.tiebe.otarium.ui.home.elo + +import com.arkivanov.decompose.ComponentContext +import com.arkivanov.decompose.value.MutableValue +import com.arkivanov.decompose.value.Value +import com.arkivanov.essenty.parcelable.Parcelable +import com.arkivanov.essenty.parcelable.Parcelize +import nl.tiebe.otarium.ui.home.MenuItemComponent +import nl.tiebe.otarium.ui.home.elo.children.assignments.AssignmentsChildComponent +import nl.tiebe.otarium.ui.home.elo.children.assignments.DefaultAssignmentsChildComponent +import nl.tiebe.otarium.ui.home.elo.children.learningresources.DefaultLearningResourcesChildComponent +import nl.tiebe.otarium.ui.home.elo.children.learningresources.LearningResourcesChildComponent +import nl.tiebe.otarium.ui.home.elo.children.studyguides.DefaultStudyGuidesChildComponent +import nl.tiebe.otarium.ui.home.elo.children.studyguides.StudyGuidesChildComponent + +interface ELOComponent : MenuItemComponent { + @Parcelize + sealed class ELOChild(val id: Int): Parcelable { + object StudyGuides : ELOChild(0) + object Assignments : ELOChild(1) + object LearningResources : ELOChild(2) + + } + + val studyGuidesComponent: StudyGuidesChildComponent + val assignmentsComponent: AssignmentsChildComponent + val learningResourcesComponent: LearningResourcesChildComponent + + val currentPage: Value + + fun changeChild(eloChild: ELOChild) +} + +class DefaultELOComponent( + componentContext: ComponentContext +): ELOComponent, ComponentContext by componentContext { + private fun studyGuidesComponent(componentContext: ComponentContext) = + DefaultStudyGuidesChildComponent( + componentContext = componentContext + ) + + private fun assignmentsComponent(componentContext: ComponentContext) = + DefaultAssignmentsChildComponent( + componentContext = componentContext + ) + + private fun learningResourcesComponent(componentContext: ComponentContext) = + DefaultLearningResourcesChildComponent( + componentContext = componentContext + ) + + override val studyGuidesComponent: StudyGuidesChildComponent = studyGuidesComponent(componentContext) + override val assignmentsComponent: AssignmentsChildComponent = assignmentsComponent(componentContext) + override val learningResourcesComponent: LearningResourcesChildComponent = learningResourcesComponent(componentContext) + + override val currentPage: MutableValue = MutableValue(0) + + + override fun changeChild(eloChild: ELOComponent.ELOChild) { + currentPage.value = eloChild.id + } + +} + +interface ELOChildComponent \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/ELOScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/ELOScreen.kt new file mode 100644 index 00000000..b948125e --- /dev/null +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/ELOScreen.kt @@ -0,0 +1,113 @@ +package nl.tiebe.otarium.ui.home.elo + +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.pager.HorizontalPager +import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.material3.Tab +import androidx.compose.material3.TabRow +import androidx.compose.material3.TabRowDefaults +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import kotlinx.coroutines.launch +import nl.tiebe.otarium.MR +import nl.tiebe.otarium.ui.home.elo.children.assignments.AssignmentsChildScreen +import nl.tiebe.otarium.ui.home.elo.children.learningresources.LearningResourcesChildScreen +import nl.tiebe.otarium.ui.home.elo.children.studyguides.StudyGuidesChildScreen +import nl.tiebe.otarium.ui.utils.tabIndicatorOffset +import nl.tiebe.otarium.utils.ui.getLocalizedString + +@OptIn(ExperimentalFoundationApi::class) +@Composable +internal fun ELOScreen(component: ELOComponent) { + val pagerState = rememberPagerState() + val coroutineScope = rememberCoroutineScope() + + Column(modifier = Modifier.fillMaxSize()) { + TabRow( + selectedTabIndex = pagerState.currentPage, + indicator = { tabPositions -> + TabRowDefaults.Indicator( + Modifier.tabIndicatorOffset( + pagerState, + 3, + tabPositions, + shouldShowIndicator = true, + pagerState.currentPage + ) + ) + } + ) { + Tab( + selected = pagerState.currentPage == 0, + onClick = { component.changeChild(ELOComponent.ELOChild.StudyGuides) }, + modifier = Modifier.height(53.dp) + ) { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + Text( + text = getLocalizedString(MR.strings.study_guides), + maxLines = 1, + overflow = TextOverflow.Clip, + textAlign = TextAlign.Center, + fontSize = 13.sp + ) + } + } + + Tab( + selected = pagerState.currentPage == 1, + onClick = { component.changeChild(ELOComponent.ELOChild.Assignments) }, + modifier = Modifier.height(53.dp) + ) { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + Text( + text = getLocalizedString(MR.strings.assignments), + maxLines = 1, + overflow = TextOverflow.Clip, + textAlign = TextAlign.Center, + fontSize = 13.sp + ) + } + } + + Tab( + selected = pagerState.currentPage == 2, + onClick = { component.changeChild(ELOComponent.ELOChild.LearningResources) }, + modifier = Modifier.height(53.dp) + ) { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + Text( + text = getLocalizedString(MR.strings.learning_resources), + maxLines = 1, + overflow = TextOverflow.Clip, + textAlign = TextAlign.Center, + fontSize = 13.sp + ) + } + } + } + + HorizontalPager(pageCount = 3, state = pagerState, beyondBoundsPageCount = 2) { page -> + when (page) { + 0 -> StudyGuidesChildScreen(component.studyGuidesComponent) + 1 -> AssignmentsChildScreen(component.assignmentsComponent) + 2 -> LearningResourcesChildScreen(component.learningResourcesComponent) + } + } + + component.currentPage.subscribe { + coroutineScope.launch { + pagerState.animateScrollToPage(it) + } + } + } +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/AssignmentsChildComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/AssignmentsChildComponent.kt new file mode 100644 index 00000000..c9cfbaf0 --- /dev/null +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/AssignmentsChildComponent.kt @@ -0,0 +1,12 @@ +package nl.tiebe.otarium.ui.home.elo.children.assignments + +import com.arkivanov.decompose.ComponentContext +import nl.tiebe.otarium.ui.home.elo.ELOChildComponent + +interface AssignmentsChildComponent : ELOChildComponent { + +} + +class DefaultAssignmentsChildComponent(componentContext: ComponentContext) : AssignmentsChildComponent, ComponentContext by componentContext { + +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/AssignmentsChildScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/AssignmentsChildScreen.kt new file mode 100644 index 00000000..7d8a8fdb --- /dev/null +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/AssignmentsChildScreen.kt @@ -0,0 +1,9 @@ +package nl.tiebe.otarium.ui.home.elo.children.assignments + +import androidx.compose.runtime.Composable + +@Composable +internal fun AssignmentsChildScreen(component: AssignmentsChildComponent) { + + +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/learningresources/LearningResourcesChildComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/learningresources/LearningResourcesChildComponent.kt new file mode 100644 index 00000000..464629ca --- /dev/null +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/learningresources/LearningResourcesChildComponent.kt @@ -0,0 +1,12 @@ +package nl.tiebe.otarium.ui.home.elo.children.learningresources + +import com.arkivanov.decompose.ComponentContext +import nl.tiebe.otarium.ui.home.elo.ELOChildComponent + +interface LearningResourcesChildComponent : ELOChildComponent { + +} + +class DefaultLearningResourcesChildComponent(componentContext: ComponentContext) : LearningResourcesChildComponent, ComponentContext by componentContext { + +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/learningresources/LearningResourcesChildScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/learningresources/LearningResourcesChildScreen.kt new file mode 100644 index 00000000..803971fc --- /dev/null +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/learningresources/LearningResourcesChildScreen.kt @@ -0,0 +1,9 @@ +package nl.tiebe.otarium.ui.home.elo.children.learningresources + +import androidx.compose.runtime.Composable + +@Composable +internal fun LearningResourcesChildScreen(component: LearningResourcesChildComponent) { + + +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/StudyGuidesChildComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/StudyGuidesChildComponent.kt new file mode 100644 index 00000000..531d41f6 --- /dev/null +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/StudyGuidesChildComponent.kt @@ -0,0 +1,12 @@ +package nl.tiebe.otarium.ui.home.elo.children.studyguides + +import com.arkivanov.decompose.ComponentContext +import nl.tiebe.otarium.ui.home.elo.ELOChildComponent + +interface StudyGuidesChildComponent : ELOChildComponent { + +} + +class DefaultStudyGuidesChildComponent(componentContext: ComponentContext) : StudyGuidesChildComponent, ComponentContext by componentContext { + +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/StudyGuidesChildScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/StudyGuidesChildScreen.kt new file mode 100644 index 00000000..82a64c6e --- /dev/null +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/StudyGuidesChildScreen.kt @@ -0,0 +1,9 @@ +package nl.tiebe.otarium.ui.home.elo.children.studyguides + +import androidx.compose.runtime.Composable + +@Composable +internal fun StudyGuidesChildScreen(component: StudyGuidesChildComponent) { + + +} \ No newline at end of file diff --git a/shared/src/commonMain/resources/MR/base/strings.xml b/shared/src/commonMain/resources/MR/base/strings.xml index c022a554..36e5c8e9 100644 --- a/shared/src/commonMain/resources/MR/base/strings.xml +++ b/shared/src/commonMain/resources/MR/base/strings.xml @@ -56,4 +56,9 @@ To CC BCC + + ELO + Study guides + Assignments + Learning resources \ No newline at end of file diff --git a/shared/src/commonMain/resources/MR/images/icons/bottombar/book_open_filled.svg b/shared/src/commonMain/resources/MR/images/icons/bottombar/book_open_filled.svg new file mode 100644 index 00000000..b0cbc997 --- /dev/null +++ b/shared/src/commonMain/resources/MR/images/icons/bottombar/book_open_filled.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/shared/src/commonMain/resources/MR/images/icons/bottombar/book_open_outline.svg b/shared/src/commonMain/resources/MR/images/icons/bottombar/book_open_outline.svg new file mode 100644 index 00000000..a139d796 --- /dev/null +++ b/shared/src/commonMain/resources/MR/images/icons/bottombar/book_open_outline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/shared/src/commonMain/resources/MR/nl/strings.xml b/shared/src/commonMain/resources/MR/nl/strings.xml index 433dd42d..d7a53433 100644 --- a/shared/src/commonMain/resources/MR/nl/strings.xml +++ b/shared/src/commonMain/resources/MR/nl/strings.xml @@ -54,4 +54,9 @@ Aan CC BCC + + ELO + Studiewijzers + Opdrachten + Leermiddelen \ No newline at end of file From 01463c0ff19ea1a2eeb9645e1d1a850158fc6216 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Fri, 28 Apr 2023 18:59:01 +0200 Subject: [PATCH 02/82] study guide --- .../home/elo/children/studyguides/StudyGuidesChildComponent.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/StudyGuidesChildComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/StudyGuidesChildComponent.kt index 531d41f6..2bd80f4c 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/StudyGuidesChildComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/StudyGuidesChildComponent.kt @@ -8,5 +8,4 @@ interface StudyGuidesChildComponent : ELOChildComponent { } class DefaultStudyGuidesChildComponent(componentContext: ComponentContext) : StudyGuidesChildComponent, ComponentContext by componentContext { - } \ No newline at end of file From 7c5bc1c4192e2dea0326f193149f254f28601844 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Fri, 28 Apr 2023 19:00:39 +0200 Subject: [PATCH 03/82] assignments --- .../home/elo/children/assignments/AssignmentsChildComponent.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/AssignmentsChildComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/AssignmentsChildComponent.kt index c9cfbaf0..f4bef295 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/AssignmentsChildComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/AssignmentsChildComponent.kt @@ -8,5 +8,4 @@ interface AssignmentsChildComponent : ELOChildComponent { } class DefaultAssignmentsChildComponent(componentContext: ComponentContext) : AssignmentsChildComponent, ComponentContext by componentContext { - } \ No newline at end of file From 5f8387188c6a2c63834e801dbb4715cfe947e2c7 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Fri, 28 Apr 2023 19:01:10 +0200 Subject: [PATCH 04/82] learning resources --- .../learningresources/LearningResourcesChildComponent.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/learningresources/LearningResourcesChildComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/learningresources/LearningResourcesChildComponent.kt index 464629ca..8dce0fff 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/learningresources/LearningResourcesChildComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/learningresources/LearningResourcesChildComponent.kt @@ -8,5 +8,4 @@ interface LearningResourcesChildComponent : ELOChildComponent { } class DefaultLearningResourcesChildComponent(componentContext: ComponentContext) : LearningResourcesChildComponent, ComponentContext by componentContext { - } \ No newline at end of file From d47be3b396e9ba05b8e2fa4988bceb414c386176 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Fri, 28 Apr 2023 20:33:17 +0200 Subject: [PATCH 05/82] progress --- .../nl/tiebe/otarium/ui/home/elo/ELOScreen.kt | 2 +- .../studyguides/StudyGuidesChildComponent.kt | 115 +++++++++++++++++- .../studyguides/StudyGuidesChildScreen.kt | 16 ++- .../folder/StudyGuideFolderComponent.kt | 13 ++ .../folder/StudyGuideFolderScreen.kt | 8 ++ .../listscreen/StudyGuideListComponent.kt | 55 +++++++++ .../listscreen/StudyGuideListItem.kt | 18 +++ .../listscreen/StudyGuideListScreen.kt | 29 +++++ .../otarium/ui/home/grades/GradeScreen.kt | 2 +- .../kotlin/nl/tiebe/otarium/utils/Utils.kt | 41 ++++++- 10 files changed, 289 insertions(+), 10 deletions(-) create mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderComponent.kt create mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderScreen.kt create mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/listscreen/StudyGuideListComponent.kt create mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/listscreen/StudyGuideListItem.kt create mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/listscreen/StudyGuideListScreen.kt diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/ELOScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/ELOScreen.kt index b948125e..4774963a 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/ELOScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/ELOScreen.kt @@ -96,7 +96,7 @@ internal fun ELOScreen(component: ELOComponent) { } } - HorizontalPager(pageCount = 3, state = pagerState, beyondBoundsPageCount = 2) { page -> + HorizontalPager(pageCount = 3, state = pagerState, beyondBoundsPageCount = 2, modifier = Modifier.fillMaxSize()) { page -> when (page) { 0 -> StudyGuidesChildScreen(component.studyGuidesComponent) 1 -> AssignmentsChildScreen(component.assignmentsComponent) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/StudyGuidesChildComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/StudyGuidesChildComponent.kt index 2bd80f4c..50854d1a 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/StudyGuidesChildComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/StudyGuidesChildComponent.kt @@ -1,11 +1,124 @@ package nl.tiebe.otarium.ui.home.elo.children.studyguides import com.arkivanov.decompose.ComponentContext +import com.arkivanov.decompose.router.stack.ChildStack +import com.arkivanov.decompose.router.stack.StackNavigation +import com.arkivanov.decompose.router.stack.childStack +import com.arkivanov.decompose.router.stack.push +import com.arkivanov.decompose.value.MutableValue +import com.arkivanov.decompose.value.Value +import com.arkivanov.essenty.parcelable.Parcelable +import com.arkivanov.essenty.parcelable.Parcelize +import dev.tiebe.magisterapi.api.messages.MessageFlow +import dev.tiebe.magisterapi.response.messages.MessageFolder +import io.ktor.http.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import nl.tiebe.otarium.Data import nl.tiebe.otarium.ui.home.elo.ELOChildComponent +import nl.tiebe.otarium.ui.home.elo.children.studyguides.folder.DefaultStudyGuideFolderComponent +import nl.tiebe.otarium.ui.home.elo.children.studyguides.folder.StudyGuideFolderComponent +import nl.tiebe.otarium.ui.home.elo.children.studyguides.listscreen.DefaultStudyGuideListComponent +import nl.tiebe.otarium.ui.home.elo.children.studyguides.listscreen.StudyGuideListComponent +import nl.tiebe.otarium.ui.root.componentCoroutineScope interface StudyGuidesChildComponent : ELOChildComponent { + val navigation: StackNavigation + val childStack: Value> + + val refreshState: Value + val scope: CoroutineScope + + suspend fun getFoldersAsync() + fun getFolders() + + fun navigate(child: Config) { + navigation.push(child) + } + + fun navigateToFolder(folder: MessageFolder) { + navigate(Config.Folder(folder.id)) + } + + val folders: Value> + + sealed class Child { + class StudyGuideListChild(val component: StudyGuideListComponent) : Child() + class FolderChild(val component: StudyGuideFolderComponent) : Child() + } + + sealed class Config : Parcelable { + @Parcelize + object StudyGuideList : Config() + + @Parcelize + data class Folder(val folderId: Int) : Config() + } } class DefaultStudyGuidesChildComponent(componentContext: ComponentContext) : StudyGuidesChildComponent, ComponentContext by componentContext { -} \ No newline at end of file + override val refreshState: MutableValue = MutableValue(false) + + override val scope: CoroutineScope = componentCoroutineScope() + override val folders: MutableValue> = MutableValue(listOf()) + + override val navigation = StackNavigation() + + override val childStack: Value> = + childStack( + source = navigation, + initialConfiguration = StudyGuidesChildComponent.Config.StudyGuideList, + handleBackButton = true, // Pop the back stack on back button press + childFactory = ::createChild, + ) + + private fun createChild(config: StudyGuidesChildComponent.Config, componentContext: ComponentContext): StudyGuidesChildComponent.Child = + when (config) { + is StudyGuidesChildComponent.Config.StudyGuideList -> StudyGuidesChildComponent.Child.StudyGuideListChild(createStudyGuideListComponent(this)) + is StudyGuidesChildComponent.Config.Folder -> StudyGuidesChildComponent.Child.FolderChild(createFolderComponent(componentContext, let { if (folders.value.isEmpty()) runBlocking { getFoldersAsync() }; folders.value.first { it.id == config.folderId } })) + } + + private fun createStudyGuideListComponent(componentContext: ComponentContext) = + DefaultStudyGuideListComponent( + componentContext = componentContext, + parentComponent = this + ) + + + private fun createFolderComponent(componentContext: ComponentContext, folder: MessageFolder) = + DefaultStudyGuideFolderComponent( + componentContext = componentContext, + ) + + override suspend fun getFoldersAsync() { + refreshState.value = true + + try { + folders.value = + MessageFlow.getAllFolders( + Url(Data.selectedAccount.tenantUrl), + Data.selectedAccount.tokens.accessToken + ) + } catch (e: Exception) { + e.printStackTrace() + } + + refreshState.value = false + } + + override fun getFolders() { + scope.launch { + getFoldersAsync() + } + } + + init { + scope.launch { + getFolders() + } + } +} + +interface StudyGuideChildScreen \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/StudyGuidesChildScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/StudyGuidesChildScreen.kt index 82a64c6e..1256b763 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/StudyGuidesChildScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/StudyGuidesChildScreen.kt @@ -1,9 +1,23 @@ package nl.tiebe.otarium.ui.home.elo.children.studyguides +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.padding +import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState +import nl.tiebe.otarium.ui.home.elo.children.studyguides.folder.StudyGuideFolderScreen +import nl.tiebe.otarium.ui.home.elo.children.studyguides.listscreen.StudyGuideListScreen @Composable internal fun StudyGuidesChildScreen(component: StudyGuidesChildComponent) { + val child = component.childStack.subscribeAsState().value - + Box(modifier = Modifier.padding(start = 5.dp, end = 5.dp)) { + when (val screen = child.active.instance) { + is StudyGuidesChildComponent.Child.StudyGuideListChild -> StudyGuideListScreen(screen.component) + is StudyGuidesChildComponent.Child.FolderChild -> StudyGuideFolderScreen(screen.component) + } + } } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderComponent.kt new file mode 100644 index 00000000..b3a60133 --- /dev/null +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderComponent.kt @@ -0,0 +1,13 @@ +package nl.tiebe.otarium.ui.home.elo.children.studyguides.folder + +import com.arkivanov.decompose.ComponentContext + +interface StudyGuideFolderComponent { + +} + +class DefaultStudyGuideFolderComponent(componentContext: ComponentContext) : StudyGuideFolderComponent, ComponentContext by componentContext { + + + +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderScreen.kt new file mode 100644 index 00000000..0b9ac4f3 --- /dev/null +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderScreen.kt @@ -0,0 +1,8 @@ +package nl.tiebe.otarium.ui.home.elo.children.studyguides.folder + +import androidx.compose.runtime.Composable + +@Composable +internal fun StudyGuideFolderScreen(component: StudyGuideFolderComponent) { + +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/listscreen/StudyGuideListComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/listscreen/StudyGuideListComponent.kt new file mode 100644 index 00000000..beab39f7 --- /dev/null +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/listscreen/StudyGuideListComponent.kt @@ -0,0 +1,55 @@ +package nl.tiebe.otarium.ui.home.elo.children.studyguides.listscreen + +import com.arkivanov.decompose.ComponentContext +import com.arkivanov.decompose.value.MutableValue +import com.arkivanov.decompose.value.Value +import dev.tiebe.magisterapi.api.StudyGuideFlow +import dev.tiebe.magisterapi.response.studyguide.StudyGuide +import io.ktor.http.* +import kotlinx.coroutines.launch +import kotlinx.datetime.Clock +import kotlinx.datetime.TimeZone +import kotlinx.datetime.toLocalDateTime +import nl.tiebe.otarium.Data +import nl.tiebe.otarium.ui.home.elo.children.studyguides.StudyGuideChildScreen +import nl.tiebe.otarium.ui.home.elo.children.studyguides.StudyGuidesChildComponent +import nl.tiebe.otarium.ui.root.componentCoroutineScope +import nl.tiebe.otarium.utils.toFormattedString + +interface StudyGuideListComponent : StudyGuideChildScreen { + val studyGuides: Value> + val parentComponent: StudyGuidesChildComponent + + val isRefreshing: Value + + fun refreshStudyGuides() } + +class DefaultStudyGuideListComponent( + componentContext: ComponentContext, + override val parentComponent: StudyGuidesChildComponent +) : StudyGuideListComponent, ComponentContext by componentContext { + override val studyGuides: MutableValue> = MutableValue(listOf()) + + override val isRefreshing: MutableValue = MutableValue(false) + + val scope = componentCoroutineScope() + + override fun refreshStudyGuides() { + scope.launch { + isRefreshing.value = true + val date = Clock.System.now().toLocalDateTime(TimeZone.of("Europe/Amsterdam")) + + studyGuides.value = StudyGuideFlow.getFullStudyGuideList( + Url(Data.selectedAccount.tenantUrl), + Data.selectedAccount.tokens.accessToken, + Data.selectedAccount.accountId, + "${date.year.toFormattedString()}-${date.monthNumber.toFormattedString()}-${date.dayOfMonth.toFormattedString()}" + ) + isRefreshing.value = false + } + } + + init { + refreshStudyGuides() + } +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/listscreen/StudyGuideListItem.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/listscreen/StudyGuideListItem.kt new file mode 100644 index 00000000..c64555ad --- /dev/null +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/listscreen/StudyGuideListItem.kt @@ -0,0 +1,18 @@ +package nl.tiebe.otarium.ui.home.elo.children.studyguides.listscreen + +import androidx.compose.foundation.clickable +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ListItem +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import dev.tiebe.magisterapi.response.studyguide.StudyGuide + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +internal fun StudyGuideListItem(component: StudyGuideListComponent, item: StudyGuide) { + ListItem( + headlineText = { Text(item.title) }, + modifier = Modifier.clickable { }//component.openStudyGuide(item) } + ) +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/listscreen/StudyGuideListScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/listscreen/StudyGuideListScreen.kt new file mode 100644 index 00000000..26ea00b9 --- /dev/null +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/listscreen/StudyGuideListScreen.kt @@ -0,0 +1,29 @@ +package nl.tiebe.otarium.ui.home.elo.children.studyguides.listscreen + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.Divider +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.pullrefresh.pullRefresh +import androidx.compose.material.pullrefresh.rememberPullRefreshState +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState + +@OptIn(ExperimentalMaterialApi::class) +@Composable +internal fun StudyGuideListScreen(component: StudyGuideListComponent) { + val studyGuideItems = component.studyGuides.subscribeAsState().value + val scrollState = rememberScrollState() + val pullRefreshState = rememberPullRefreshState(component.isRefreshing.subscribeAsState().value, onRefresh = component::refreshStudyGuides) + + Column(Modifier.fillMaxSize().pullRefresh(pullRefreshState).verticalScroll(scrollState)) { + studyGuideItems.forEach { + StudyGuideListItem(component, it) + + Divider() + } + } +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/GradeScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/GradeScreen.kt index aec97b5e..b69e5118 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/GradeScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/GradeScreen.kt @@ -79,7 +79,7 @@ internal fun GradesScreen(component: GradesComponent) { } } - HorizontalPager(pageCount = 2, state = pagerState, beyondBoundsPageCount = 1) { page -> + HorizontalPager(pageCount = 2, state = pagerState, beyondBoundsPageCount = 1, modifier = Modifier.fillMaxSize()) { page -> when (page) { 0 -> RecentGradesChild(component.recentGradeComponent) 1 -> GradeCalculationChild(component.calculationChildComponent) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/Utils.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/Utils.kt index a3fee884..9dee74f9 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/Utils.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/Utils.kt @@ -1,10 +1,7 @@ package nl.tiebe.otarium.utils import io.ktor.utils.io.* -import kotlinx.datetime.LocalDateTime -import kotlinx.datetime.TimeZone -import kotlinx.datetime.toInstant -import kotlinx.datetime.toLocalDateTime +import kotlinx.datetime.* expect fun copyToClipboard(text: String) @@ -17,7 +14,39 @@ expect fun getDownloadFileLocation(id: String, fileName: String): ByteWriteChann expect fun openFileFromCache(id: String, fileName: String) fun LocalDateTime.toFormattedString(): String { - val dateTime = this.toInstant(TimeZone.UTC).toLocalDateTime(TimeZone.of("Europe/Amsterdam")) + val dateTime = this.toInstant(TimeZone.UTC) - return "${dateTime.dayOfMonth}-${dateTime.monthNumber}-${dateTime.year} ${dateTime.hour}:${dateTime.minute}:${dateTime.second}" + return dateTime.toFormattedString() +} + +fun LocalDateTime.toFormattedStringDate(): String { + val dateTime = this.toInstant(TimeZone.UTC) + + return dateTime.toFormattedStringDate() +} + +fun LocalDateTime.toFormattedStringTime(): String { + val dateTime = this.toInstant(TimeZone.UTC) + + return dateTime.toFormattedStringTime() +} + +fun Instant.toFormattedString(): String { + return this.toFormattedStringDate() + " " + this.toFormattedStringTime() +} + +fun Instant.toFormattedStringDate(): String { + val dateTime = this.toLocalDateTime(TimeZone.of("Europe/Amsterdam")) + + return "${dateTime.dayOfMonth.toFormattedString()}-${dateTime.monthNumber.toFormattedString()}-${dateTime.year.toFormattedString()}" +} + +fun Instant.toFormattedStringTime(): String { + val dateTime = this.toLocalDateTime(TimeZone.of("Europe/Amsterdam")) + + return "${dateTime.hour.toFormattedString()}:${dateTime.minute.toFormattedString()}:${dateTime.second.toFormattedString()}" +} + +fun Int.toFormattedString(): String { + return if (this < 10) "0$this" else "$this" } \ No newline at end of file From 4413ed082d24ec7e71f70712c99540003899a3fe Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Sat, 29 Apr 2023 13:23:52 +0200 Subject: [PATCH 06/82] progress --- .../studyguides/StudyGuidesChildComponent.kt | 53 +++---------------- .../folder/StudyGuideFolderComponent.kt | 39 +++++++++++++- .../folder/StudyGuideFolderItem.kt | 40 ++++++++++++++ .../folder/StudyGuideFolderScreen.kt | 12 +++++ .../folder/StudyGuideResourceListItem.kt | 15 ++++++ .../listscreen/StudyGuideListComponent.kt | 7 ++- .../listscreen/StudyGuideListItem.kt | 2 +- .../listscreen/StudyGuideListScreen.kt | 2 +- .../nl/tiebe/otarium/ui/utils/HtmlHandler.kt | 6 +++ 9 files changed, 126 insertions(+), 50 deletions(-) create mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderItem.kt create mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideResourceListItem.kt diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/StudyGuidesChildComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/StudyGuidesChildComponent.kt index 50854d1a..8707e903 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/StudyGuidesChildComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/StudyGuidesChildComponent.kt @@ -9,13 +9,8 @@ import com.arkivanov.decompose.value.MutableValue import com.arkivanov.decompose.value.Value import com.arkivanov.essenty.parcelable.Parcelable import com.arkivanov.essenty.parcelable.Parcelize -import dev.tiebe.magisterapi.api.messages.MessageFlow -import dev.tiebe.magisterapi.response.messages.MessageFolder -import io.ktor.http.* +import dev.tiebe.magisterapi.response.studyguide.StudyGuide import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking -import nl.tiebe.otarium.Data import nl.tiebe.otarium.ui.home.elo.ELOChildComponent import nl.tiebe.otarium.ui.home.elo.children.studyguides.folder.DefaultStudyGuideFolderComponent import nl.tiebe.otarium.ui.home.elo.children.studyguides.folder.StudyGuideFolderComponent @@ -30,18 +25,11 @@ interface StudyGuidesChildComponent : ELOChildComponent { val refreshState: Value val scope: CoroutineScope - suspend fun getFoldersAsync() - fun getFolders() - fun navigate(child: Config) { navigation.push(child) } - fun navigateToFolder(folder: MessageFolder) { - navigate(Config.Folder(folder.id)) - } - - val folders: Value> + val studyGuides: Value> sealed class Child { class StudyGuideListChild(val component: StudyGuideListComponent) : Child() @@ -53,7 +41,7 @@ interface StudyGuidesChildComponent : ELOChildComponent { object StudyGuideList : Config() @Parcelize - data class Folder(val folderId: Int) : Config() + data class StudyGuide(val studyGuideLink: String) : Config() } } @@ -62,7 +50,7 @@ class DefaultStudyGuidesChildComponent(componentContext: ComponentContext) : Stu override val refreshState: MutableValue = MutableValue(false) override val scope: CoroutineScope = componentCoroutineScope() - override val folders: MutableValue> = MutableValue(listOf()) + override val studyGuides: MutableValue> = MutableValue(listOf()) override val navigation = StackNavigation() @@ -77,7 +65,7 @@ class DefaultStudyGuidesChildComponent(componentContext: ComponentContext) : Stu private fun createChild(config: StudyGuidesChildComponent.Config, componentContext: ComponentContext): StudyGuidesChildComponent.Child = when (config) { is StudyGuidesChildComponent.Config.StudyGuideList -> StudyGuidesChildComponent.Child.StudyGuideListChild(createStudyGuideListComponent(this)) - is StudyGuidesChildComponent.Config.Folder -> StudyGuidesChildComponent.Child.FolderChild(createFolderComponent(componentContext, let { if (folders.value.isEmpty()) runBlocking { getFoldersAsync() }; folders.value.first { it.id == config.folderId } })) + is StudyGuidesChildComponent.Config.StudyGuide -> StudyGuidesChildComponent.Child.FolderChild(createStudyGuideComponent(componentContext, config.studyGuideLink)) } private fun createStudyGuideListComponent(componentContext: ComponentContext) = @@ -87,38 +75,11 @@ class DefaultStudyGuidesChildComponent(componentContext: ComponentContext) : Stu ) - private fun createFolderComponent(componentContext: ComponentContext, folder: MessageFolder) = + private fun createStudyGuideComponent(componentContext: ComponentContext, studyGuideLink: String) = DefaultStudyGuideFolderComponent( componentContext = componentContext, + studyGuideLink = studyGuideLink, ) - - override suspend fun getFoldersAsync() { - refreshState.value = true - - try { - folders.value = - MessageFlow.getAllFolders( - Url(Data.selectedAccount.tenantUrl), - Data.selectedAccount.tokens.accessToken - ) - } catch (e: Exception) { - e.printStackTrace() - } - - refreshState.value = false - } - - override fun getFolders() { - scope.launch { - getFoldersAsync() - } - } - - init { - scope.launch { - getFolders() - } - } } interface StudyGuideChildScreen \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderComponent.kt index b3a60133..64f6f51c 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderComponent.kt @@ -1,13 +1,50 @@ package nl.tiebe.otarium.ui.home.elo.children.studyguides.folder import com.arkivanov.decompose.ComponentContext +import com.arkivanov.decompose.value.MutableValue +import com.arkivanov.decompose.value.Value +import dev.tiebe.magisterapi.api.StudyGuideFlow +import dev.tiebe.magisterapi.response.studyguide.StudyGuideContent +import dev.tiebe.magisterapi.response.studyguide.StudyGuideContentItem +import io.ktor.http.* +import kotlinx.coroutines.launch +import nl.tiebe.otarium.Data +import nl.tiebe.otarium.ui.root.componentCoroutineScope interface StudyGuideFolderComponent { + val content: Value + val contentItems: Value> + val studyGuideLink: String } -class DefaultStudyGuideFolderComponent(componentContext: ComponentContext) : StudyGuideFolderComponent, ComponentContext by componentContext { +class DefaultStudyGuideFolderComponent(componentContext: ComponentContext, override val studyGuideLink: String) : StudyGuideFolderComponent, ComponentContext by componentContext { + override val content: MutableValue = MutableValue(StudyGuideContent(0, false, false, null, StudyGuideContent.Companion.Contents(listOf(), listOf(), 0), "", "", listOf(), "")) + override val contentItems: MutableValue> = MutableValue(listOf()) + val scope = componentCoroutineScope() + private fun loadContent() { + scope.launch { + content.value = StudyGuideFlow.getStudyGuideContent(Url(Data.selectedAccount.tenantUrl), Data.selectedAccount.tokens.accessToken, studyGuideLink) + val items = mutableListOf() + + for (item in content.value.contents.items) { + items.add( + StudyGuideFlow.getStudyGuideContentItem( + Url(Data.selectedAccount.tenantUrl), + Data.selectedAccount.tokens.accessToken, + item.links.first { it.rel == "Self" }.href + ) + ) + } + + contentItems.value = items + } + } + + init { + loadContent() + } } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderItem.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderItem.kt new file mode 100644 index 00000000..f94979f4 --- /dev/null +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderItem.kt @@ -0,0 +1,40 @@ +package nl.tiebe.otarium.ui.home.elo.children.studyguides.folder + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.expandVertically +import androidx.compose.animation.shrinkVertically +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import dev.tiebe.magisterapi.response.studyguide.StudyGuideContentItem +import nl.tiebe.otarium.ui.utils.parseHtml + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +internal fun StudyGuideFolderItem(component: StudyGuideFolderComponent, item: StudyGuideContentItem) { + var showContents by remember { mutableStateOf(false) } + + ListItem( + headlineText = { Text(item.title) }, + supportingText = { Text(item.description.parseHtml()) }, + colors = ListItemDefaults.colors( + containerColor = MaterialTheme.colorScheme.inverseOnSurface + ), + modifier = Modifier.clickable { showContents = !showContents } + ) + + AnimatedVisibility(visible = showContents, enter = expandVertically(), exit = shrinkVertically()) { + Column(modifier = Modifier.padding(start = 16.dp)) { + item.resources.forEach { + Divider() + StudyGuideResourceListItem(component, it) + } + } + } + + Divider() +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderScreen.kt index 0b9ac4f3..74378232 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderScreen.kt @@ -1,8 +1,20 @@ package nl.tiebe.otarium.ui.home.elo.children.studyguides.folder +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState @Composable internal fun StudyGuideFolderScreen(component: StudyGuideFolderComponent) { + val contentItems = component.contentItems.subscribeAsState().value + val scrollState = rememberScrollState() + + Column(modifier = Modifier.fillMaxSize().verticalScroll(scrollState)) { + contentItems.forEach { + StudyGuideFolderItem(component, it) + } + } } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideResourceListItem.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideResourceListItem.kt new file mode 100644 index 00000000..0a114104 --- /dev/null +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideResourceListItem.kt @@ -0,0 +1,15 @@ +package nl.tiebe.otarium.ui.home.elo.children.studyguides.folder + +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ListItem +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import dev.tiebe.magisterapi.response.studyguide.Resource + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +internal fun StudyGuideResourceListItem(component: StudyGuideFolderComponent, item: Resource) { + ListItem( + headlineText = { Text(item.name) }, + ) +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/listscreen/StudyGuideListComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/listscreen/StudyGuideListComponent.kt index beab39f7..87a4aae0 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/listscreen/StudyGuideListComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/listscreen/StudyGuideListComponent.kt @@ -22,7 +22,12 @@ interface StudyGuideListComponent : StudyGuideChildScreen { val isRefreshing: Value - fun refreshStudyGuides() } + fun refreshStudyGuides() + + fun navigateToStudyGuide(studyGuide: StudyGuide) { + parentComponent.navigate(StudyGuidesChildComponent.Config.StudyGuide(studyGuide.links.first { it.rel == "Self" }.href)) + } +} class DefaultStudyGuideListComponent( componentContext: ComponentContext, diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/listscreen/StudyGuideListItem.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/listscreen/StudyGuideListItem.kt index c64555ad..9baa74f8 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/listscreen/StudyGuideListItem.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/listscreen/StudyGuideListItem.kt @@ -13,6 +13,6 @@ import dev.tiebe.magisterapi.response.studyguide.StudyGuide internal fun StudyGuideListItem(component: StudyGuideListComponent, item: StudyGuide) { ListItem( headlineText = { Text(item.title) }, - modifier = Modifier.clickable { }//component.openStudyGuide(item) } + modifier = Modifier.clickable { component.navigateToStudyGuide(item) } ) } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/listscreen/StudyGuideListScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/listscreen/StudyGuideListScreen.kt index 26ea00b9..6990b174 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/listscreen/StudyGuideListScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/listscreen/StudyGuideListScreen.kt @@ -19,7 +19,7 @@ internal fun StudyGuideListScreen(component: StudyGuideListComponent) { val scrollState = rememberScrollState() val pullRefreshState = rememberPullRefreshState(component.isRefreshing.subscribeAsState().value, onRefresh = component::refreshStudyGuides) - Column(Modifier.fillMaxSize().pullRefresh(pullRefreshState).verticalScroll(scrollState)) { + Column(Modifier.fillMaxSize().verticalScroll(scrollState).pullRefresh(pullRefreshState)) { studyGuideItems.forEach { StudyGuideListItem(component, it) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/HtmlHandler.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/HtmlHandler.kt index 739dd1fa..2141ea96 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/HtmlHandler.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/HtmlHandler.kt @@ -75,6 +75,12 @@ fun String.parseHtml(): AnnotatedString { .replace("", "\n") .replace("
  • \n", "
  • ") .replace("
    ", "\n
    ───────────────────\n") + .replace("", "") + .replace("", "") + .replace("", "") + .replace("", "") + .replace("", "") + .replace("", "") return buildAnnotatedString { From 7c3845f4d031189da144e05a1d474d05073a14c5 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Sat, 29 Apr 2023 13:51:25 +0200 Subject: [PATCH 07/82] clickable text --- .../nl/tiebe/otarium/utils/UtilsAndroid.kt | 1 - .../folder/StudyGuideFolderComponent.kt | 31 +++++++++++++ .../folder/StudyGuideFolderItem.kt | 3 +- .../folder/StudyGuideFolderScreen.kt | 2 + .../folder/StudyGuideResourceListItem.kt | 44 +++++++++++++++++-- .../home/messages/message/MessageComponent.kt | 1 + .../ui/home/messages/message/MessageScreen.kt | 21 ++------- .../home/timetable/item/TimetableItemPopup.kt | 27 ++---------- .../tiebe/otarium/ui/utils/ClickableText.kt | 38 ++++++++++++++++ 9 files changed, 121 insertions(+), 47 deletions(-) create mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/ClickableText.kt diff --git a/shared/src/androidMain/kotlin/nl/tiebe/otarium/utils/UtilsAndroid.kt b/shared/src/androidMain/kotlin/nl/tiebe/otarium/utils/UtilsAndroid.kt index b93a78f8..33c24743 100644 --- a/shared/src/androidMain/kotlin/nl/tiebe/otarium/utils/UtilsAndroid.kt +++ b/shared/src/androidMain/kotlin/nl/tiebe/otarium/utils/UtilsAndroid.kt @@ -48,7 +48,6 @@ actual fun openFileFromCache(id: String, fileName: String) { val fileUri = FileProvider.getUriForFile(Android.context, Android.context.applicationContext.packageName, file) intent.setData(fileUri) - intent.flags = Intent.FLAG_ACTIVITY_NO_HISTORY intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) Android.context.startActivity(Intent.createChooser(intent, fileName)) } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderComponent.kt index 64f6f51c..9fc6c417 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderComponent.kt @@ -4,24 +4,36 @@ import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.value.MutableValue import com.arkivanov.decompose.value.Value import dev.tiebe.magisterapi.api.StudyGuideFlow +import dev.tiebe.magisterapi.response.studyguide.Resource import dev.tiebe.magisterapi.response.studyguide.StudyGuideContent import dev.tiebe.magisterapi.response.studyguide.StudyGuideContentItem +import io.ktor.client.statement.* import io.ktor.http.* +import io.ktor.utils.io.* import kotlinx.coroutines.launch import nl.tiebe.otarium.Data import nl.tiebe.otarium.ui.root.componentCoroutineScope +import nl.tiebe.otarium.utils.getDownloadFileLocation +import nl.tiebe.otarium.utils.openFileFromCache +import nl.tiebe.otarium.utils.requestGET interface StudyGuideFolderComponent { val content: Value val contentItems: Value> val studyGuideLink: String + val resourceDownloadProgress: Value> + + fun downloadResource(item: Resource) + } class DefaultStudyGuideFolderComponent(componentContext: ComponentContext, override val studyGuideLink: String) : StudyGuideFolderComponent, ComponentContext by componentContext { override val content: MutableValue = MutableValue(StudyGuideContent(0, false, false, null, StudyGuideContent.Companion.Contents(listOf(), listOf(), 0), "", "", listOf(), "")) override val contentItems: MutableValue> = MutableValue(listOf()) + override val resourceDownloadProgress: MutableValue> = MutableValue(mapOf()) + val scope = componentCoroutineScope() private fun loadContent() { @@ -44,6 +56,25 @@ class DefaultStudyGuideFolderComponent(componentContext: ComponentContext, overr } } + override fun downloadResource(item: Resource) { + scope.launch { + val response = requestGET( + URLBuilder(Data.selectedAccount.tenantUrl).appendEncodedPathSegments(item.links.first { it.rel == "Contents" }.href).build(), + accessToken = Data.selectedAccount.tokens.accessToken, + onDownload = { bytesSentTotal, contentLength -> + resourceDownloadProgress.value = resourceDownloadProgress.value + Pair(item.id, bytesSentTotal.toFloat() / contentLength.toFloat()) + } + ).bodyAsChannel() + + response.copyAndClose(getDownloadFileLocation(item.id.toString(), item.name)) + openFileFromCache(item.id.toString(), item.name) + + resourceDownloadProgress.value = resourceDownloadProgress.value.toMutableMap().also { + it.remove(item.id) + } + } + } + init { loadContent() } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderItem.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderItem.kt index f94979f4..a40fccd6 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderItem.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderItem.kt @@ -11,6 +11,7 @@ import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import dev.tiebe.magisterapi.response.studyguide.StudyGuideContentItem +import nl.tiebe.otarium.ui.utils.ClickableText import nl.tiebe.otarium.ui.utils.parseHtml @OptIn(ExperimentalMaterial3Api::class) @@ -20,7 +21,7 @@ internal fun StudyGuideFolderItem(component: StudyGuideFolderComponent, item: St ListItem( headlineText = { Text(item.title) }, - supportingText = { Text(item.description.parseHtml()) }, + supportingText = { ClickableText(item.description.parseHtml()) }, colors = ListItemDefaults.colors( containerColor = MaterialTheme.colorScheme.inverseOnSurface ), diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderScreen.kt index 74378232..18c11e4c 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderScreen.kt @@ -2,6 +2,8 @@ package nl.tiebe.otarium.ui.home.elo.children.studyguides.folder import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideResourceListItem.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideResourceListItem.kt index 0a114104..8daf251c 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideResourceListItem.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideResourceListItem.kt @@ -1,15 +1,53 @@ package nl.tiebe.otarium.ui.home.elo.children.studyguides.folder +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ListItem +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.unit.dp +import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState import dev.tiebe.magisterapi.response.studyguide.Resource +import nl.tiebe.otarium.ui.utils.DownloadingFileIndicator +import nl.tiebe.otarium.ui.utils.LoadingFileIndicator @OptIn(ExperimentalMaterial3Api::class) @Composable internal fun StudyGuideResourceListItem(component: StudyGuideFolderComponent, item: Resource) { - ListItem( - headlineText = { Text(item.name) }, - ) + Box { + ListItem( + modifier = Modifier.clickable { component.downloadResource(item) }, + headlineText = { Text(item.name) }, + ) + + val progress = + component.resourceDownloadProgress.subscribeAsState().value[item.id] ?: 0f + + if (progress != 0f && !progress.isNaN()) { + val color = MaterialTheme.colorScheme.primary + val trackColor = MaterialTheme.colorScheme.surfaceVariant + + if (progress != 1f) { + DownloadingFileIndicator( + progress = progress, + modifier = Modifier.matchParentSize().align(Alignment.BottomStart), + color = color, + trackColor = trackColor, + height = 5.dp + ) + } else { + LoadingFileIndicator( + modifier = Modifier.matchParentSize().align(Alignment.BottomStart), + color = color, + trackColor = trackColor, + height = 5.dp + ) + } + + } + } } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/message/MessageComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/message/MessageComponent.kt index 4ed5fba9..e246dc8b 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/message/MessageComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/message/MessageComponent.kt @@ -34,6 +34,7 @@ class DefaultMessageComponent( ): MessageComponent, ComponentContext by componentContext { val scope = componentCoroutineScope() + @Suppress("DuplicatedCode") override val message: MutableValue = MutableValue(MessageData(MessageData.Companion.Sender(0, MessageData.Companion.Links(MessageData.Companion.Link(""), MessageData.Companion.Link(""), MessageData.Companion.Link("")), ""), null, listOf(), null, false, false, 0, "", false, listOf(), MessageData.Companion.Links(MessageData.Companion.Link(""), MessageData.Companion.Link(""), MessageData.Companion.Link("")), 0, "", listOf(), "")) override val attachments: MutableValue> = MutableValue(listOf()) override val attachmentDownloadProgress: MutableValue> = MutableValue(mapOf()) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/message/MessageScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/message/MessageScreen.kt index 6cdef524..7c9149f9 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/message/MessageScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/message/MessageScreen.kt @@ -13,6 +13,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.text.TextLayoutResult import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState +import nl.tiebe.otarium.ui.utils.ClickableText import nl.tiebe.otarium.ui.utils.parseHtml import nl.tiebe.otarium.utils.openUrl @@ -26,25 +27,9 @@ internal fun MessageScreen(component: MessageComponent) { val messageContent = component.message.subscribeAsState().value val text = messageContent.content.parseHtml() - val onClick: (Int) -> Unit = { offset -> - text.getStringAnnotations(tag = "URL", start = offset, end = offset).firstOrNull()?.let { annotation -> - openUrl(annotation.item) - } - } - - val layoutResult = remember { mutableStateOf(null) } - val pressIndicator = Modifier.pointerInput(onClick) { - detectTapGestures { pos -> - layoutResult.value?.let { layoutResult -> - onClick(layoutResult.getOffsetForPosition(pos)) - } - } - } - - Text( + ClickableText( text = text, - modifier = Modifier.fillMaxSize().then(pressIndicator), - onTextLayout = { layoutResult.value = it } + modifier = Modifier.fillMaxSize() ) } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/item/TimetableItemPopup.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/item/TimetableItemPopup.kt index f817ffc2..12ae8bfd 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/item/TimetableItemPopup.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/item/TimetableItemPopup.kt @@ -1,6 +1,5 @@ package nl.tiebe.otarium.ui.home.timetable.item -import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize @@ -9,13 +8,9 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add import androidx.compose.material3.* import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.rotate -import androidx.compose.ui.input.pointer.pointerInput -import androidx.compose.ui.text.TextLayoutResult import androidx.compose.ui.unit.dp import kotlinx.datetime.TimeZone import kotlinx.datetime.toInstant @@ -23,8 +18,8 @@ import kotlinx.datetime.toLocalDateTime import nl.tiebe.otarium.magister.AgendaItemWithAbsence import nl.tiebe.otarium.ui.home.timetable.TimetableComponent import nl.tiebe.otarium.ui.utils.BackButton +import nl.tiebe.otarium.ui.utils.ClickableText import nl.tiebe.otarium.ui.utils.parseHtml -import nl.tiebe.otarium.utils.openUrl @Composable internal fun TimetableItemPopup(component: TimetableComponent, agendaItemWithAbsence: AgendaItemWithAbsence) { @@ -69,25 +64,9 @@ internal fun TimetableItemPopup(component: TimetableComponent, agendaItemWithAbs val text = (agendaItemWithAbsence.agendaItem.content ?: "").parseHtml() - val onClick: (Int) -> Unit = { offset -> - text.getStringAnnotations(tag = "URL", start = offset, end = offset).firstOrNull()?.let { annotation -> - openUrl(annotation.item) - } - } - - val layoutResult = remember { mutableStateOf(null) } - val pressIndicator = Modifier.pointerInput(onClick) { - detectTapGestures { pos -> - layoutResult.value?.let { layoutResult -> - onClick(layoutResult.getOffsetForPosition(pos)) - } - } - } - - Text( + ClickableText( text = text, - modifier = Modifier.fillMaxSize().then(pressIndicator), - onTextLayout = { layoutResult.value = it }, + modifier = Modifier.fillMaxSize(), style = MaterialTheme.typography.bodyMedium ) } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/ClickableText.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/ClickableText.kt new file mode 100644 index 00000000..c7b9dc28 --- /dev/null +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/ClickableText.kt @@ -0,0 +1,38 @@ +package nl.tiebe.otarium.ui.utils + +import androidx.compose.foundation.gestures.detectTapGestures +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.TextLayoutResult +import androidx.compose.ui.text.TextStyle +import nl.tiebe.otarium.utils.openUrl + +@Composable +internal fun ClickableText(text: AnnotatedString, modifier: Modifier = Modifier, style: TextStyle = TextStyle.Default) { + val onClick: (Int) -> Unit = { offset -> + text.getStringAnnotations(tag = "URL", start = offset, end = offset).firstOrNull()?.let { annotation -> + openUrl(annotation.item) + } + } + + val layoutResult = remember { mutableStateOf(null) } + val pressIndicator = Modifier.pointerInput(onClick) { + detectTapGestures { pos -> + layoutResult.value?.let { layoutResult -> + onClick(layoutResult.getOffsetForPosition(pos)) + } + } + } + + Text( + text = text, + modifier = modifier.then(pressIndicator), + onTextLayout = { layoutResult.value = it }, + style = style + ) +} \ No newline at end of file From 456452a6b8ea62565928f89fdb898263c435bab4 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Sat, 29 Apr 2023 14:03:55 +0200 Subject: [PATCH 08/82] fix link --- .../children/studyguides/folder/StudyGuideFolderItem.kt | 7 +++++-- .../kotlin/nl/tiebe/otarium/ui/utils/HtmlHandler.kt | 3 ++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderItem.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderItem.kt index a40fccd6..993fb12a 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderItem.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderItem.kt @@ -27,15 +27,18 @@ internal fun StudyGuideFolderItem(component: StudyGuideFolderComponent, item: St ), modifier = Modifier.clickable { showContents = !showContents } ) + Divider() AnimatedVisibility(visible = showContents, enter = expandVertically(), exit = shrinkVertically()) { Column(modifier = Modifier.padding(start = 16.dp)) { item.resources.forEach { - Divider() StudyGuideResourceListItem(component, it) + + if (item.resources.last() != it) { + Divider() + } } } } - Divider() } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/HtmlHandler.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/HtmlHandler.kt index 2141ea96..6e70b670 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/HtmlHandler.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/HtmlHandler.kt @@ -12,6 +12,7 @@ import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.text.style.TextIndent import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.times +import nl.tiebe.otarium.darkModeState import kotlin.math.min private val tags = listOf( @@ -39,7 +40,7 @@ private val tags = listOf( Tag("
      ", "
    ", specialOpeningAction = { htmlList = true }, specialClosingAction = { htmlList = null }), Tag("
      ", "
    ", specialOpeningAction = { listIndex = 0; htmlList = false }, specialClosingAction = { htmlList = null }), - Tag("", regex = "", regexAction = { to, value -> to.pushStringAnnotation("URL", value) }, spanStyle = SpanStyle(color = Color.Cyan, textDecoration = TextDecoration.Underline)), + Tag("", regex = "", regexAction = { to, value -> to.pushStringAnnotation("URL", value) }, spanStyle = SpanStyle(color = if (!darkModeState.value) Color(0, 0, 238) else Color(83, 155, 245), textDecoration = TextDecoration.Underline)), Tag("
    ", "", spanStyle = SpanStyle(letterSpacing = 0.sp)), From d3ba3444a53755a543b2da841c37c519dd7d20ca Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Sat, 29 Apr 2023 14:17:18 +0200 Subject: [PATCH 09/82] more bugfixes --- .../folder/StudyGuideFolderComponent.kt | 8 ++++- .../folder/StudyGuideFolderItem.kt | 2 +- .../folder/StudyGuideFolderScreen.kt | 29 +++++++++++++++---- .../tiebe/otarium/ui/utils/ClickableText.kt | 4 +-- 4 files changed, 34 insertions(+), 9 deletions(-) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderComponent.kt index 9fc6c417..ea89dace 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderComponent.kt @@ -24,8 +24,11 @@ interface StudyGuideFolderComponent { val resourceDownloadProgress: Value> + val refreshing: Value + fun downloadResource(item: Resource) + fun loadContent() } class DefaultStudyGuideFolderComponent(componentContext: ComponentContext, override val studyGuideLink: String) : StudyGuideFolderComponent, ComponentContext by componentContext { @@ -33,11 +36,13 @@ class DefaultStudyGuideFolderComponent(componentContext: ComponentContext, overr override val contentItems: MutableValue> = MutableValue(listOf()) override val resourceDownloadProgress: MutableValue> = MutableValue(mapOf()) + override val refreshing: MutableValue = MutableValue(false) val scope = componentCoroutineScope() - private fun loadContent() { + override fun loadContent() { scope.launch { + refreshing.value = true content.value = StudyGuideFlow.getStudyGuideContent(Url(Data.selectedAccount.tenantUrl), Data.selectedAccount.tokens.accessToken, studyGuideLink) val items = mutableListOf() @@ -53,6 +58,7 @@ class DefaultStudyGuideFolderComponent(componentContext: ComponentContext, overr } contentItems.value = items + refreshing.value = false } } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderItem.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderItem.kt index 993fb12a..f175d088 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderItem.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderItem.kt @@ -21,7 +21,7 @@ internal fun StudyGuideFolderItem(component: StudyGuideFolderComponent, item: St ListItem( headlineText = { Text(item.title) }, - supportingText = { ClickableText(item.description.parseHtml()) }, + supportingText = { ClickableText(item.description.parseHtml()) { showContents = !showContents } }, colors = ListItemDefaults.colors( containerColor = MaterialTheme.colorScheme.inverseOnSurface ), diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderScreen.kt index 18c11e4c..ed17b5c3 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderScreen.kt @@ -1,22 +1,41 @@ package nl.tiebe.otarium.ui.home.elo.children.studyguides.folder +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.pullrefresh.PullRefreshIndicator +import androidx.compose.material.pullrefresh.pullRefresh +import androidx.compose.material.pullrefresh.rememberPullRefreshState import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState +@OptIn(ExperimentalMaterialApi::class) @Composable internal fun StudyGuideFolderScreen(component: StudyGuideFolderComponent) { - val contentItems = component.contentItems.subscribeAsState().value - val scrollState = rememberScrollState() + val pullRefreshState = rememberPullRefreshState( + refreshing = component.refreshing.subscribeAsState().value, + onRefresh = component::loadContent + ) - Column(modifier = Modifier.fillMaxSize().verticalScroll(scrollState)) { - contentItems.forEach { - StudyGuideFolderItem(component, it) + val contentItems = component.contentItems.subscribeAsState().value + + Box(modifier = Modifier.fillMaxSize().pullRefresh(pullRefreshState)) { + Column(modifier = Modifier.fillMaxSize().verticalScroll(scrollState)) { + contentItems.forEach { + StudyGuideFolderItem(component, it) + } } + + PullRefreshIndicator( + refreshing = component.refreshing.subscribeAsState().value, + state = pullRefreshState, + modifier = Modifier.align(Alignment.TopCenter) + ) } } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/ClickableText.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/ClickableText.kt index c7b9dc28..6c1aaac5 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/ClickableText.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/ClickableText.kt @@ -13,11 +13,11 @@ import androidx.compose.ui.text.TextStyle import nl.tiebe.otarium.utils.openUrl @Composable -internal fun ClickableText(text: AnnotatedString, modifier: Modifier = Modifier, style: TextStyle = TextStyle.Default) { +internal fun ClickableText(text: AnnotatedString, modifier: Modifier = Modifier, style: TextStyle = TextStyle.Default, onNoTextFound: () -> Unit = {}) { val onClick: (Int) -> Unit = { offset -> text.getStringAnnotations(tag = "URL", start = offset, end = offset).firstOrNull()?.let { annotation -> openUrl(annotation.item) - } + } ?: onNoTextFound() } val layoutResult = remember { mutableStateOf(null) } From 1b1b475483f527c00904986ef1677ab4bdeb7b9b Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Sat, 29 Apr 2023 14:21:31 +0200 Subject: [PATCH 10/82] bugfixes --- .../listscreen/StudyGuideListScreen.kt | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/listscreen/StudyGuideListScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/listscreen/StudyGuideListScreen.kt index 6990b174..1a375d2f 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/listscreen/StudyGuideListScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/listscreen/StudyGuideListScreen.kt @@ -1,14 +1,17 @@ package nl.tiebe.otarium.ui.home.elo.children.studyguides.listscreen +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll -import androidx.compose.material.Divider import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.pullrefresh.PullRefreshIndicator import androidx.compose.material.pullrefresh.pullRefresh import androidx.compose.material.pullrefresh.rememberPullRefreshState +import androidx.compose.material3.Divider import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState @@ -19,11 +22,19 @@ internal fun StudyGuideListScreen(component: StudyGuideListComponent) { val scrollState = rememberScrollState() val pullRefreshState = rememberPullRefreshState(component.isRefreshing.subscribeAsState().value, onRefresh = component::refreshStudyGuides) - Column(Modifier.fillMaxSize().verticalScroll(scrollState).pullRefresh(pullRefreshState)) { - studyGuideItems.forEach { - StudyGuideListItem(component, it) + Box(modifier = Modifier.fillMaxSize().pullRefresh(pullRefreshState)) { + Column(Modifier.fillMaxSize().verticalScroll(scrollState)) { + studyGuideItems.forEach { + StudyGuideListItem(component, it) - Divider() + Divider() + } } + + PullRefreshIndicator( + refreshing = component.isRefreshing.subscribeAsState().value, + state = pullRefreshState, + modifier = Modifier.align(Alignment.TopCenter) + ) } } \ No newline at end of file From ca20c7260b609fce40a014f2adc1cfa7cb79a804 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Sat, 29 Apr 2023 16:38:15 +0200 Subject: [PATCH 11/82] progress --- .../assignments/AssignmentsChildComponent.kt | 72 ++++++++++++++++++- .../assignments/AssignmentsChildScreen.kt | 13 ++++ .../listscreen/AssignmentListComponent.kt | 65 +++++++++++++++++ .../listscreen/AssignmentListItem.kt | 18 +++++ .../listscreen/AssignmentListScreen.kt | 41 +++++++++++ .../studyguides/StudyGuidesChildComponent.kt | 1 + .../folder/StudyGuideFolderComponent.kt | 2 +- .../listscreen/StudyGuideListComponent.kt | 2 +- 8 files changed, 211 insertions(+), 3 deletions(-) create mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/listscreen/AssignmentListComponent.kt create mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/listscreen/AssignmentListItem.kt create mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/listscreen/AssignmentListScreen.kt diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/AssignmentsChildComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/AssignmentsChildComponent.kt index f4bef295..de9c1238 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/AssignmentsChildComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/AssignmentsChildComponent.kt @@ -1,11 +1,81 @@ package nl.tiebe.otarium.ui.home.elo.children.assignments import com.arkivanov.decompose.ComponentContext +import com.arkivanov.decompose.router.stack.ChildStack +import com.arkivanov.decompose.router.stack.StackNavigation +import com.arkivanov.decompose.router.stack.childStack +import com.arkivanov.decompose.router.stack.push +import com.arkivanov.decompose.value.MutableValue +import com.arkivanov.decompose.value.Value +import com.arkivanov.essenty.parcelable.Parcelable +import com.arkivanov.essenty.parcelable.Parcelize +import kotlinx.coroutines.CoroutineScope import nl.tiebe.otarium.ui.home.elo.ELOChildComponent +import nl.tiebe.otarium.ui.home.elo.children.assignments.listscreen.AssignmentListComponent +import nl.tiebe.otarium.ui.home.elo.children.assignments.listscreen.DefaultAssignmentListComponent +import nl.tiebe.otarium.ui.home.elo.children.studyguides.folder.DefaultStudyGuideFolderComponent +import nl.tiebe.otarium.ui.home.elo.children.studyguides.folder.StudyGuideFolderComponent +import nl.tiebe.otarium.ui.root.componentCoroutineScope interface AssignmentsChildComponent : ELOChildComponent { + val navigation: StackNavigation + val childStack: Value> + val refreshState: Value + val scope: CoroutineScope + + fun navigate(child: Config) { + navigation.push(child) + } + + sealed class Child { + class AssignmentList(val component: AssignmentListComponent) : Child() + class Assignment(val component: StudyGuideFolderComponent) : Child() + } + + sealed class Config : Parcelable { + @Parcelize + object AssignmentList : Config() + + @Parcelize + data class Assignment(val studyGuideLink: String) : Config() + } } class DefaultAssignmentsChildComponent(componentContext: ComponentContext) : AssignmentsChildComponent, ComponentContext by componentContext { -} \ No newline at end of file + override val refreshState: MutableValue = MutableValue(false) + + override val scope: CoroutineScope = componentCoroutineScope() + + override val navigation = StackNavigation() + + override val childStack: Value> = + childStack( + source = navigation, + initialConfiguration = AssignmentsChildComponent.Config.AssignmentList, + key = "AssignmentsChildStack", + handleBackButton = true, // Pop the back stack on back button press + childFactory = ::createChild, + ) + + private fun createChild(config: AssignmentsChildComponent.Config, componentContext: ComponentContext): AssignmentsChildComponent.Child = + when (config) { + is AssignmentsChildComponent.Config.AssignmentList -> AssignmentsChildComponent.Child.AssignmentList(createAssignmentListComponent(this)) + is AssignmentsChildComponent.Config.Assignment -> AssignmentsChildComponent.Child.Assignment(createStudyGuideComponent(componentContext, config.studyGuideLink)) + } + + private fun createAssignmentListComponent(componentContext: ComponentContext) = + DefaultAssignmentListComponent( + componentContext = componentContext, + parentComponent = this + ) + + + private fun createStudyGuideComponent(componentContext: ComponentContext, studyGuideLink: String) = + DefaultStudyGuideFolderComponent( + componentContext = componentContext, + studyGuideLink = studyGuideLink, + ) +} + +interface AssignmentChildScreen \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/AssignmentsChildScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/AssignmentsChildScreen.kt index 7d8a8fdb..ebc34a37 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/AssignmentsChildScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/AssignmentsChildScreen.kt @@ -1,9 +1,22 @@ package nl.tiebe.otarium.ui.home.elo.children.assignments +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState +import nl.tiebe.otarium.ui.home.elo.children.assignments.listscreen.AssignmentListScreen @Composable internal fun AssignmentsChildScreen(component: AssignmentsChildComponent) { + val child = component.childStack.subscribeAsState().value + Box(modifier = Modifier.padding(start = 5.dp, end = 5.dp)) { + when (val screen = child.active.instance) { + is AssignmentsChildComponent.Child.Assignment -> TODO() + is AssignmentsChildComponent.Child.AssignmentList -> AssignmentListScreen(screen.component) + } + } } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/listscreen/AssignmentListComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/listscreen/AssignmentListComponent.kt new file mode 100644 index 00000000..d9072854 --- /dev/null +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/listscreen/AssignmentListComponent.kt @@ -0,0 +1,65 @@ +package nl.tiebe.otarium.ui.home.elo.children.assignments.listscreen + +import com.arkivanov.decompose.ComponentContext +import com.arkivanov.decompose.value.MutableValue +import com.arkivanov.decompose.value.Value +import dev.tiebe.magisterapi.api.assignment.AssignmentFlow +import dev.tiebe.magisterapi.api.general.GeneralFlow +import dev.tiebe.magisterapi.response.assignment.Assignment +import io.ktor.http.* +import kotlinx.coroutines.launch +import nl.tiebe.otarium.Data +import nl.tiebe.otarium.ui.home.elo.children.assignments.AssignmentChildScreen +import nl.tiebe.otarium.ui.home.elo.children.assignments.AssignmentsChildComponent +import nl.tiebe.otarium.ui.root.componentCoroutineScope + +interface AssignmentListComponent : AssignmentChildScreen { + val assignments: Value> + val parentComponent: AssignmentsChildComponent + + val isRefreshing: Value + + fun refreshAssignments() + + fun navigateToAssignment(assignment: Assignment) { + parentComponent.navigate(AssignmentsChildComponent.Config.Assignment(assignment.links.first { it.rel == "Self" }.href)) + } +} + +class DefaultAssignmentListComponent( + componentContext: ComponentContext, + override val parentComponent: AssignmentsChildComponent +) : AssignmentListComponent, ComponentContext by componentContext { + override val assignments: MutableValue> = MutableValue(listOf()) + + override val isRefreshing: MutableValue = MutableValue(false) + + val scope = componentCoroutineScope() + + override fun refreshAssignments() { + scope.launch { + isRefreshing.value = true + val year = GeneralFlow.getYears( + Data.selectedAccount.tenantUrl, + Data.selectedAccount.tokens.accessToken, + Data.selectedAccount.accountId + ).first() + + assignments.value = AssignmentFlow.getAssignments( + Url(Data.selectedAccount.tenantUrl), + Data.selectedAccount.tokens.accessToken, + Data.selectedAccount.accountId, + 0, + 250, + year.start, + year.end + ) + + isRefreshing.value = false + } + } + + init { + refreshAssignments() + } +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/listscreen/AssignmentListItem.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/listscreen/AssignmentListItem.kt new file mode 100644 index 00000000..d53af6c9 --- /dev/null +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/listscreen/AssignmentListItem.kt @@ -0,0 +1,18 @@ +package nl.tiebe.otarium.ui.home.elo.children.assignments.listscreen + +import androidx.compose.foundation.clickable +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ListItem +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import dev.tiebe.magisterapi.response.assignment.Assignment + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +internal fun AssignmentListItem(component: AssignmentListComponent, item: Assignment) { + ListItem( + headlineText = { Text(item.title) }, + modifier = Modifier.clickable { component.navigateToAssignment(item) } + ) +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/listscreen/AssignmentListScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/listscreen/AssignmentListScreen.kt new file mode 100644 index 00000000..8f166adf --- /dev/null +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/listscreen/AssignmentListScreen.kt @@ -0,0 +1,41 @@ +package nl.tiebe.otarium.ui.home.elo.children.assignments.listscreen + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.pullrefresh.PullRefreshIndicator +import androidx.compose.material.pullrefresh.pullRefresh +import androidx.compose.material.pullrefresh.rememberPullRefreshState +import androidx.compose.material3.Divider +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState + +@OptIn(ExperimentalMaterialApi::class) +@Composable +internal fun AssignmentListScreen(component: AssignmentListComponent) { + val assignments = component.assignments.subscribeAsState().value + + val scrollState = rememberScrollState() + val pullRefreshState = rememberPullRefreshState(component.isRefreshing.subscribeAsState().value, onRefresh = component::refreshAssignments) + + Box(modifier = Modifier.fillMaxSize().pullRefresh(pullRefreshState)) { + Column(Modifier.fillMaxSize().verticalScroll(scrollState)) { + assignments.forEach { + AssignmentListItem(component, it) + + Divider() + } + } + + PullRefreshIndicator( + refreshing = component.isRefreshing.subscribeAsState().value, + state = pullRefreshState, + modifier = Modifier.align(Alignment.TopCenter) + ) + } +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/StudyGuidesChildComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/StudyGuidesChildComponent.kt index 8707e903..25f8b850 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/StudyGuidesChildComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/StudyGuidesChildComponent.kt @@ -58,6 +58,7 @@ class DefaultStudyGuidesChildComponent(componentContext: ComponentContext) : Stu childStack( source = navigation, initialConfiguration = StudyGuidesChildComponent.Config.StudyGuideList, + key = "StudyGuideChildStack", handleBackButton = true, // Pop the back stack on back button press childFactory = ::createChild, ) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderComponent.kt index ea89dace..58dc22f0 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderComponent.kt @@ -3,7 +3,7 @@ package nl.tiebe.otarium.ui.home.elo.children.studyguides.folder import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.value.MutableValue import com.arkivanov.decompose.value.Value -import dev.tiebe.magisterapi.api.StudyGuideFlow +import dev.tiebe.magisterapi.api.studyguide.StudyGuideFlow import dev.tiebe.magisterapi.response.studyguide.Resource import dev.tiebe.magisterapi.response.studyguide.StudyGuideContent import dev.tiebe.magisterapi.response.studyguide.StudyGuideContentItem diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/listscreen/StudyGuideListComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/listscreen/StudyGuideListComponent.kt index 87a4aae0..647629d7 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/listscreen/StudyGuideListComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/listscreen/StudyGuideListComponent.kt @@ -3,7 +3,7 @@ package nl.tiebe.otarium.ui.home.elo.children.studyguides.listscreen import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.value.MutableValue import com.arkivanov.decompose.value.Value -import dev.tiebe.magisterapi.api.StudyGuideFlow +import dev.tiebe.magisterapi.api.studyguide.StudyGuideFlow import dev.tiebe.magisterapi.response.studyguide.StudyGuide import io.ktor.http.* import kotlinx.coroutines.launch From 84b8a164050377f0f9b9bd25a26ec1f122efbcbd Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Sat, 29 Apr 2023 19:30:05 +0200 Subject: [PATCH 12/82] finish --- .../LearningResourcesChildComponent.kt | 45 +++++++++++++++++++ .../LearningResourcesChildScreen.kt | 40 +++++++++++++++++ .../folder/StudyGuideFolderComponent.kt | 2 +- .../listscreen/StudyGuideListComponent.kt | 2 +- 4 files changed, 87 insertions(+), 2 deletions(-) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/learningresources/LearningResourcesChildComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/learningresources/LearningResourcesChildComponent.kt index 8dce0fff..55d6f46f 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/learningresources/LearningResourcesChildComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/learningresources/LearningResourcesChildComponent.kt @@ -1,11 +1,56 @@ package nl.tiebe.otarium.ui.home.elo.children.learningresources import com.arkivanov.decompose.ComponentContext +import com.arkivanov.decompose.value.MutableValue +import com.arkivanov.decompose.value.Value +import dev.tiebe.magisterapi.api.learningresource.LearningResourceFlow +import dev.tiebe.magisterapi.response.learningresource.LearningResource +import io.ktor.http.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import nl.tiebe.otarium.Data import nl.tiebe.otarium.ui.home.elo.ELOChildComponent +import nl.tiebe.otarium.ui.root.componentCoroutineScope +import nl.tiebe.otarium.utils.openUrl interface LearningResourcesChildComponent : ELOChildComponent { + val learningResources: Value> + val isRefreshing: Value + + val scope: CoroutineScope + + fun refreshLearningResources() + + fun openLearningResource(learningResource: LearningResource) { + scope.launch { + val url = LearningResourceFlow.getLearningResourceUrl( + Url(Data.selectedAccount.tenantUrl), + Data.selectedAccount.tokens.accessToken, + learningResource.links.first { it.rel == "content" }.href + ) + + openUrl(url ?: return@launch) + } + } } class DefaultLearningResourcesChildComponent(componentContext: ComponentContext) : LearningResourcesChildComponent, ComponentContext by componentContext { + override val learningResources: MutableValue> = MutableValue(emptyList()) + override val isRefreshing: MutableValue = MutableValue(false) + + override val scope = componentCoroutineScope() + + override fun refreshLearningResources() { + scope.launch { + isRefreshing.value = true + learningResources.value = LearningResourceFlow.getLearningResources(Url(Data.selectedAccount.tenantUrl), Data.selectedAccount.tokens.accessToken, Data.selectedAccount.accountId) + isRefreshing.value = false + } + } + + init { + refreshLearningResources() + } + } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/learningresources/LearningResourcesChildScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/learningresources/LearningResourcesChildScreen.kt index 803971fc..1fe63b3d 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/learningresources/LearningResourcesChildScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/learningresources/LearningResourcesChildScreen.kt @@ -1,9 +1,49 @@ package nl.tiebe.otarium.ui.home.elo.children.learningresources +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.pullrefresh.PullRefreshIndicator +import androidx.compose.material.pullrefresh.pullRefresh +import androidx.compose.material.pullrefresh.rememberPullRefreshState +import androidx.compose.material3.Divider +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ListItem +import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState +@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class) @Composable internal fun LearningResourcesChildScreen(component: LearningResourcesChildComponent) { + val learningResources = component.learningResources.subscribeAsState().value + val pullRefreshState = rememberPullRefreshState(component.isRefreshing.subscribeAsState().value, component::refreshLearningResources) + Box(modifier = Modifier.fillMaxSize().pullRefresh(pullRefreshState)) { + val scrollState = rememberScrollState() + + Column(Modifier.verticalScroll(scrollState)) { + learningResources.forEach { + ListItem( + headlineText = { Text(it.title) }, + modifier = Modifier.clickable { component.openLearningResource(it) } + ) + + Divider() + } + } + + PullRefreshIndicator( + state = pullRefreshState, + refreshing = component.isRefreshing.subscribeAsState().value, + modifier = Modifier.align(Alignment.TopCenter) + ) + } } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderComponent.kt index ea89dace..58dc22f0 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderComponent.kt @@ -3,7 +3,7 @@ package nl.tiebe.otarium.ui.home.elo.children.studyguides.folder import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.value.MutableValue import com.arkivanov.decompose.value.Value -import dev.tiebe.magisterapi.api.StudyGuideFlow +import dev.tiebe.magisterapi.api.studyguide.StudyGuideFlow import dev.tiebe.magisterapi.response.studyguide.Resource import dev.tiebe.magisterapi.response.studyguide.StudyGuideContent import dev.tiebe.magisterapi.response.studyguide.StudyGuideContentItem diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/listscreen/StudyGuideListComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/listscreen/StudyGuideListComponent.kt index 87a4aae0..647629d7 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/listscreen/StudyGuideListComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/listscreen/StudyGuideListComponent.kt @@ -3,7 +3,7 @@ package nl.tiebe.otarium.ui.home.elo.children.studyguides.listscreen import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.value.MutableValue import com.arkivanov.decompose.value.Value -import dev.tiebe.magisterapi.api.StudyGuideFlow +import dev.tiebe.magisterapi.api.studyguide.StudyGuideFlow import dev.tiebe.magisterapi.response.studyguide.StudyGuide import io.ktor.http.* import kotlinx.coroutines.launch From e555262fac77fc1c1bbbfd4fb2a8fd8b74842347 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Sat, 29 Apr 2023 20:53:20 +0200 Subject: [PATCH 13/82] changes --- .../assignments/AssignmentsChildComponent.kt | 16 +++---- .../assignments/AssignmentsChildScreen.kt | 3 +- .../assignment/AssignmentScreen.kt | 33 +++++++++++++++ .../assignment/AssignmentScreenComponent.kt | 42 +++++++++++++++++++ .../assignment/VersionInfoScreen.kt | 9 ++++ 5 files changed, 94 insertions(+), 9 deletions(-) create mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreen.kt create mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreenComponent.kt create mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionInfoScreen.kt diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/AssignmentsChildComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/AssignmentsChildComponent.kt index de9c1238..5e8cdb52 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/AssignmentsChildComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/AssignmentsChildComponent.kt @@ -11,10 +11,10 @@ import com.arkivanov.essenty.parcelable.Parcelable import com.arkivanov.essenty.parcelable.Parcelize import kotlinx.coroutines.CoroutineScope import nl.tiebe.otarium.ui.home.elo.ELOChildComponent +import nl.tiebe.otarium.ui.home.elo.children.assignments.assignment.AssignmentScreenComponent +import nl.tiebe.otarium.ui.home.elo.children.assignments.assignment.DefaultAssignmentScreenComponent import nl.tiebe.otarium.ui.home.elo.children.assignments.listscreen.AssignmentListComponent import nl.tiebe.otarium.ui.home.elo.children.assignments.listscreen.DefaultAssignmentListComponent -import nl.tiebe.otarium.ui.home.elo.children.studyguides.folder.DefaultStudyGuideFolderComponent -import nl.tiebe.otarium.ui.home.elo.children.studyguides.folder.StudyGuideFolderComponent import nl.tiebe.otarium.ui.root.componentCoroutineScope interface AssignmentsChildComponent : ELOChildComponent { @@ -30,7 +30,7 @@ interface AssignmentsChildComponent : ELOChildComponent { sealed class Child { class AssignmentList(val component: AssignmentListComponent) : Child() - class Assignment(val component: StudyGuideFolderComponent) : Child() + class Assignment(val component: AssignmentScreenComponent) : Child() } sealed class Config : Parcelable { @@ -38,7 +38,7 @@ interface AssignmentsChildComponent : ELOChildComponent { object AssignmentList : Config() @Parcelize - data class Assignment(val studyGuideLink: String) : Config() + data class Assignment(val assignmentLink: String) : Config() } } @@ -61,7 +61,7 @@ class DefaultAssignmentsChildComponent(componentContext: ComponentContext) : Ass private fun createChild(config: AssignmentsChildComponent.Config, componentContext: ComponentContext): AssignmentsChildComponent.Child = when (config) { is AssignmentsChildComponent.Config.AssignmentList -> AssignmentsChildComponent.Child.AssignmentList(createAssignmentListComponent(this)) - is AssignmentsChildComponent.Config.Assignment -> AssignmentsChildComponent.Child.Assignment(createStudyGuideComponent(componentContext, config.studyGuideLink)) + is AssignmentsChildComponent.Config.Assignment -> AssignmentsChildComponent.Child.Assignment(createAssignmentScreenComponent(componentContext, config.assignmentLink)) } private fun createAssignmentListComponent(componentContext: ComponentContext) = @@ -71,10 +71,10 @@ class DefaultAssignmentsChildComponent(componentContext: ComponentContext) : Ass ) - private fun createStudyGuideComponent(componentContext: ComponentContext, studyGuideLink: String) = - DefaultStudyGuideFolderComponent( + private fun createAssignmentScreenComponent(componentContext: ComponentContext, assignmentLink: String) = + DefaultAssignmentScreenComponent( componentContext = componentContext, - studyGuideLink = studyGuideLink, + assignmentLink = assignmentLink, ) } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/AssignmentsChildScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/AssignmentsChildScreen.kt index ebc34a37..c01c33a3 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/AssignmentsChildScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/AssignmentsChildScreen.kt @@ -6,6 +6,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState +import nl.tiebe.otarium.ui.home.elo.children.assignments.assignment.AssignmentScreen import nl.tiebe.otarium.ui.home.elo.children.assignments.listscreen.AssignmentListScreen @Composable @@ -14,7 +15,7 @@ internal fun AssignmentsChildScreen(component: AssignmentsChildComponent) { Box(modifier = Modifier.padding(start = 5.dp, end = 5.dp)) { when (val screen = child.active.instance) { - is AssignmentsChildComponent.Child.Assignment -> TODO() + is AssignmentsChildComponent.Child.Assignment -> AssignmentScreen(screen.component) is AssignmentsChildComponent.Child.AssignmentList -> AssignmentListScreen(screen.component) } } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreen.kt new file mode 100644 index 00000000..7bee5cf7 --- /dev/null +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreen.kt @@ -0,0 +1,33 @@ +package nl.tiebe.otarium.ui.home.elo.children.assignments.assignment + +import androidx.compose.foundation.layout.Box +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.pullrefresh.PullRefreshIndicator +import androidx.compose.material.pullrefresh.pullRefresh +import androidx.compose.material.pullrefresh.rememberPullRefreshState +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState + +@OptIn(ExperimentalMaterialApi::class) +@Composable +internal fun AssignmentScreen(component: AssignmentScreenComponent) { + val assignment = component.assignment.subscribeAsState().value + + if (assignment.id == 0) return + + val pullRefreshState = rememberPullRefreshState(refreshing = component.isRefreshing.subscribeAsState().value, onRefresh = { component.refreshAssignment() }) + + Box(Modifier.pullRefresh(pullRefreshState)) { + DropdownMenu() + + + PullRefreshIndicator( + state = pullRefreshState, + refreshing = component.isRefreshing.subscribeAsState().value, + modifier = Modifier.align(Alignment.TopCenter) + ) + } + +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreenComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreenComponent.kt new file mode 100644 index 00000000..eddfcad6 --- /dev/null +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreenComponent.kt @@ -0,0 +1,42 @@ +package nl.tiebe.otarium.ui.home.elo.children.assignments.assignment + +import com.arkivanov.decompose.ComponentContext +import com.arkivanov.decompose.value.MutableValue +import com.arkivanov.decompose.value.Value +import dev.tiebe.magisterapi.api.assignment.AssignmentFlow +import dev.tiebe.magisterapi.response.assignment.Assignment +import io.ktor.http.* +import kotlinx.coroutines.launch +import nl.tiebe.otarium.Data +import nl.tiebe.otarium.ui.root.componentCoroutineScope + +interface AssignmentScreenComponent { + val assignment: Value + val assignmentLink: String + + val isRefreshing: Value + + fun refreshAssignment() + +} + +class DefaultAssignmentScreenComponent(componentContext: ComponentContext, override val assignmentLink: String): AssignmentScreenComponent, ComponentContext by componentContext { + override val assignment: MutableValue = MutableValue(Assignment(false, null, "", listOf(), null, 0, null, "", 0, listOf(), false, "", false, 0, "", "", listOf(), false)) + + override val isRefreshing: MutableValue = MutableValue(false) + val scope = componentCoroutineScope() + + override fun refreshAssignment() { + scope.launch { + isRefreshing.value = true + assignment.value = AssignmentFlow.getFullAssignment(Url(Data.selectedAccount.tenantUrl), Data.selectedAccount.tokens.accessToken, assignmentLink) + isRefreshing.value = false + } + } + + init { + refreshAssignment() + } + + +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionInfoScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionInfoScreen.kt new file mode 100644 index 00000000..7e574547 --- /dev/null +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionInfoScreen.kt @@ -0,0 +1,9 @@ +package nl.tiebe.otarium.ui.home.elo.children.assignments.assignment + +import androidx.compose.runtime.Composable +import dev.tiebe.magisterapi.response.assignment.Assignment + +@Composable +internal fun VersionInfoScreen(component: AssignmentScreenComponent, assignment: Assignment, versionNumber: Int) { + +} \ No newline at end of file From 43d219ca97575da19d0b30cd450e793aee417356 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Mon, 8 May 2023 10:55:14 +0200 Subject: [PATCH 14/82] commit --- .../component/home/StoreHomeComponent.kt | 8 ++- .../nl/tiebe/otarium/ui/home/HomeComponent.kt | 6 +- .../assignment/AssignmentScreen.kt | 56 +++++++++++++++++-- .../assignment/AssignmentScreenComponent.kt | 28 +++++++++- .../assignment/VersionInfoScreen.kt | 32 ++++++++++- .../home/timetable/item/TimetableItemPopup.kt | 8 +-- .../kotlin/nl/tiebe/otarium/utils/__Icons.kt | 19 ------- .../icons/bottombar/book_open_filled.svg | 1 - .../icons/bottombar/book_open_outline.svg | 1 - 9 files changed, 122 insertions(+), 37 deletions(-) delete mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/__Icons.kt delete mode 100644 shared/src/commonMain/resources/MR/images/icons/bottombar/book_open_filled.svg delete mode 100644 shared/src/commonMain/resources/MR/images/icons/bottombar/book_open_outline.svg diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/store/component/home/StoreHomeComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/store/component/home/StoreHomeComponent.kt index 9dd58fdd..37bcc4ff 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/store/component/home/StoreHomeComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/store/component/home/StoreHomeComponent.kt @@ -12,8 +12,8 @@ import nl.tiebe.otarium.store.component.home.children.settings.StoreSettingsComp import nl.tiebe.otarium.ui.home.HomeComponent import nl.tiebe.otarium.ui.home.MenuItemComponent import nl.tiebe.otarium.ui.home.debug.DefaultDebugComponent +import nl.tiebe.otarium.ui.home.elo.DefaultELOComponent import nl.tiebe.otarium.ui.home.messages.DefaultMessagesComponent -import nl.tiebe.otarium.ui.home.settings.DefaultSettingsComponent import nl.tiebe.otarium.ui.root.RootComponent class StoreHomeComponent(componentContext: ComponentContext, override val navigateRootComponent: (RootComponent.ChildScreen) -> Unit @@ -38,6 +38,7 @@ class StoreHomeComponent(componentContext: ComponentContext, override val naviga is HomeComponent.MenuItem.Messages -> messagesComponent(componentContext) is HomeComponent.MenuItem.Settings -> settingsComponent(componentContext) is HomeComponent.MenuItem.Debug -> debugComponent(componentContext) + is HomeComponent.MenuItem.ELO -> eloComponent(componentContext) } } @@ -57,6 +58,11 @@ class StoreHomeComponent(componentContext: ComponentContext, override val naviga componentContext = componentContext ) + private fun eloComponent(componentContext: ComponentContext) = + DefaultELOComponent( + componentContext = componentContext + ) + private fun settingsComponent(componentContext: ComponentContext) = StoreSettingsComponent( componentContext = componentContext, diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/HomeComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/HomeComponent.kt index 13c05c44..f863431f 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/HomeComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/HomeComponent.kt @@ -27,7 +27,7 @@ import nl.tiebe.otarium.utils.icons.bottombar.* interface HomeComponent { val dialog: Value> - val visibleItems: List get() = listOf(MenuItem.Timetable, MenuItem.Grades, MenuItem.Messages, MenuItem.Settings) + val visibleItems: List get() = listOf(MenuItem.Timetable, MenuItem.Grades, MenuItem.Messages, MenuItem.ELO, MenuItem.Settings) @Parcelize sealed class MenuItem(val resourceId: StringResource, val icon: @Composable () -> Unit, val iconSelected: @Composable () -> Unit): Parcelable { @@ -51,8 +51,8 @@ interface HomeComponent { object ELO: MenuItem( MR.strings.eloItem, - { Icon(Icons.Bottombar.BookOpenOutline, "ELO") }, - { Icon(Icons.Bottombar.BookOpenFilled, "ELO") } + { Icon(Icons.Bottombar.BookOpenOutline, "ELO", tint = MaterialTheme.colorScheme.onPrimary) }, + { Icon(Icons.Bottombar.BookOpenFilled, "ELO", tint = MaterialTheme.colorScheme.onSecondaryContainer) }, ) object Settings: MenuItem( diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreen.kt index 7bee5cf7..304b7b22 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreen.kt @@ -1,16 +1,27 @@ package nl.tiebe.otarium.ui.home.elo.children.assignments.assignment -import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.pager.HorizontalPager +import androidx.compose.foundation.pager.PageSize +import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.pullrefresh.PullRefreshIndicator import androidx.compose.material.pullrefresh.pullRefresh import androidx.compose.material.pullrefresh.rememberPullRefreshState +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.unit.dp import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState +import kotlin.math.absoluteValue -@OptIn(ExperimentalMaterialApi::class) +@OptIn(ExperimentalMaterialApi::class, ExperimentalFoundationApi::class) @Composable internal fun AssignmentScreen(component: AssignmentScreenComponent) { val assignment = component.assignment.subscribeAsState().value @@ -19,9 +30,46 @@ internal fun AssignmentScreen(component: AssignmentScreenComponent) { val pullRefreshState = rememberPullRefreshState(refreshing = component.isRefreshing.subscribeAsState().value, onRefresh = { component.refreshAssignment() }) - Box(Modifier.pullRefresh(pullRefreshState)) { - DropdownMenu() + Box(Modifier.fillMaxSize().pullRefresh(pullRefreshState)) { + val versions = component.versions.subscribeAsState().value + val pagerState = rememberPagerState() + Row( + Modifier + .height(20.dp) + .fillMaxWidth() + .align(Alignment.BottomCenter), + horizontalArrangement = Arrangement.Center + ) { + repeat(versions.size) { iteration -> + val color = if (pagerState.currentPage == iteration) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurface + Box( + modifier = Modifier + .padding(2.dp) + .clip(CircleShape) + .background(color) + .size(10.dp) + + ) + } + } + + HorizontalPager(pageCount = versions.size, state = pagerState, pageSize = PageSize.Fixed(350.dp), contentPadding = PaddingValues(20.dp)) { + VersionInfoScreen(component, assignment, assignment.navigationItemsVersion[it].id, + Modifier.size(350.dp).padding(5.dp).graphicsLayer { + // Calculate the absolute offset for the current page from the + // scroll position. We use the absolute value which allows us to mirror + // any effects for both directions + val pageOffset = ( + (pagerState.currentPage - it) + pagerState + .currentPageOffsetFraction + ).absoluteValue + + // We animate the alpha, between 50% and 100% + alpha = (1 - (1f - pageOffset.coerceIn(0f, 1f))) * 0.5f + (1f - pageOffset.coerceIn(0f, 1f)) * 1f + } + ) + } PullRefreshIndicator( state = pullRefreshState, diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreenComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreenComponent.kt index eddfcad6..c60df30c 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreenComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreenComponent.kt @@ -5,6 +5,7 @@ import com.arkivanov.decompose.value.MutableValue import com.arkivanov.decompose.value.Value import dev.tiebe.magisterapi.api.assignment.AssignmentFlow import dev.tiebe.magisterapi.response.assignment.Assignment +import dev.tiebe.magisterapi.response.assignment.AssignmentVersion import io.ktor.http.* import kotlinx.coroutines.launch import nl.tiebe.otarium.Data @@ -12,16 +13,21 @@ import nl.tiebe.otarium.ui.root.componentCoroutineScope interface AssignmentScreenComponent { val assignment: Value + val versions: Value> + val assignmentLink: String val isRefreshing: Value fun refreshAssignment() + suspend fun getVersions(assignment: Assignment) + } class DefaultAssignmentScreenComponent(componentContext: ComponentContext, override val assignmentLink: String): AssignmentScreenComponent, ComponentContext by componentContext { override val assignment: MutableValue = MutableValue(Assignment(false, null, "", listOf(), null, 0, null, "", 0, listOf(), false, "", false, 0, "", "", listOf(), false)) + override val versions: MutableValue> = MutableValue(listOf()) override val isRefreshing: MutableValue = MutableValue(false) val scope = componentCoroutineScope() @@ -29,11 +35,31 @@ class DefaultAssignmentScreenComponent(componentContext: ComponentContext, overr override fun refreshAssignment() { scope.launch { isRefreshing.value = true - assignment.value = AssignmentFlow.getFullAssignment(Url(Data.selectedAccount.tenantUrl), Data.selectedAccount.tokens.accessToken, assignmentLink) + val tempAssignment = AssignmentFlow.getFullAssignment(Url(Data.selectedAccount.tenantUrl), Data.selectedAccount.tokens.accessToken, assignmentLink) + getVersions(tempAssignment) + assignment.value = tempAssignment isRefreshing.value = false } } + override suspend fun getVersions(assignment: Assignment) { + val list = mutableListOf() + + assignment.navigationItemsVersion.forEach { + println("test") + list.add( + AssignmentFlow.getVersionInfo( + Url(Data.selectedAccount.tenantUrl), + Data.selectedAccount.tokens.accessToken, + it.links.first { link -> link.rel == "Self" }.href + ) + ) + } + + versions.value = list + } + + init { refreshAssignment() } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionInfoScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionInfoScreen.kt index 7e574547..9b5e5ebe 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionInfoScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionInfoScreen.kt @@ -1,9 +1,39 @@ package nl.tiebe.otarium.ui.home.elo.children.assignments.assignment +import androidx.compose.foundation.layout.Column +import androidx.compose.material3.ElevatedCard +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ListItem +import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier import dev.tiebe.magisterapi.response.assignment.Assignment +import kotlinx.datetime.toLocalDateTime +import nl.tiebe.otarium.ui.utils.parseHtml +import nl.tiebe.otarium.utils.toFormattedString +@OptIn(ExperimentalMaterial3Api::class) @Composable -internal fun VersionInfoScreen(component: AssignmentScreenComponent, assignment: Assignment, versionNumber: Int) { +internal fun VersionInfoScreen(component: AssignmentScreenComponent, assignment: Assignment, versionId: Int, modifier: Modifier) { + ElevatedCard(Modifier.then(modifier)) { + Column { + val version = component.versions.value.first { it.id == versionId } + ListItem( + overlineText = { Text("Title") }, + headlineText = { Text(assignment.title) } + ) + + ListItem( + overlineText = { Text("Submit before") }, + headlineText = { Text(version.deadline.substring(0, 26).toLocalDateTime().toFormattedString()) } + ) + + + ListItem( + overlineText = { Text("Description") }, + headlineText = { Text(assignment.description.parseHtml()) } + ) + } + } } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/item/TimetableItemPopup.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/item/TimetableItemPopup.kt index 59e342b4..43d8d6d4 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/item/TimetableItemPopup.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/item/TimetableItemPopup.kt @@ -1,6 +1,5 @@ package nl.tiebe.otarium.ui.home.timetable.item -import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize @@ -9,22 +8,19 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add import androidx.compose.material3.* import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.rotate -import androidx.compose.ui.input.pointer.pointerInput -import androidx.compose.ui.text.TextLayoutResult import androidx.compose.ui.unit.dp import kotlinx.datetime.TimeZone import kotlinx.datetime.toInstant import kotlinx.datetime.toLocalDateTime +import nl.tiebe.otarium.MR import nl.tiebe.otarium.magister.AgendaItemWithAbsence import nl.tiebe.otarium.ui.home.timetable.TimetableComponent import nl.tiebe.otarium.ui.utils.BackButton +import nl.tiebe.otarium.ui.utils.ClickableText import nl.tiebe.otarium.ui.utils.parseHtml -import nl.tiebe.otarium.utils.openUrl import nl.tiebe.otarium.utils.ui.getLocalizedString @Composable diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/__Icons.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/__Icons.kt deleted file mode 100644 index a631772a..00000000 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/__Icons.kt +++ /dev/null @@ -1,19 +0,0 @@ -package nl.tiebe.otarium.utils - -import androidx.compose.ui.graphics.vector.ImageVector -import nl.tiebe.otarium.utils.icons.bottombar.BookOpenFilled -import nl.tiebe.otarium.utils.icons.bottombar.BookOpenOutline -import kotlin.collections.List as ____KtList - -public object Icons - -private var __AllIcons: ____KtList? = null - -public val Icons.AllIcons: ____KtList - get() { - if (__AllIcons != null) { - return __AllIcons!! - } - __AllIcons= listOf(BookOpenOutline, BookOpenFilled) - return __AllIcons!! - } diff --git a/shared/src/commonMain/resources/MR/images/icons/bottombar/book_open_filled.svg b/shared/src/commonMain/resources/MR/images/icons/bottombar/book_open_filled.svg deleted file mode 100644 index b0cbc997..00000000 --- a/shared/src/commonMain/resources/MR/images/icons/bottombar/book_open_filled.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/shared/src/commonMain/resources/MR/images/icons/bottombar/book_open_outline.svg b/shared/src/commonMain/resources/MR/images/icons/bottombar/book_open_outline.svg deleted file mode 100644 index a139d796..00000000 --- a/shared/src/commonMain/resources/MR/images/icons/bottombar/book_open_outline.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file From abfb49319a433f4e923a8d6f062e76ece86ec701 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Mon, 8 May 2023 11:32:01 +0200 Subject: [PATCH 15/82] quick backup --- .../assignment/AssignmentScreen.kt | 4 +- .../assignment/AssignmentScreenComponent.kt | 1 - .../assignment/VersionInfoScreen.kt | 67 ++++++++++++++----- .../commonMain/resources/MR/base/strings.xml | 2 + .../commonMain/resources/MR/nl/strings.xml | 2 + 5 files changed, 57 insertions(+), 19 deletions(-) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreen.kt index 304b7b22..a1be3656 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreen.kt @@ -26,8 +26,6 @@ import kotlin.math.absoluteValue internal fun AssignmentScreen(component: AssignmentScreenComponent) { val assignment = component.assignment.subscribeAsState().value - if (assignment.id == 0) return - val pullRefreshState = rememberPullRefreshState(refreshing = component.isRefreshing.subscribeAsState().value, onRefresh = { component.refreshAssignment() }) Box(Modifier.fillMaxSize().pullRefresh(pullRefreshState)) { @@ -56,7 +54,7 @@ internal fun AssignmentScreen(component: AssignmentScreenComponent) { HorizontalPager(pageCount = versions.size, state = pagerState, pageSize = PageSize.Fixed(350.dp), contentPadding = PaddingValues(20.dp)) { VersionInfoScreen(component, assignment, assignment.navigationItemsVersion[it].id, - Modifier.size(350.dp).padding(5.dp).graphicsLayer { + Modifier.width(350.dp).padding(5.dp).graphicsLayer { // Calculate the absolute offset for the current page from the // scroll position. We use the absolute value which allows us to mirror // any effects for both directions diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreenComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreenComponent.kt index c60df30c..76a4bb67 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreenComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreenComponent.kt @@ -46,7 +46,6 @@ class DefaultAssignmentScreenComponent(componentContext: ComponentContext, overr val list = mutableListOf() assignment.navigationItemsVersion.forEach { - println("test") list.add( AssignmentFlow.getVersionInfo( Url(Data.selectedAccount.tenantUrl), diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionInfoScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionInfoScreen.kt index 9b5e5ebe..453a682c 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionInfoScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionInfoScreen.kt @@ -1,12 +1,17 @@ package nl.tiebe.otarium.ui.home.elo.children.assignments.assignment import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.ElevatedCard import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ListItem import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp import dev.tiebe.magisterapi.response.assignment.Assignment import kotlinx.datetime.toLocalDateTime import nl.tiebe.otarium.ui.utils.parseHtml @@ -15,25 +20,57 @@ import nl.tiebe.otarium.utils.toFormattedString @OptIn(ExperimentalMaterial3Api::class) @Composable internal fun VersionInfoScreen(component: AssignmentScreenComponent, assignment: Assignment, versionId: Int, modifier: Modifier) { - ElevatedCard(Modifier.then(modifier)) { - Column { - val version = component.versions.value.first { it.id == versionId } + val version = component.versions.value.first { it.id == versionId } - ListItem( - overlineText = { Text("Title") }, - headlineText = { Text(assignment.title) } - ) - ListItem( - overlineText = { Text("Submit before") }, - headlineText = { Text(version.deadline.substring(0, 26).toLocalDateTime().toFormattedString()) } - ) + Column(modifier = modifier.verticalScroll(rememberScrollState())) { + ElevatedCard { + Column { + ListItem( + overlineText = { Text("Title") }, + headlineText = { Text(assignment.title) } + ) + ListItem( + overlineText = { Text("Version")}, + headlineText = { Text(version.versionIndex.toString()) } + ) - ListItem( - overlineText = { Text("Description") }, - headlineText = { Text(assignment.description.parseHtml()) } - ) + ListItem( + overlineText = { Text("Submit before") }, + headlineText = { Text(version.deadline.substring(0, 26).toLocalDateTime().toFormattedString()) } + ) + + + ListItem( + overlineText = { Text("Description") }, + headlineText = { Text(assignment.description.parseHtml()) } + ) + } + } + + Spacer(modifier = Modifier.height(10.dp)) + + ElevatedCard { + Column { + if (assignment.gradedOn != null) { + ListItem( + overlineText = { Text("Graded") }, + headlineText = { Text(assignment.gradedOn!!.substring(0, 26).toLocalDateTime().toFormattedString()) } + ) + } + + ListItem( + overlineText = { Text("Submit before") }, + headlineText = { Text(version.deadline.substring(0, 26).toLocalDateTime().toFormattedString()) } + ) + + + ListItem( + overlineText = { Text("Description") }, + headlineText = { Text(assignment.description.parseHtml()) } + ) + } } } } \ No newline at end of file diff --git a/shared/src/commonMain/resources/MR/base/strings.xml b/shared/src/commonMain/resources/MR/base/strings.xml index f763b2de..7bdc7337 100644 --- a/shared/src/commonMain/resources/MR/base/strings.xml +++ b/shared/src/commonMain/resources/MR/base/strings.xml @@ -64,4 +64,6 @@ Loading... Absence + + version \ No newline at end of file diff --git a/shared/src/commonMain/resources/MR/nl/strings.xml b/shared/src/commonMain/resources/MR/nl/strings.xml index 6ab38b0a..30ea57e4 100644 --- a/shared/src/commonMain/resources/MR/nl/strings.xml +++ b/shared/src/commonMain/resources/MR/nl/strings.xml @@ -62,4 +62,6 @@ Aan het laden... Absentie + + versie \ No newline at end of file From ecaac79b08a53e7e7e702e47ff81d100318e6dea Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Mon, 8 May 2023 11:53:45 +0200 Subject: [PATCH 16/82] move some stuff --- .../assignment/AssignmentScreenComponent.kt | 29 +++++ .../assignments/assignment/VersionCards.kt | 111 ++++++++++++++++++ .../assignment/VersionInfoScreen.kt | 57 +-------- .../ui/home/messages/message/MessageHeader.kt | 29 +---- .../otarium/ui/utils/ProgressIndicator.kt | 30 +++++ 5 files changed, 176 insertions(+), 80 deletions(-) create mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionCards.kt diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreenComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreenComponent.kt index 76a4bb67..7c87aa87 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreenComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreenComponent.kt @@ -6,10 +6,16 @@ import com.arkivanov.decompose.value.Value import dev.tiebe.magisterapi.api.assignment.AssignmentFlow import dev.tiebe.magisterapi.response.assignment.Assignment import dev.tiebe.magisterapi.response.assignment.AssignmentVersion +import dev.tiebe.magisterapi.response.assignment.FeedbackBijlagen +import io.ktor.client.statement.* import io.ktor.http.* +import io.ktor.utils.io.* import kotlinx.coroutines.launch import nl.tiebe.otarium.Data import nl.tiebe.otarium.ui.root.componentCoroutineScope +import nl.tiebe.otarium.utils.getDownloadFileLocation +import nl.tiebe.otarium.utils.openFileFromCache +import nl.tiebe.otarium.utils.requestGET interface AssignmentScreenComponent { val assignment: Value @@ -22,7 +28,9 @@ interface AssignmentScreenComponent { fun refreshAssignment() suspend fun getVersions(assignment: Assignment) + fun downloadAttachment(attachment: FeedbackBijlagen) + val attachmentDownloadProgress: Value> } class DefaultAssignmentScreenComponent(componentContext: ComponentContext, override val assignmentLink: String): AssignmentScreenComponent, ComponentContext by componentContext { @@ -58,6 +66,27 @@ class DefaultAssignmentScreenComponent(componentContext: ComponentContext, overr versions.value = list } + override val attachmentDownloadProgress: MutableValue> = MutableValue(mapOf()) + + override fun downloadAttachment(attachment: FeedbackBijlagen) { + scope.launch { + val response = requestGET( + URLBuilder(Data.selectedAccount.tenantUrl).appendEncodedPathSegments(attachment.links.first { it.rel == "Self" }.href).build(), + accessToken = Data.selectedAccount.tokens.accessToken, + onDownload = { bytesSentTotal, contentLength -> + attachmentDownloadProgress.value = attachmentDownloadProgress.value + Pair(attachment.id, bytesSentTotal.toFloat() / contentLength.toFloat()) + } + ).bodyAsChannel() + + response.copyAndClose(getDownloadFileLocation(attachment.id.toString(), attachment.naam)) + openFileFromCache(attachment.id.toString(), attachment.naam) + + attachmentDownloadProgress.value = attachmentDownloadProgress.value.toMutableMap().also { + it.remove(attachment.id) + } + } + } + init { refreshAssignment() diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionCards.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionCards.kt new file mode 100644 index 00000000..7f70b3b5 --- /dev/null +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionCards.kt @@ -0,0 +1,111 @@ +package nl.tiebe.otarium.ui.home.elo.children.assignments.assignment + +import androidx.compose.foundation.horizontalScroll +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState +import dev.tiebe.magisterapi.response.assignment.Assignment +import dev.tiebe.magisterapi.response.assignment.AssignmentVersion +import kotlinx.datetime.toLocalDateTime +import nl.tiebe.otarium.ui.utils.DownloadIndicator +import nl.tiebe.otarium.ui.utils.parseHtml +import nl.tiebe.otarium.utils.icons.Email +import nl.tiebe.otarium.utils.icons.Icons +import nl.tiebe.otarium.utils.icons.email.Attachment +import nl.tiebe.otarium.utils.toFormattedString + + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +internal fun MainInfoCard(assignment: Assignment, version: AssignmentVersion) { + ElevatedCard { + Column { + ListItem( + overlineText = { Text("Title") }, + headlineText = { Text(assignment.title) } + ) + + ListItem( + overlineText = { Text("Version") }, + headlineText = { Text(version.versionIndex.toString()) } + ) + + ListItem( + overlineText = { Text("Submit before") }, + headlineText = { Text(version.deadline.substring(0, 26).toLocalDateTime().toFormattedString()) } + ) + + + ListItem( + overlineText = { Text("Description") }, + headlineText = { Text(assignment.description.parseHtml()) } + ) + } + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +internal fun TeacherFeedbackCard(component: AssignmentScreenComponent, assignment: Assignment, version: AssignmentVersion) { + ElevatedCard { + Column { + if (version.gradedOn != null) { + ListItem( + overlineText = { Text("Graded on") }, + headlineText = { Text(version.gradedOn!!.substring(0, 26).toLocalDateTime().toFormattedString()) } + ) + } + + if (version.grade != null) { + ListItem( + overlineText = { Text("Grade") }, + headlineText = { Text(version.grade!!) } + ) + } + + if (version.teacherNote != null) { + ListItem( + overlineText = { Text("Feedback") }, + headlineText = { Text(version.teacherNote!!.parseHtml()) } + ) + } + + if (version.feedbackAttachments.isNotEmpty()) { + val scrollState = rememberScrollState() + + Row(modifier = Modifier.fillMaxWidth().horizontalScroll(scrollState)) { + for (attachment in version.feedbackAttachments) { + ElevatedCard( + onClick = { component.downloadAttachment(attachment) }, + modifier = Modifier.height(70.dp).padding(10.dp) + ) { + Box(modifier = Modifier.fillMaxSize()) { + Row( + modifier = Modifier.fillMaxSize().padding(10.dp), + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + Icons.Email.Attachment, + contentDescription = "Attachment" + ) + Text(text = attachment.naam, modifier = Modifier.padding(start = 10.dp)) + } + + DownloadIndicator( + component.attachmentDownloadProgress.subscribeAsState().value[attachment.id] + ?: 0f + ) + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionInfoScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionInfoScreen.kt index 453a682c..8c4d8aec 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionInfoScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionInfoScreen.kt @@ -5,72 +5,23 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.ElevatedCard -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.ListItem -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import dev.tiebe.magisterapi.response.assignment.Assignment -import kotlinx.datetime.toLocalDateTime -import nl.tiebe.otarium.ui.utils.parseHtml -import nl.tiebe.otarium.utils.toFormattedString -@OptIn(ExperimentalMaterial3Api::class) @Composable internal fun VersionInfoScreen(component: AssignmentScreenComponent, assignment: Assignment, versionId: Int, modifier: Modifier) { val version = component.versions.value.first { it.id == versionId } Column(modifier = modifier.verticalScroll(rememberScrollState())) { - ElevatedCard { - Column { - ListItem( - overlineText = { Text("Title") }, - headlineText = { Text(assignment.title) } - ) + MainInfoCard(assignment, version) - ListItem( - overlineText = { Text("Version")}, - headlineText = { Text(version.versionIndex.toString()) } - ) + if (version.gradedOn != null || version.grade != null || version.teacherNote != null || version.feedbackAttachments.isNotEmpty()) { + Spacer(modifier = Modifier.height(10.dp)) - ListItem( - overlineText = { Text("Submit before") }, - headlineText = { Text(version.deadline.substring(0, 26).toLocalDateTime().toFormattedString()) } - ) - - - ListItem( - overlineText = { Text("Description") }, - headlineText = { Text(assignment.description.parseHtml()) } - ) - } - } - - Spacer(modifier = Modifier.height(10.dp)) - - ElevatedCard { - Column { - if (assignment.gradedOn != null) { - ListItem( - overlineText = { Text("Graded") }, - headlineText = { Text(assignment.gradedOn!!.substring(0, 26).toLocalDateTime().toFormattedString()) } - ) - } - - ListItem( - overlineText = { Text("Submit before") }, - headlineText = { Text(version.deadline.substring(0, 26).toLocalDateTime().toFormattedString()) } - ) - - - ListItem( - overlineText = { Text("Description") }, - headlineText = { Text(assignment.description.parseHtml()) } - ) - } + TeacherFeedbackCard(component, assignment, version) } } } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/message/MessageHeader.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/message/MessageHeader.kt index e95529b6..23e5f9fa 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/message/MessageHeader.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/message/MessageHeader.kt @@ -15,8 +15,7 @@ import kotlinx.datetime.toLocalDateTime import nl.tiebe.otarium.MR import nl.tiebe.otarium.ui.home.messages.MessagesComponent import nl.tiebe.otarium.ui.home.messages.message.receiver.ReceiverInfoComponent -import nl.tiebe.otarium.ui.utils.DownloadingFileIndicator -import nl.tiebe.otarium.ui.utils.LoadingFileIndicator +import nl.tiebe.otarium.ui.utils.DownloadIndicator import nl.tiebe.otarium.utils.icons.Email import nl.tiebe.otarium.utils.icons.Icons import nl.tiebe.otarium.utils.icons.email.Attachment @@ -100,31 +99,7 @@ internal fun MessageHeader(component: MessageComponent) { Text(text = attachment.name, modifier = Modifier.padding(start = 10.dp)) } - val progress = - component.attachmentDownloadProgress.subscribeAsState().value[attachment.id] ?: 0f - - if (progress != 0f && !progress.isNaN()) { - val color = MaterialTheme.colorScheme.primary - val trackColor = MaterialTheme.colorScheme.surfaceVariant - - if (progress != 1f) { - DownloadingFileIndicator( - progress = progress, - modifier = Modifier.matchParentSize().align(Alignment.BottomStart), - color = color, - trackColor = trackColor, - height = 5.dp - ) - } else { - LoadingFileIndicator( - modifier = Modifier.matchParentSize().align(Alignment.BottomStart), - color = color, - trackColor = trackColor, - height = 5.dp - ) - } - - } + DownloadIndicator(component.attachmentDownloadProgress.subscribeAsState().value[attachment.id] ?: 0f) } } } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/ProgressIndicator.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/ProgressIndicator.kt index 76b1c37b..d3a24f77 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/ProgressIndicator.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/ProgressIndicator.kt @@ -2,14 +2,44 @@ package nl.tiebe.otarium.ui.utils import androidx.compose.animation.core.* import androidx.compose.foundation.Canvas +import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.progressSemantics +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.drawscope.DrawScope import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.LayoutDirection +import androidx.compose.ui.unit.dp + +@Composable +internal fun BoxScope.DownloadIndicator(progress: Float) { + if (progress != 0f && !progress.isNaN()) { + val color = MaterialTheme.colorScheme.primary + val trackColor = MaterialTheme.colorScheme.surfaceVariant + + if (progress != 1f) { + DownloadingFileIndicator( + progress = progress, + modifier = Modifier.matchParentSize().align(Alignment.BottomStart), + color = color, + trackColor = trackColor, + height = 5.dp + ) + } else { + LoadingFileIndicator( + modifier = Modifier.matchParentSize().align(Alignment.BottomStart), + color = color, + trackColor = trackColor, + height = 5.dp + ) + } + + } +} fun DrawScope.drawLinearIndicator( startFraction: Float, From 2251ffd1e5c1b4e275977b24c7a106333f278a1c Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Mon, 8 May 2023 12:28:54 +0200 Subject: [PATCH 17/82] assignments --- .../assignment/AssignmentScreen.kt | 2 +- .../assignment/AssignmentScreenComponent.kt | 21 ++++++++ .../assignments/assignment/VersionCards.kt | 54 +++++++++++++++++++ .../assignment/VersionInfoScreen.kt | 7 ++- .../listscreen/AssignmentListItem.kt | 4 +- 5 files changed, 85 insertions(+), 3 deletions(-) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreen.kt index a1be3656..ca088d94 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreen.kt @@ -53,7 +53,7 @@ internal fun AssignmentScreen(component: AssignmentScreenComponent) { } HorizontalPager(pageCount = versions.size, state = pagerState, pageSize = PageSize.Fixed(350.dp), contentPadding = PaddingValues(20.dp)) { - VersionInfoScreen(component, assignment, assignment.navigationItemsVersion[it].id, + VersionInfoScreen(component, assignment, assignment.navigationItemsVersion.reversed()[it].id, Modifier.width(350.dp).padding(5.dp).graphicsLayer { // Calculate the absolute offset for the current page from the // scroll position. We use the absolute value which allows us to mirror diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreenComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreenComponent.kt index 7c87aa87..1bfd5c83 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreenComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreenComponent.kt @@ -7,6 +7,7 @@ import dev.tiebe.magisterapi.api.assignment.AssignmentFlow import dev.tiebe.magisterapi.response.assignment.Assignment import dev.tiebe.magisterapi.response.assignment.AssignmentVersion import dev.tiebe.magisterapi.response.assignment.FeedbackBijlagen +import dev.tiebe.magisterapi.response.assignment.LeerlingBijlagen import io.ktor.client.statement.* import io.ktor.http.* import io.ktor.utils.io.* @@ -31,6 +32,7 @@ interface AssignmentScreenComponent { fun downloadAttachment(attachment: FeedbackBijlagen) val attachmentDownloadProgress: Value> + fun downloadAttachment(attachment: LeerlingBijlagen) } class DefaultAssignmentScreenComponent(componentContext: ComponentContext, override val assignmentLink: String): AssignmentScreenComponent, ComponentContext by componentContext { @@ -87,6 +89,25 @@ class DefaultAssignmentScreenComponent(componentContext: ComponentContext, overr } } + override fun downloadAttachment(attachment: LeerlingBijlagen) { + scope.launch { + val response = requestGET( + URLBuilder(Data.selectedAccount.tenantUrl).appendEncodedPathSegments(attachment.links.first { it.rel == "Self" }.href).build(), + accessToken = Data.selectedAccount.tokens.accessToken, + onDownload = { bytesSentTotal, contentLength -> + attachmentDownloadProgress.value = attachmentDownloadProgress.value + Pair(attachment.id, bytesSentTotal.toFloat() / contentLength.toFloat()) + } + ).bodyAsChannel() + + response.copyAndClose(getDownloadFileLocation(attachment.id.toString(), attachment.naam)) + openFileFromCache(attachment.id.toString(), attachment.naam) + + attachmentDownloadProgress.value = attachmentDownloadProgress.value.toMutableMap().also { + it.remove(attachment.id) + } + } + } + init { refreshAssignment() diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionCards.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionCards.kt index 7f70b3b5..26263fd5 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionCards.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionCards.kt @@ -108,4 +108,58 @@ internal fun TeacherFeedbackCard(component: AssignmentScreenComponent, assignmen } } } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +internal fun StudentVersionCard(component: AssignmentScreenComponent, assignment: Assignment, version: AssignmentVersion) { + ElevatedCard { + Column { + if (version.submittedOn != null) { + ListItem( + overlineText = { Text("Submitted on") }, + headlineText = { Text(version.gradedOn!!.substring(0, 26).toLocalDateTime().toFormattedString()) } + ) + } + + if (version.studentNote != null) { + ListItem( + overlineText = { Text("Feedback") }, + headlineText = { Text(version.studentNote!!.parseHtml()) } + ) + } + + if (version.studentAttachments.isNotEmpty()) { + val scrollState = rememberScrollState() + + Row(modifier = Modifier.fillMaxWidth().horizontalScroll(scrollState)) { + for (attachment in version.studentAttachments) { + ElevatedCard( + onClick = { component.downloadAttachment(attachment) }, + modifier = Modifier.height(70.dp).padding(10.dp) + ) { + Box(modifier = Modifier.fillMaxSize()) { + Row( + modifier = Modifier.fillMaxSize().padding(10.dp), + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + Icons.Email.Attachment, + contentDescription = "Attachment" + ) + Text(text = attachment.naam, modifier = Modifier.padding(start = 10.dp)) + } + + DownloadIndicator( + component.attachmentDownloadProgress.subscribeAsState().value[attachment.id] + ?: 0f + ) + } + } + } + } + } + } + } } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionInfoScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionInfoScreen.kt index 8c4d8aec..1466ec4e 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionInfoScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionInfoScreen.kt @@ -14,7 +14,6 @@ import dev.tiebe.magisterapi.response.assignment.Assignment internal fun VersionInfoScreen(component: AssignmentScreenComponent, assignment: Assignment, versionId: Int, modifier: Modifier) { val version = component.versions.value.first { it.id == versionId } - Column(modifier = modifier.verticalScroll(rememberScrollState())) { MainInfoCard(assignment, version) @@ -23,5 +22,11 @@ internal fun VersionInfoScreen(component: AssignmentScreenComponent, assignment: TeacherFeedbackCard(component, assignment, version) } + + if (version.submittedOn != null || version.studentNote != null || version.studentAttachments.isNotEmpty()) { + Spacer(modifier = Modifier.height(10.dp)) + + StudentVersionCard(component, assignment, version) + } } } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/listscreen/AssignmentListItem.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/listscreen/AssignmentListItem.kt index d53af6c9..9960e1eb 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/listscreen/AssignmentListItem.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/listscreen/AssignmentListItem.kt @@ -13,6 +13,8 @@ import dev.tiebe.magisterapi.response.assignment.Assignment internal fun AssignmentListItem(component: AssignmentListComponent, item: Assignment) { ListItem( headlineText = { Text(item.title) }, - modifier = Modifier.clickable { component.navigateToAssignment(item) } + modifier = Modifier.clickable { component.navigateToAssignment(item) }, + trailingContent = { Text(item.grade) + } ) } \ No newline at end of file From 663be2360e216daec8a37460c0e97d908251d4d2 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Mon, 8 May 2023 13:55:44 +0200 Subject: [PATCH 18/82] fixes --- .../otarium/ui/home/debug/DebugComponent.kt | 13 +++++ .../otarium/ui/home/debug/DebugScreen.kt | 7 +++ .../assignments/assignment/VersionCards.kt | 20 +++---- .../listscreen/AssignmentListComponent.kt | 3 ++ .../listscreen/AssignmentListScreen.kt | 52 +++++++++++++++++-- .../commonMain/resources/MR/base/strings.xml | 16 +++++- .../commonMain/resources/MR/nl/strings.xml | 16 +++++- 7 files changed, 112 insertions(+), 15 deletions(-) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/debug/DebugComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/debug/DebugComponent.kt index b9c9e7dd..89e99156 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/debug/DebugComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/debug/DebugComponent.kt @@ -1,6 +1,7 @@ package nl.tiebe.otarium.ui.home.debug import com.arkivanov.decompose.ComponentContext +import dev.icerock.moko.resources.desc.StringDesc import dev.tiebe.magisterapi.response.TokenResponse import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -14,6 +15,8 @@ import nl.tiebe.otarium.ui.root.RootComponent import nl.tiebe.otarium.ui.root.componentCoroutineScope import nl.tiebe.otarium.utils.copyToClipboard +var currentLanguage = "en" + interface DebugComponent: MenuItemComponent { val navigateRootComponent: (RootComponent.ChildScreen) -> Unit val scope: CoroutineScope @@ -42,6 +45,16 @@ interface DebugComponent: MenuItemComponent { } } } + + fun changeLanguage() { + if (currentLanguage == "en") { + StringDesc.localeType = StringDesc.LocaleType.Custom("nl") + currentLanguage = "nl" + } else { + StringDesc.localeType = StringDesc.LocaleType.Custom("en") + currentLanguage = "en" + } + } } class DefaultDebugComponent( diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/debug/DebugScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/debug/DebugScreen.kt index 1d63dbf7..4cd1339e 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/debug/DebugScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/debug/DebugScreen.kt @@ -78,6 +78,13 @@ internal fun DebugScreen(component: DebugComponent) { ) { component.importAccounts(getClipboardText()) } + + SettingRowIconButton( + leftText = AnnotatedString("Change language"), + icon = Icons.Default.KeyboardArrowRight + ) { + component.changeLanguage() + } } } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionCards.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionCards.kt index 26263fd5..d66eee28 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionCards.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionCards.kt @@ -12,12 +12,14 @@ import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState import dev.tiebe.magisterapi.response.assignment.Assignment import dev.tiebe.magisterapi.response.assignment.AssignmentVersion import kotlinx.datetime.toLocalDateTime +import nl.tiebe.otarium.MR import nl.tiebe.otarium.ui.utils.DownloadIndicator import nl.tiebe.otarium.ui.utils.parseHtml import nl.tiebe.otarium.utils.icons.Email import nl.tiebe.otarium.utils.icons.Icons import nl.tiebe.otarium.utils.icons.email.Attachment import nl.tiebe.otarium.utils.toFormattedString +import nl.tiebe.otarium.utils.ui.getLocalizedString @OptIn(ExperimentalMaterial3Api::class) @@ -26,23 +28,23 @@ internal fun MainInfoCard(assignment: Assignment, version: AssignmentVersion) { ElevatedCard { Column { ListItem( - overlineText = { Text("Title") }, + overlineText = { Text(getLocalizedString(MR.strings.assignment_title)) }, headlineText = { Text(assignment.title) } ) ListItem( - overlineText = { Text("Version") }, + overlineText = { Text(getLocalizedString(MR.strings.assignment_version)) }, headlineText = { Text(version.versionIndex.toString()) } ) ListItem( - overlineText = { Text("Submit before") }, + overlineText = { Text(getLocalizedString(MR.strings.assignment_deadline)) }, headlineText = { Text(version.deadline.substring(0, 26).toLocalDateTime().toFormattedString()) } ) ListItem( - overlineText = { Text("Description") }, + overlineText = { Text(getLocalizedString(MR.strings.assignment_description)) }, headlineText = { Text(assignment.description.parseHtml()) } ) } @@ -56,21 +58,21 @@ internal fun TeacherFeedbackCard(component: AssignmentScreenComponent, assignmen Column { if (version.gradedOn != null) { ListItem( - overlineText = { Text("Graded on") }, + overlineText = { Text(getLocalizedString(MR.strings.assignment_graded_on)) }, headlineText = { Text(version.gradedOn!!.substring(0, 26).toLocalDateTime().toFormattedString()) } ) } if (version.grade != null) { ListItem( - overlineText = { Text("Grade") }, + overlineText = { Text(getLocalizedString(MR.strings.assignment_grade)) }, headlineText = { Text(version.grade!!) } ) } if (version.teacherNote != null) { ListItem( - overlineText = { Text("Feedback") }, + overlineText = { Text(getLocalizedString(MR.strings.assignment_feedback)) }, headlineText = { Text(version.teacherNote!!.parseHtml()) } ) } @@ -117,14 +119,14 @@ internal fun StudentVersionCard(component: AssignmentScreenComponent, assignment Column { if (version.submittedOn != null) { ListItem( - overlineText = { Text("Submitted on") }, + overlineText = { Text(getLocalizedString(MR.strings.assignment_submitted_on)) }, headlineText = { Text(version.gradedOn!!.substring(0, 26).toLocalDateTime().toFormattedString()) } ) } if (version.studentNote != null) { ListItem( - overlineText = { Text("Feedback") }, + overlineText = { Text(getLocalizedString(MR.strings.assignment_description)) }, headlineText = { Text(version.studentNote!!.parseHtml()) } ) } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/listscreen/AssignmentListComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/listscreen/AssignmentListComponent.kt index d9072854..885c6ec2 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/listscreen/AssignmentListComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/listscreen/AssignmentListComponent.kt @@ -24,6 +24,8 @@ interface AssignmentListComponent : AssignmentChildScreen { fun navigateToAssignment(assignment: Assignment) { parentComponent.navigate(AssignmentsChildComponent.Config.Assignment(assignment.links.first { it.rel == "Self" }.href)) } + + val selectedTab: MutableValue } class DefaultAssignmentListComponent( @@ -33,6 +35,7 @@ class DefaultAssignmentListComponent( override val assignments: MutableValue> = MutableValue(listOf()) override val isRefreshing: MutableValue = MutableValue(false) + override val selectedTab: MutableValue = MutableValue(0) val scope = componentCoroutineScope() diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/listscreen/AssignmentListScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/listscreen/AssignmentListScreen.kt index 8f166adf..b7aea742 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/listscreen/AssignmentListScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/listscreen/AssignmentListScreen.kt @@ -3,6 +3,7 @@ package nl.tiebe.otarium.ui.home.elo.children.assignments.listscreen import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.ExperimentalMaterialApi @@ -10,10 +11,18 @@ import androidx.compose.material.pullrefresh.PullRefreshIndicator import androidx.compose.material.pullrefresh.pullRefresh import androidx.compose.material.pullrefresh.rememberPullRefreshState import androidx.compose.material3.Divider +import androidx.compose.material3.Tab +import androidx.compose.material3.TabRow +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState +import nl.tiebe.otarium.MR +import nl.tiebe.otarium.utils.ui.getLocalizedString @OptIn(ExperimentalMaterialApi::class) @Composable @@ -24,11 +33,46 @@ internal fun AssignmentListScreen(component: AssignmentListComponent) { val pullRefreshState = rememberPullRefreshState(component.isRefreshing.subscribeAsState().value, onRefresh = component::refreshAssignments) Box(modifier = Modifier.fillMaxSize().pullRefresh(pullRefreshState)) { - Column(Modifier.fillMaxSize().verticalScroll(scrollState)) { - assignments.forEach { - AssignmentListItem(component, it) + Column(Modifier.fillMaxSize()) { + val selectedTab = component.selectedTab.subscribeAsState().value - Divider() + TabRow(selectedTab) { + Tab(selected = selectedTab == 0, onClick = { component.selectedTab.value = 0 }, modifier = Modifier.height(53.dp)) { + Text(getLocalizedString(MR.strings.assignment_all), style = TextStyle(fontSize = 14.sp)) + } + + Tab(selected = selectedTab == 1, onClick = { component.selectedTab.value = 2 }, modifier = Modifier.height(53.dp)) { + Text(getLocalizedString(MR.strings.assignment_open), style = TextStyle(fontSize = 14.sp)) + } + + Tab(selected = selectedTab == 2, onClick = { component.selectedTab.value = 1 }, modifier = Modifier.height(53.dp)) { + Text(getLocalizedString(MR.strings.assignment_submitted), style = TextStyle(fontSize = 14.sp)) + } + + Tab(selected = selectedTab == 3, onClick = { component.selectedTab.value = 3 }, modifier = Modifier.height(53.dp)) { + Text(getLocalizedString(MR.strings.assignment_graded), style = TextStyle(fontSize = 14.sp)) + } + + Tab(selected = selectedTab == 4, onClick = { component.selectedTab.value = 4 }, modifier = Modifier.height(53.dp)) { + Text(getLocalizedString(MR.strings.assignment_closed), style = TextStyle(fontSize = 14.sp)) + } + } + + Column(Modifier.fillMaxSize().verticalScroll(scrollState)) { + assignments.filter { + when (selectedTab) { + 0 -> true + 1 -> !it.closed + 2 -> !it.submitAgain + 3 -> it.gradedOn != null && !it.closed + 4 -> it.closed + else -> false + } + }.forEach { + AssignmentListItem(component, it) + + Divider() + } } } diff --git a/shared/src/commonMain/resources/MR/base/strings.xml b/shared/src/commonMain/resources/MR/base/strings.xml index 7bdc7337..5c52a67b 100644 --- a/shared/src/commonMain/resources/MR/base/strings.xml +++ b/shared/src/commonMain/resources/MR/base/strings.xml @@ -65,5 +65,19 @@ Loading... Absence - version + Title + Version + Deadline + Description + + All + Open + Submitted + Closed + Graded + + Graded on + Grade + Submitted on + Feedback \ No newline at end of file diff --git a/shared/src/commonMain/resources/MR/nl/strings.xml b/shared/src/commonMain/resources/MR/nl/strings.xml index 30ea57e4..821a3d47 100644 --- a/shared/src/commonMain/resources/MR/nl/strings.xml +++ b/shared/src/commonMain/resources/MR/nl/strings.xml @@ -63,5 +63,19 @@ Aan het laden... Absentie - versie + Titel + Versie + Inleveren voor + Beschrijving + + Alle + Open + Ingeleverd + Afgerond + Beoordeeld + + Beoordeld op + Beoordeling + Ingeleverd op + Opmerkingen \ No newline at end of file From c085b513ea0a3c6fb330b50f4833c09e7ee60860 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Mon, 8 May 2023 13:55:44 +0200 Subject: [PATCH 19/82] fixes --- .../otarium/ui/home/debug/DebugComponent.kt | 13 +++++ .../otarium/ui/home/debug/DebugScreen.kt | 7 +++ .../assignments/assignment/VersionCards.kt | 22 ++++---- .../listscreen/AssignmentListComponent.kt | 3 ++ .../listscreen/AssignmentListScreen.kt | 52 +++++++++++++++++-- .../commonMain/resources/MR/base/strings.xml | 16 +++++- .../commonMain/resources/MR/nl/strings.xml | 16 +++++- 7 files changed, 113 insertions(+), 16 deletions(-) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/debug/DebugComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/debug/DebugComponent.kt index b9c9e7dd..89e99156 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/debug/DebugComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/debug/DebugComponent.kt @@ -1,6 +1,7 @@ package nl.tiebe.otarium.ui.home.debug import com.arkivanov.decompose.ComponentContext +import dev.icerock.moko.resources.desc.StringDesc import dev.tiebe.magisterapi.response.TokenResponse import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -14,6 +15,8 @@ import nl.tiebe.otarium.ui.root.RootComponent import nl.tiebe.otarium.ui.root.componentCoroutineScope import nl.tiebe.otarium.utils.copyToClipboard +var currentLanguage = "en" + interface DebugComponent: MenuItemComponent { val navigateRootComponent: (RootComponent.ChildScreen) -> Unit val scope: CoroutineScope @@ -42,6 +45,16 @@ interface DebugComponent: MenuItemComponent { } } } + + fun changeLanguage() { + if (currentLanguage == "en") { + StringDesc.localeType = StringDesc.LocaleType.Custom("nl") + currentLanguage = "nl" + } else { + StringDesc.localeType = StringDesc.LocaleType.Custom("en") + currentLanguage = "en" + } + } } class DefaultDebugComponent( diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/debug/DebugScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/debug/DebugScreen.kt index 1d63dbf7..4cd1339e 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/debug/DebugScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/debug/DebugScreen.kt @@ -78,6 +78,13 @@ internal fun DebugScreen(component: DebugComponent) { ) { component.importAccounts(getClipboardText()) } + + SettingRowIconButton( + leftText = AnnotatedString("Change language"), + icon = Icons.Default.KeyboardArrowRight + ) { + component.changeLanguage() + } } } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionCards.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionCards.kt index 26263fd5..a8f0440a 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionCards.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionCards.kt @@ -12,12 +12,14 @@ import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState import dev.tiebe.magisterapi.response.assignment.Assignment import dev.tiebe.magisterapi.response.assignment.AssignmentVersion import kotlinx.datetime.toLocalDateTime +import nl.tiebe.otarium.MR import nl.tiebe.otarium.ui.utils.DownloadIndicator import nl.tiebe.otarium.ui.utils.parseHtml import nl.tiebe.otarium.utils.icons.Email import nl.tiebe.otarium.utils.icons.Icons import nl.tiebe.otarium.utils.icons.email.Attachment import nl.tiebe.otarium.utils.toFormattedString +import nl.tiebe.otarium.utils.ui.getLocalizedString @OptIn(ExperimentalMaterial3Api::class) @@ -26,23 +28,23 @@ internal fun MainInfoCard(assignment: Assignment, version: AssignmentVersion) { ElevatedCard { Column { ListItem( - overlineText = { Text("Title") }, + overlineText = { Text(getLocalizedString(MR.strings.assignment_title)) }, headlineText = { Text(assignment.title) } ) ListItem( - overlineText = { Text("Version") }, + overlineText = { Text(getLocalizedString(MR.strings.assignment_version)) }, headlineText = { Text(version.versionIndex.toString()) } ) ListItem( - overlineText = { Text("Submit before") }, + overlineText = { Text(getLocalizedString(MR.strings.assignment_deadline)) }, headlineText = { Text(version.deadline.substring(0, 26).toLocalDateTime().toFormattedString()) } ) ListItem( - overlineText = { Text("Description") }, + overlineText = { Text(getLocalizedString(MR.strings.assignment_description)) }, headlineText = { Text(assignment.description.parseHtml()) } ) } @@ -56,21 +58,21 @@ internal fun TeacherFeedbackCard(component: AssignmentScreenComponent, assignmen Column { if (version.gradedOn != null) { ListItem( - overlineText = { Text("Graded on") }, + overlineText = { Text(getLocalizedString(MR.strings.assignment_graded_on)) }, headlineText = { Text(version.gradedOn!!.substring(0, 26).toLocalDateTime().toFormattedString()) } ) } if (version.grade != null) { ListItem( - overlineText = { Text("Grade") }, + overlineText = { Text(getLocalizedString(MR.strings.assignment_grade)) }, headlineText = { Text(version.grade!!) } ) } if (version.teacherNote != null) { ListItem( - overlineText = { Text("Feedback") }, + overlineText = { Text(getLocalizedString(MR.strings.assignment_feedback)) }, headlineText = { Text(version.teacherNote!!.parseHtml()) } ) } @@ -117,14 +119,14 @@ internal fun StudentVersionCard(component: AssignmentScreenComponent, assignment Column { if (version.submittedOn != null) { ListItem( - overlineText = { Text("Submitted on") }, - headlineText = { Text(version.gradedOn!!.substring(0, 26).toLocalDateTime().toFormattedString()) } + overlineText = { Text(getLocalizedString(MR.strings.assignment_submitted_on)) }, + headlineText = { Text(version.submittedOn!!.substring(0, 26).toLocalDateTime().toFormattedString()) } ) } if (version.studentNote != null) { ListItem( - overlineText = { Text("Feedback") }, + overlineText = { Text(getLocalizedString(MR.strings.assignment_description)) }, headlineText = { Text(version.studentNote!!.parseHtml()) } ) } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/listscreen/AssignmentListComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/listscreen/AssignmentListComponent.kt index d9072854..885c6ec2 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/listscreen/AssignmentListComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/listscreen/AssignmentListComponent.kt @@ -24,6 +24,8 @@ interface AssignmentListComponent : AssignmentChildScreen { fun navigateToAssignment(assignment: Assignment) { parentComponent.navigate(AssignmentsChildComponent.Config.Assignment(assignment.links.first { it.rel == "Self" }.href)) } + + val selectedTab: MutableValue } class DefaultAssignmentListComponent( @@ -33,6 +35,7 @@ class DefaultAssignmentListComponent( override val assignments: MutableValue> = MutableValue(listOf()) override val isRefreshing: MutableValue = MutableValue(false) + override val selectedTab: MutableValue = MutableValue(0) val scope = componentCoroutineScope() diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/listscreen/AssignmentListScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/listscreen/AssignmentListScreen.kt index 8f166adf..b7aea742 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/listscreen/AssignmentListScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/listscreen/AssignmentListScreen.kt @@ -3,6 +3,7 @@ package nl.tiebe.otarium.ui.home.elo.children.assignments.listscreen import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.ExperimentalMaterialApi @@ -10,10 +11,18 @@ import androidx.compose.material.pullrefresh.PullRefreshIndicator import androidx.compose.material.pullrefresh.pullRefresh import androidx.compose.material.pullrefresh.rememberPullRefreshState import androidx.compose.material3.Divider +import androidx.compose.material3.Tab +import androidx.compose.material3.TabRow +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState +import nl.tiebe.otarium.MR +import nl.tiebe.otarium.utils.ui.getLocalizedString @OptIn(ExperimentalMaterialApi::class) @Composable @@ -24,11 +33,46 @@ internal fun AssignmentListScreen(component: AssignmentListComponent) { val pullRefreshState = rememberPullRefreshState(component.isRefreshing.subscribeAsState().value, onRefresh = component::refreshAssignments) Box(modifier = Modifier.fillMaxSize().pullRefresh(pullRefreshState)) { - Column(Modifier.fillMaxSize().verticalScroll(scrollState)) { - assignments.forEach { - AssignmentListItem(component, it) + Column(Modifier.fillMaxSize()) { + val selectedTab = component.selectedTab.subscribeAsState().value - Divider() + TabRow(selectedTab) { + Tab(selected = selectedTab == 0, onClick = { component.selectedTab.value = 0 }, modifier = Modifier.height(53.dp)) { + Text(getLocalizedString(MR.strings.assignment_all), style = TextStyle(fontSize = 14.sp)) + } + + Tab(selected = selectedTab == 1, onClick = { component.selectedTab.value = 2 }, modifier = Modifier.height(53.dp)) { + Text(getLocalizedString(MR.strings.assignment_open), style = TextStyle(fontSize = 14.sp)) + } + + Tab(selected = selectedTab == 2, onClick = { component.selectedTab.value = 1 }, modifier = Modifier.height(53.dp)) { + Text(getLocalizedString(MR.strings.assignment_submitted), style = TextStyle(fontSize = 14.sp)) + } + + Tab(selected = selectedTab == 3, onClick = { component.selectedTab.value = 3 }, modifier = Modifier.height(53.dp)) { + Text(getLocalizedString(MR.strings.assignment_graded), style = TextStyle(fontSize = 14.sp)) + } + + Tab(selected = selectedTab == 4, onClick = { component.selectedTab.value = 4 }, modifier = Modifier.height(53.dp)) { + Text(getLocalizedString(MR.strings.assignment_closed), style = TextStyle(fontSize = 14.sp)) + } + } + + Column(Modifier.fillMaxSize().verticalScroll(scrollState)) { + assignments.filter { + when (selectedTab) { + 0 -> true + 1 -> !it.closed + 2 -> !it.submitAgain + 3 -> it.gradedOn != null && !it.closed + 4 -> it.closed + else -> false + } + }.forEach { + AssignmentListItem(component, it) + + Divider() + } } } diff --git a/shared/src/commonMain/resources/MR/base/strings.xml b/shared/src/commonMain/resources/MR/base/strings.xml index 7bdc7337..5c52a67b 100644 --- a/shared/src/commonMain/resources/MR/base/strings.xml +++ b/shared/src/commonMain/resources/MR/base/strings.xml @@ -65,5 +65,19 @@ Loading... Absence - version + Title + Version + Deadline + Description + + All + Open + Submitted + Closed + Graded + + Graded on + Grade + Submitted on + Feedback \ No newline at end of file diff --git a/shared/src/commonMain/resources/MR/nl/strings.xml b/shared/src/commonMain/resources/MR/nl/strings.xml index 30ea57e4..821a3d47 100644 --- a/shared/src/commonMain/resources/MR/nl/strings.xml +++ b/shared/src/commonMain/resources/MR/nl/strings.xml @@ -63,5 +63,19 @@ Aan het laden... Absentie - versie + Titel + Versie + Inleveren voor + Beschrijving + + Alle + Open + Ingeleverd + Afgerond + Beoordeeld + + Beoordeld op + Beoordeling + Ingeleverd op + Opmerkingen \ No newline at end of file From 1320f9841b4273c755e59e64047203d9b0feed80 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Mon, 8 May 2023 14:10:56 +0200 Subject: [PATCH 20/82] fixes --- buildSrc/src/main/kotlin/Dependencies.kt | 4 +-- .../tiebe/otarium/utils/versions/v21/V21.kt | 35 +++++++++++-------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt index a9376bb4..27272263 100755 --- a/buildSrc/src/main/kotlin/Dependencies.kt +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -1,7 +1,7 @@ object Version { - const val appVersion = "3.2.3-alpha" - const val appVersionCode = 31 + const val appVersion = "3.3.0-alpha01" + const val appVersionCode = 32 const val magister = "1.1.8" diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/versions/v21/V21.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/versions/v21/V21.kt index bbb3e510..08c459cd 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/versions/v21/V21.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/versions/v21/V21.kt @@ -14,24 +14,31 @@ import nl.tiebe.otarium.settings fun migrateFromV21() { runBlocking { - val currentAccount: JsonObject = Json.decodeFromString(settings.getStringOrNull("magister_tokens") ?: return@runBlocking) - val accountId = currentAccount["accountId"]!!.jsonPrimitive.content.toInt() - val tokens = Json.decodeFromString(currentAccount["tokens"]!!.jsonObject.toString()) + try { + val currentAccount: JsonObject = + Json.decodeFromString(settings.getStringOrNull("magister_tokens") ?: return@runBlocking) + val accountId = currentAccount["accountId"]!!.jsonPrimitive.content.toInt() + val tokens = Json.decodeFromString(currentAccount["tokens"]!!.jsonObject.toString()) - val profileInfo = ProfileInfoFlow.getProfileInfo(currentAccount["tenantUrl"]!!.jsonPrimitive.content, tokens.accessToken) + val profileInfo = + ProfileInfoFlow.getProfileInfo(currentAccount["tenantUrl"]!!.jsonPrimitive.content, tokens.accessToken) - val newAccount = MagisterAccount(accountId, profileInfo, currentAccount["tenantUrl"]!!.jsonPrimitive.content) + val newAccount = + MagisterAccount(accountId, profileInfo, currentAccount["tenantUrl"]!!.jsonPrimitive.content) - settings.putString("grades-$accountId", settings.getString("grades", "[]")) - settings.putString("full_grade_list-$accountId", settings.getString("full_grade_list", "[]")) - settings.putString("agenda-$accountId", settings.getString("agenda", "[]")) - settings.putString("tokens-${accountId}", Json.encodeToString(tokens)) + settings.putString("grades-$accountId", settings.getString("grades", "[]")) + settings.putString("full_grade_list-$accountId", settings.getString("full_grade_list", "[]")) + settings.putString("agenda-$accountId", settings.getString("agenda", "[]")) + settings.putString("tokens-${accountId}", Json.encodeToString(tokens)) - settings.remove("grades") - settings.remove("full_grade_list") - settings.remove("agenda") - settings.remove("magister_tokens") + settings.remove("grades") + settings.remove("full_grade_list") + settings.remove("agenda") + settings.remove("magister_tokens") - settings.putString("accounts", Json.encodeToString(listOf(newAccount))) + settings.putString("accounts", Json.encodeToString(listOf(newAccount))) + } catch (e: Exception) { + settings.clear() + } } } \ No newline at end of file From 73c7cfe621fce5146f31f69050a5c140c615634b Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Mon, 8 May 2023 14:34:18 +0200 Subject: [PATCH 21/82] fixes --- .../kotlin/nl/tiebe/otarium/ui/login/LoginComponent.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/login/LoginComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/login/LoginComponent.kt index d037c39f..bf328244 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/login/LoginComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/login/LoginComponent.kt @@ -95,7 +95,7 @@ class DefaultLoginComponent(val componentContext: ComponentContext, val navigate private var onBack: BackCallback? = null override fun addBackHandler(onBack: BackCallback) { if (this.onBack != null) - backHandler.unregister(onBack) + backHandler.unregister(this.onBack!!) this.onBack = onBack backHandler.register(onBack) From ba058eb7a14b309098a826ef7779028801fdfb1e Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Mon, 8 May 2023 18:20:28 +0200 Subject: [PATCH 22/82] colors --- .../tiebe/otarium/androidApp/MainActivity.kt | 16 +- shared/build.gradle.kts | 1 + shared/shared.podspec | 2 +- .../nl/tiebe/otarium/RootViewAndroid.kt | 2 +- .../otarium/utils/BackgroundManagerAndroid.kt | 4 +- .../nl/tiebe/otarium/utils/UtilsAndroid.kt | 7 + .../kotlin/nl/tiebe/otarium/Data.kt | 21 ++ .../settings/StoreSettingsComponent.kt | 9 + .../nl/tiebe/otarium/ui/home/HomeComponent.kt | 4 +- .../ui/home/settings/SettingsComponent.kt | 13 + .../ui/home/settings/SettingsScreen.kt | 2 + .../home/settings/items/ui/UIChildScreen.kt | 15 + .../items/ui/colors/ColorChildComponent.kt | 71 ++++ .../items/ui/colors/ColorChildScreen.kt | 138 +++++++- .../home/settings/utils/SettingColorPicker.kt | 52 +++ .../ui/home/timetable/item/TimetableItem.kt | 8 +- .../kotlin/nl/tiebe/otarium/ui/theme/Color.kt | 12 - .../nl/tiebe/otarium/ui/theme/CustomTheme.kt | 43 +++ .../kotlin/nl/tiebe/otarium/ui/theme/Theme.kt | 42 +-- .../ui/utils/colorpicker/ColorWheel.kt | 60 ++++ .../utils/colorpicker/HarmonyColorPicker.kt | 335 ++++++++++++++++++ .../otarium/ui/utils/colorpicker/HsvColor.kt | 164 +++++++++ .../kotlin/nl/tiebe/otarium/utils/Utils.kt | 2 + .../commonMain/resources/MR/base/strings.xml | 22 +- .../commonMain/resources/MR/nl/strings.xml | 3 + .../kotlin/nl/tiebe/otarium/utils/Utils.kt | 4 + 26 files changed, 982 insertions(+), 70 deletions(-) create mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/utils/SettingColorPicker.kt delete mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/theme/Color.kt create mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/theme/CustomTheme.kt create mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/colorpicker/ColorWheel.kt create mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/colorpicker/HarmonyColorPicker.kt create mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/colorpicker/HsvColor.kt diff --git a/androidApp/src/main/java/nl/tiebe/otarium/androidApp/MainActivity.kt b/androidApp/src/main/java/nl/tiebe/otarium/androidApp/MainActivity.kt index f3d10fcd..3c81c27b 100644 --- a/androidApp/src/main/java/nl/tiebe/otarium/androidApp/MainActivity.kt +++ b/androidApp/src/main/java/nl/tiebe/otarium/androidApp/MainActivity.kt @@ -12,8 +12,6 @@ import androidx.compose.material3.dynamicDarkColorScheme import androidx.compose.material3.dynamicLightColorScheme import com.arkivanov.decompose.defaultComponentContext import nl.tiebe.otarium.RootView -import nl.tiebe.otarium.ui.theme.DarkColorScheme -import nl.tiebe.otarium.ui.theme.LightColorScheme import nl.tiebe.otarium.utils.refreshGradesBackground import nl.tiebe.otarium.utils.reloadTokensBackground import nl.tiebe.otarium.utils.ui.Android @@ -53,9 +51,7 @@ class MainActivity : AppCompatActivity() { dynamicColor && !darkMode -> { dynamicLightColorScheme(Android.context) } - - darkMode -> DarkColorScheme - else -> LightColorScheme + else -> null } setContent { @@ -83,10 +79,12 @@ class MainActivity : AppCompatActivity() { private fun deleteDir(dir: File?): Boolean { if (dir != null && dir.isDirectory) { val children = dir.list() - for (i in children.indices) { - val success = deleteDir(File(dir, children[i])) - if (!success) { - return false + if (children != null) { + for (i in children.indices) { + val success = deleteDir(File(dir, children[i])) + if (!success) { + return false + } } } } diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts index 4eaf4964..98640b63 100644 --- a/shared/build.gradle.kts +++ b/shared/build.gradle.kts @@ -76,6 +76,7 @@ kotlin { implementation(Decompose.compose) implementation(magisterAPI) + implementation("com.github.ajalt.colormath:colormath:3.2.0") } } val androidMain by getting { diff --git a/shared/shared.podspec b/shared/shared.podspec index ac6ad1ce..cb131e70 100644 --- a/shared/shared.podspec +++ b/shared/shared.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = 'shared' - spec.version = '3.2.3-alpha' + spec.version = '3.3.0-alpha01' spec.homepage = 'https://otarium.groosman.nl' spec.source = { :http=> ''} spec.authors = '' diff --git a/shared/src/androidMain/kotlin/nl/tiebe/otarium/RootViewAndroid.kt b/shared/src/androidMain/kotlin/nl/tiebe/otarium/RootViewAndroid.kt index 0087b40b..ae284135 100644 --- a/shared/src/androidMain/kotlin/nl/tiebe/otarium/RootViewAndroid.kt +++ b/shared/src/androidMain/kotlin/nl/tiebe/otarium/RootViewAndroid.kt @@ -5,7 +5,7 @@ import androidx.compose.runtime.Composable import com.arkivanov.decompose.DefaultComponentContext @Composable -fun RootView(rootComponentContext: DefaultComponentContext, colorScheme: ColorScheme) { +fun RootView(rootComponentContext: DefaultComponentContext, colorScheme: ColorScheme?) { ProvideComponentContext(rootComponentContext) { setup() diff --git a/shared/src/androidMain/kotlin/nl/tiebe/otarium/utils/BackgroundManagerAndroid.kt b/shared/src/androidMain/kotlin/nl/tiebe/otarium/utils/BackgroundManagerAndroid.kt index 4871666d..6abb3044 100644 --- a/shared/src/androidMain/kotlin/nl/tiebe/otarium/utils/BackgroundManagerAndroid.kt +++ b/shared/src/androidMain/kotlin/nl/tiebe/otarium/utils/BackgroundManagerAndroid.kt @@ -7,6 +7,7 @@ import android.app.PendingIntent.FLAG_MUTABLE import android.content.Context import android.content.pm.PackageManager import android.os.Build +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.core.app.ActivityCompat import androidx.core.app.NotificationCompat @@ -17,7 +18,6 @@ import com.google.common.util.concurrent.ListenableFuture import kotlinx.coroutines.runBlocking import nl.tiebe.otarium.R import nl.tiebe.otarium.magister.refreshGrades -import nl.tiebe.otarium.ui.theme.Blue80 import nl.tiebe.otarium.utils.ui.Android import java.util.concurrent.TimeUnit @@ -94,7 +94,7 @@ actual fun sendNotification(title: String, message: String) { .setAutoCancel(true) .setContentIntent(PendingIntent.getActivity(Android.context, 0, intent, if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) FLAG_MUTABLE else FLAG_CANCEL_CURRENT)) .setGroup(System.currentTimeMillis().toString()) - .setColor(Blue80.toArgb()) + .setColor(Color(nl.tiebe.otarium.Data.customDarkTheme.primary).toArgb()) with(NotificationManagerCompat.from(Android.context)) { if (ActivityCompat.checkSelfPermission( diff --git a/shared/src/androidMain/kotlin/nl/tiebe/otarium/utils/UtilsAndroid.kt b/shared/src/androidMain/kotlin/nl/tiebe/otarium/utils/UtilsAndroid.kt index 33c24743..3a367825 100644 --- a/shared/src/androidMain/kotlin/nl/tiebe/otarium/utils/UtilsAndroid.kt +++ b/shared/src/androidMain/kotlin/nl/tiebe/otarium/utils/UtilsAndroid.kt @@ -2,6 +2,7 @@ package nl.tiebe.otarium.utils import android.content.Intent import android.net.Uri +import android.os.Build import androidx.core.content.FileProvider import io.ktor.util.cio.* import io.ktor.utils.io.* @@ -50,4 +51,10 @@ actual fun openFileFromCache(id: String, fileName: String) { intent.setData(fileUri) intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) Android.context.startActivity(Intent.createChooser(intent, fileName)) +} + + + +actual fun dynamicColorsPossible(): Boolean { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/Data.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/Data.kt index 12dda0b3..d4afdf65 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/Data.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/Data.kt @@ -4,6 +4,9 @@ import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import nl.tiebe.otarium.magister.MagisterAccount +import nl.tiebe.otarium.ui.theme.CustomTheme +import nl.tiebe.otarium.ui.theme.defaultDarkTheme +import nl.tiebe.otarium.ui.theme.defaultLightTheme object Data { var finishedOnboarding: Boolean @@ -35,4 +38,22 @@ object Data { get() = settings.getInt("decimals", 2) set(value) = settings.putInt("decimals", value) + var customThemeEnabled: Boolean + get() = settings.getBoolean("custom_theme", false) + set(value) = settings.putBoolean("custom_theme", value) + + var customLightTheme: CustomTheme + get() = settings.getStringOrNull("custom_light_theme")?.let { + Json.decodeFromString(it) } ?: defaultLightTheme + set(value) = settings.putString("custom_light_theme", Json.encodeToString(value)) + + var customDarkTheme: CustomTheme + get() = settings.getStringOrNull("custom_dark_theme")?.let { + Json.decodeFromString(it) } ?: defaultDarkTheme + set(value) = settings.putString("custom_dark_theme", Json.encodeToString(value)) + + var dynamicTheme: Boolean + get() = settings.getBoolean("dynamic_theme", false) + set(value) = settings.putBoolean("dynamic_theme", value) + } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/store/component/home/children/settings/StoreSettingsComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/store/component/home/children/settings/StoreSettingsComponent.kt index 2694c74b..98dd6f7a 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/store/component/home/children/settings/StoreSettingsComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/store/component/home/children/settings/StoreSettingsComponent.kt @@ -15,6 +15,8 @@ import nl.tiebe.otarium.ui.home.settings.items.main.DefaultMainChildComponent import nl.tiebe.otarium.ui.home.settings.items.main.MainChildComponent import nl.tiebe.otarium.ui.home.settings.items.ui.DefaultUIChildComponent import nl.tiebe.otarium.ui.home.settings.items.ui.UIChildComponent +import nl.tiebe.otarium.ui.home.settings.items.ui.colors.ColorChildComponent +import nl.tiebe.otarium.ui.home.settings.items.ui.colors.DefaultColorChildComponent import nl.tiebe.otarium.ui.home.settings.items.users.UserChildComponent import nl.tiebe.otarium.ui.root.RootComponent @@ -39,6 +41,7 @@ class StoreSettingsComponent( is SettingsComponent.Config.Bugs -> SettingsComponent.Child.BugsChild(bugsChild(componentContext)) is SettingsComponent.Config.Users -> SettingsComponent.Child.UsersChild(usersChild(componentContext)) is SettingsComponent.Config.UI -> SettingsComponent.Child.UIChild(uiChild(componentContext)) + SettingsComponent.Config.Colors -> SettingsComponent.Child.ColorChild(colorChild(componentContext)) } private fun mainChild(componentContext: ComponentContext): MainChildComponent = @@ -71,4 +74,10 @@ class StoreSettingsComponent( componentContext = componentContext, _navigate = ::navigate ) + + private fun colorChild(componentContext: ComponentContext): ColorChildComponent = + DefaultColorChildComponent( + componentContext = componentContext, + _navigate = ::navigate + ) } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/HomeComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/HomeComponent.kt index f863431f..5b9f1d4b 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/HomeComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/HomeComponent.kt @@ -56,13 +56,13 @@ interface HomeComponent { ) object Settings: MenuItem( - MR.strings.settings_title, + MR.strings.settingsItem, { Icon(Icons.Bottombar.CogOutline, "Settings", tint = MaterialTheme.colorScheme.onPrimary) }, { Icon(Icons.Bottombar.CogFilled, "Settings", tint = MaterialTheme.colorScheme.onSecondaryContainer) }, ) object Debug: MenuItem( - MR.strings.settings_title, + MR.strings.settingsItem, { Icon(Icons.Bottombar.Box10Outline, "Debug", tint = MaterialTheme.colorScheme.onPrimary) }, { Icon(Icons.Bottombar.Box10Filled, "Debug", tint = MaterialTheme.colorScheme.onSecondaryContainer) }, ) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/SettingsComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/SettingsComponent.kt index 39fbb146..7cf9570e 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/SettingsComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/SettingsComponent.kt @@ -15,6 +15,8 @@ import nl.tiebe.otarium.ui.home.settings.items.main.DefaultMainChildComponent import nl.tiebe.otarium.ui.home.settings.items.main.MainChildComponent import nl.tiebe.otarium.ui.home.settings.items.ui.DefaultUIChildComponent import nl.tiebe.otarium.ui.home.settings.items.ui.UIChildComponent +import nl.tiebe.otarium.ui.home.settings.items.ui.colors.ColorChildComponent +import nl.tiebe.otarium.ui.home.settings.items.ui.colors.DefaultColorChildComponent import nl.tiebe.otarium.ui.home.settings.items.users.DefaultUserChildComponent import nl.tiebe.otarium.ui.home.settings.items.users.UserChildComponent import nl.tiebe.otarium.ui.root.RootComponent @@ -40,6 +42,7 @@ interface SettingsComponent: MenuItemComponent { class BugsChild(val component: BugsChildComponent) : Child() class UsersChild(val component: UserChildComponent) : Child() class UIChild(val component: UIChildComponent) : Child() + class ColorChild(val component: ColorChildComponent) : Child() } sealed class Config(val localizedString: String) : Parcelable { @@ -57,6 +60,9 @@ interface SettingsComponent: MenuItemComponent { @Parcelize object UI : Config(getLocalizedString(MR.strings.ui_settings)) + + @Parcelize + object Colors : Config(getLocalizedString(MR.strings.color_settings)) } } @@ -82,6 +88,7 @@ class DefaultSettingsComponent( is SettingsComponent.Config.Bugs -> SettingsComponent.Child.BugsChild(bugsChild(componentContext)) is SettingsComponent.Config.Users -> SettingsComponent.Child.UsersChild(usersChild(componentContext)) is SettingsComponent.Config.UI -> SettingsComponent.Child.UIChild(uiChild(componentContext)) + is SettingsComponent.Config.Colors -> SettingsComponent.Child.ColorChild(colorChild(componentContext)) } private fun mainChild(componentContext: ComponentContext): MainChildComponent = @@ -114,4 +121,10 @@ class DefaultSettingsComponent( componentContext = componentContext, _navigate = ::navigate ) + + private fun colorChild(componentContext: ComponentContext): ColorChildComponent = + DefaultColorChildComponent( + componentContext = componentContext, + _navigate = ::navigate + ) } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/SettingsScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/SettingsScreen.kt index fd1698fa..34238111 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/SettingsScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/SettingsScreen.kt @@ -14,6 +14,7 @@ import nl.tiebe.otarium.ui.home.settings.items.ads.AdsChildScreen import nl.tiebe.otarium.ui.home.settings.items.bugs.BugsChildScreen import nl.tiebe.otarium.ui.home.settings.items.main.MainChildScreen import nl.tiebe.otarium.ui.home.settings.items.ui.UIChildScreen +import nl.tiebe.otarium.ui.home.settings.items.ui.colors.ColorChildScreen import nl.tiebe.otarium.ui.home.settings.items.users.UserChildScreen @OptIn(ExperimentalMaterial3Api::class) @@ -40,6 +41,7 @@ internal fun SettingsScreen(component: SettingsComponent) { is SettingsComponent.Child.UsersChild -> UserChildScreen(child.component) is SettingsComponent.Child.BugsChild -> BugsChildScreen() is SettingsComponent.Child.UIChild -> UIChildScreen(child.component) + is SettingsComponent.Child.ColorChild -> ColorChildScreen(child.component) } } } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/UIChildScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/UIChildScreen.kt index 208d10e1..9d465302 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/UIChildScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/UIChildScreen.kt @@ -7,7 +7,12 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.AnnotatedString import nl.tiebe.otarium.Data import nl.tiebe.otarium.MR +import nl.tiebe.otarium.ui.home.settings.SettingsComponent +import nl.tiebe.otarium.ui.home.settings.utils.SettingRowIconButton import nl.tiebe.otarium.ui.home.settings.utils.SettingSlider +import nl.tiebe.otarium.utils.icons.Email +import nl.tiebe.otarium.utils.icons.Icons +import nl.tiebe.otarium.utils.icons.email.Attachment import nl.tiebe.otarium.utils.ui.getLocalizedString import kotlin.math.roundToInt @@ -19,6 +24,14 @@ internal fun UIChildScreen(component: UIChildComponent) { ) { var sliderValue by remember { mutableStateOf(Data.decimals.toFloat()) } + SettingRowIconButton( + leftText = AnnotatedString("Colorrrrssss"), + icon = Icons.Email.Attachment, + onClick = { + component.navigate(SettingsComponent.Config.Colors) + } + ) + SettingSlider( text = AnnotatedString(getLocalizedString(MR.strings.decimals_slider)), value = sliderValue, @@ -29,5 +42,7 @@ internal fun UIChildScreen(component: UIChildComponent) { valueRange = 0f..4f, steps = 2 ) + + } } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/colors/ColorChildComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/colors/ColorChildComponent.kt index 6c2a8e99..070b46fc 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/colors/ColorChildComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/colors/ColorChildComponent.kt @@ -1,19 +1,90 @@ package nl.tiebe.otarium.ui.home.settings.items.ui.colors +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.toArgb import com.arkivanov.decompose.ComponentContext +import com.arkivanov.decompose.value.MutableValue +import nl.tiebe.otarium.Data import nl.tiebe.otarium.ui.home.settings.SettingsComponent +import nl.tiebe.otarium.ui.theme.CustomTheme +import nl.tiebe.otarium.ui.theme.defaultDarkTheme +import nl.tiebe.otarium.ui.theme.defaultLightTheme +import nl.tiebe.otarium.ui.utils.colorpicker.HsvColor interface ColorChildComponent { fun navigate(child: SettingsComponent.Config) + val dynamicColorState: MutableValue + val customColorScheme: MutableValue + + val primaryLightColor: MutableValue + val secondaryLightColor: MutableValue + val tertiaryLightColor: MutableValue + + val primaryDarkColor: MutableValue + val secondaryDarkColor: MutableValue + val tertiaryDarkColor: MutableValue + + fun resetColorScheme() { + primaryLightColor.value = HsvColor.from(Color(defaultLightTheme.primary)) + secondaryLightColor.value = HsvColor.from(Color(defaultLightTheme.secondary)) + tertiaryLightColor.value = HsvColor.from(Color(defaultLightTheme.tertiary)) + + primaryDarkColor.value = HsvColor.from(Color(defaultDarkTheme.primary)) + secondaryDarkColor.value = HsvColor.from(Color(defaultDarkTheme.secondary)) + tertiaryDarkColor.value = HsvColor.from(Color(defaultDarkTheme.tertiary)) + } + + fun saveColorScheme() { + Data.customLightTheme = CustomTheme( + primary = primaryLightColor.value.toColor().toArgb(), + secondary = secondaryLightColor.value.toColor().toArgb(), + tertiary = tertiaryLightColor.value.toColor().toArgb() + ) + + println(Data.customLightTheme) + + Data.customDarkTheme = CustomTheme( + primary = primaryDarkColor.value.toColor().toArgb(), + secondary = secondaryDarkColor.value.toColor().toArgb(), + tertiary = tertiaryDarkColor.value.toColor().toArgb() + ) + } } class DefaultColorChildComponent( componentContext: ComponentContext, private val _navigate: (child: SettingsComponent.Config) -> Unit ) : ColorChildComponent, ComponentContext by componentContext { + override val dynamicColorState: MutableValue = MutableValue(Data.dynamicTheme) + override val customColorScheme: MutableValue = MutableValue(Data.customThemeEnabled) + + private val customLightTheme = Data.customLightTheme + private val customDarkTheme = Data.customDarkTheme + + override val primaryLightColor: MutableValue = MutableValue(HsvColor.from(Color(customLightTheme.primary))) + override val secondaryLightColor: MutableValue = MutableValue(HsvColor.from(Color(customLightTheme.secondary))) + override val tertiaryLightColor: MutableValue = MutableValue(HsvColor.from(Color(customLightTheme.tertiary))) + + override val primaryDarkColor: MutableValue = MutableValue(HsvColor.from(Color(customDarkTheme.primary))) + override val secondaryDarkColor: MutableValue = MutableValue(HsvColor.from(Color(customDarkTheme.secondary))) + override val tertiaryDarkColor: MutableValue = MutableValue(HsvColor.from(Color(customDarkTheme.tertiary))) + + + override fun navigate(child: SettingsComponent.Config) { _navigate(child) } + init { + dynamicColorState.subscribe { + Data.dynamicTheme = it + if (it) customColorScheme.value = false + } + customColorScheme.subscribe { + Data.customThemeEnabled = it + if (it) dynamicColorState.value = false + } + } + } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/colors/ColorChildScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/colors/ColorChildScreen.kt index a295a19e..2b44ab46 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/colors/ColorChildScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/colors/ColorChildScreen.kt @@ -1,11 +1,28 @@ package nl.tiebe.otarium.ui.home.settings.items.ui.colors -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.expandVertically +import androidx.compose.animation.shrinkVertically +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.filled.Create +import androidx.compose.material.icons.filled.Delete +import androidx.compose.material3.Button +import androidx.compose.material3.Icon +import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.unit.dp +import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState +import nl.tiebe.otarium.ui.home.settings.utils.SettingRowIconButton +import nl.tiebe.otarium.ui.home.settings.utils.SettingsColorPicker +import nl.tiebe.otarium.ui.home.settings.utils.SettingsRowToggle @Composable internal fun ColorChildScreen(component: ColorChildComponent) { @@ -13,7 +30,120 @@ internal fun ColorChildScreen(component: ColorChildComponent) { modifier = Modifier .fillMaxSize() .padding(16.dp) + .verticalScroll(rememberScrollState()), ) { - //todo + val dynamicColorScheme = component.dynamicColorState.subscribeAsState() + val customColorScheme = component.customColorScheme.subscribeAsState() + + SettingsRowToggle( + leftText = AnnotatedString("Dynamic color scheme"), + checked = dynamicColorScheme.value, + ) { + component.dynamicColorState.value = it + } + + SettingsRowToggle( + leftText = AnnotatedString("Custom color scheme"), + checked = customColorScheme.value, + ) { + component.customColorScheme.value = it + } + + if (customColorScheme.value) { + SettingRowIconButton( + leftText = AnnotatedString("Reset to default"), + icon = Icons.Default.Delete, + onClick = { + component.resetColorScheme() + } + ) + + SettingRowIconButton( + leftText = AnnotatedString("Save color scheme"), + icon = Icons.Default.Create, + onClick = { + println(component.primaryLightColor.value.toColor()) + component.saveColorScheme() + } + ) + + val showLightColors = remember { mutableStateOf(false) } + + Row(modifier = Modifier.fillMaxWidth(0.95f).height(70.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically) { + Text(text = "Light") + + Button(modifier = Modifier.width(50.dp), onClick = { showLightColors.value = !showLightColors.value }, contentPadding = PaddingValues(0.dp)) { + Icon(Icons.Default.Create, contentDescription = "Create") + } + } + + AnimatedVisibility(modifier = Modifier.padding(start = 16.dp), visible = showLightColors.value, enter = expandVertically(), exit = shrinkVertically()) { + Column { + SettingsColorPicker( + leftText = AnnotatedString("Primary color"), + color = component.primaryLightColor.subscribeAsState().value + ) { + component.primaryLightColor.value = it + } + + SettingsColorPicker( + leftText = AnnotatedString("Secondary color"), + color = component.secondaryLightColor.subscribeAsState().value + ) { + component.secondaryLightColor.value = it + } + + SettingsColorPicker( + leftText = AnnotatedString("Tertiary color"), + color = component.tertiaryLightColor.subscribeAsState().value + ) { + component.tertiaryLightColor.value = it + } + } + } + + val showDarkColors = remember { mutableStateOf(false) } + + Row(modifier = Modifier.fillMaxWidth(0.95f).height(70.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically) { + Text(text = "Dark") + + Button(modifier = Modifier.width(50.dp), onClick = { showDarkColors.value = !showDarkColors.value }, contentPadding = PaddingValues(0.dp)) { + Icon(Icons.Default.Create, contentDescription = "Create") + } + } + + AnimatedVisibility(modifier = Modifier.padding(start = 16.dp), visible = showDarkColors.value, enter = expandVertically(), exit = shrinkVertically()) { + Column { + SettingsColorPicker( + leftText = AnnotatedString("Primary color"), + color = component.primaryDarkColor.subscribeAsState().value + ) { + component.primaryDarkColor.value = it + } + + SettingsColorPicker( + leftText = AnnotatedString("Secondary color"), + color = component.secondaryDarkColor.subscribeAsState().value + ) { + component.secondaryDarkColor.value = it + } + + SettingsColorPicker( + leftText = AnnotatedString("Tertiary color"), + color = component.tertiaryDarkColor.subscribeAsState().value + ) { + component.tertiaryDarkColor.value = it + } + } + } + } + + + + } } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/utils/SettingColorPicker.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/utils/SettingColorPicker.kt new file mode 100644 index 00000000..3e37385d --- /dev/null +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/utils/SettingColorPicker.kt @@ -0,0 +1,52 @@ +package nl.tiebe.otarium.ui.home.settings.utils + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.expandVertically +import androidx.compose.animation.shrinkVertically +import androidx.compose.foundation.layout.* +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.unit.dp +import nl.tiebe.otarium.ui.utils.colorpicker.ColorHarmonyMode +import nl.tiebe.otarium.ui.utils.colorpicker.HarmonyColorPicker +import nl.tiebe.otarium.ui.utils.colorpicker.HsvColor + +@Composable +internal fun SettingsColorPicker(modifier: Modifier = Modifier, leftText: AnnotatedString, textStyle: TextStyle = TextStyle(), color: HsvColor, onChange: ((HsvColor) -> Unit)) { + val showPicker = remember { mutableStateOf(false) } + + Column { + + Row( + modifier = Modifier.fillMaxWidth(0.95f).height(70.dp).then(modifier), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Text(text = leftText, style = textStyle.merge(LocalTextStyle.current)) + + Button( + modifier = Modifier.width(50.dp), + onClick = { showPicker.value = !showPicker.value }, + contentPadding = PaddingValues(0.dp), + colors = ButtonDefaults.buttonColors(containerColor = color.toColor()) + ) {} + } + + AnimatedVisibility(visible = showPicker.value, enter = expandVertically(), exit = shrinkVertically()) { + HarmonyColorPicker( + modifier = Modifier.height(200.dp), + harmonyMode = ColorHarmonyMode.NONE, + color = color, + onColorChanged = onChange + ) + } + + Divider() + } +} diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/item/TimetableItem.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/item/TimetableItem.kt index c4e66f4b..3738c335 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/item/TimetableItem.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/item/TimetableItem.kt @@ -9,6 +9,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.style.TextOverflow @@ -19,8 +20,7 @@ import kotlinx.datetime.* import nl.tiebe.otarium.magister.getAgendaForDay import nl.tiebe.otarium.ui.home.timetable.TimetableComponent import nl.tiebe.otarium.ui.home.timetable.days -import nl.tiebe.otarium.ui.theme.Green40 -import nl.tiebe.otarium.ui.theme.Yellow40 +import nl.tiebe.otarium.ui.theme.defaultLightTheme import nl.tiebe.otarium.ui.utils.parseHtml import nl.tiebe.otarium.ui.utils.topBottomRectBorder import kotlin.math.floor @@ -116,7 +116,7 @@ internal fun TimetableItem( modifier = Modifier .clip(RoundedCornerShape(5.dp)) .size(25.dp) - .background(Green40), + .background(Color(defaultLightTheme.secondary)), contentAlignment = Alignment.Center ) { Text( @@ -130,7 +130,7 @@ internal fun TimetableItem( modifier = Modifier .clip(RoundedCornerShape(5.dp)) .size(25.dp) - .background(Yellow40), + .background(Color(defaultLightTheme.tertiary)), contentAlignment = Alignment.Center ) { Text( diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/theme/Color.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/theme/Color.kt deleted file mode 100644 index 28e86af4..00000000 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/theme/Color.kt +++ /dev/null @@ -1,12 +0,0 @@ -package nl.tiebe.otarium.ui.theme - -import androidx.compose.ui.graphics.Color - -val Blue80 = Color(0xFF0F86E4) -val Blue40 = Color(0xFF6ACBF0) - -val Green80 = Color(0xFF1FC43B) -val Green40 = Green80 - -val Yellow80 = Color(0xFFC40808) -val Yellow40 = Yellow80 \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/theme/CustomTheme.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/theme/CustomTheme.kt new file mode 100644 index 00000000..869fbd33 --- /dev/null +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/theme/CustomTheme.kt @@ -0,0 +1,43 @@ +package nl.tiebe.otarium.ui.theme + +import androidx.compose.material3.ColorScheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.toArgb +import kotlinx.serialization.Serializable + +@Serializable +data class CustomTheme( + val primary: Int, + val secondary: Int, + val tertiary: Int +) { + fun toDarkColorScheme(): ColorScheme { + return darkColorScheme( + primary = Color(primary), + secondary = Color(secondary), + tertiary = Color(tertiary) + ) + } + + fun toLightColorScheme(): ColorScheme { + return lightColorScheme( + primary = Color(primary), + secondary = Color(secondary), + tertiary = Color(tertiary) + ) + } +} + +val defaultLightTheme = CustomTheme( + Color(0xFF6ACBF0).toArgb(), + Color(0xFF1FC43B).toArgb(), + Color(0xFFC40808).toArgb() +) + +val defaultDarkTheme = CustomTheme( + Color(0xFF0F86E4).toArgb(), + Color(0xFF1FC43B).toArgb(), + Color(0xFFC40808).toArgb() +) \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/theme/Theme.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/theme/Theme.kt index 0a7781c2..94f0a1bd 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/theme/Theme.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/theme/Theme.kt @@ -6,46 +6,32 @@ import androidx.compose.material3.darkColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color +import nl.tiebe.otarium.Data import nl.tiebe.otarium.darkModeState -val DarkColorScheme = darkColorScheme( - primary = Blue80, - secondary = Green80, - tertiary = Yellow80 -) - -val LightColorScheme = lightColorScheme( - primary = Blue40, - secondary = Green40, - tertiary = Yellow40 - - /* Other default colors to override - background = Color(0xFFFFFBFE), - surface = Color(0xFFFFFBFE), - onPrimary = Color.White, - onSecondary = Color.White, - onTertiary = Color.White, - onBackground = Color(0xFF1C1B1F), - onSurface = Color(0xFF1C1B1F), - */ -) - @Composable internal fun OtariumTheme( colorScheme: ColorScheme? = null, content: @Composable () -> Unit ) { - val finalColorScheme = if (colorScheme == null) { + val selectedColorScheme = if (Data.dynamicTheme && colorScheme != null) { + colorScheme + } else if (Data.customThemeEnabled) { + when { + darkModeState.value -> Data.customDarkTheme.toDarkColorScheme() + else -> Data.customLightTheme.toLightColorScheme() + } + } else { when { - darkModeState.value -> DarkColorScheme - else -> LightColorScheme + darkModeState.value -> defaultDarkTheme.toDarkColorScheme() + else -> defaultLightTheme.toLightColorScheme() } - } else colorScheme + } - setWindowTheme(color = finalColorScheme.primary) + setWindowTheme(color = selectedColorScheme.primary) MaterialTheme( - colorScheme = finalColorScheme, + colorScheme = selectedColorScheme, typography = Typography, content = content ) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/colorpicker/ColorWheel.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/colorpicker/ColorWheel.kt new file mode 100644 index 00000000..09278d64 --- /dev/null +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/colorpicker/ColorWheel.kt @@ -0,0 +1,60 @@ +package nl.tiebe.otarium.ui.utils.colorpicker + +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.BlendMode +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.TileMode + +@Composable +internal fun ColorWheel( + hsvColor: HsvColor, + diameter: Int +) { + val saturation = 1.0f + val value = hsvColor.value + + val radius = diameter / 2f + val alpha = 1.0f + val colorSweepGradientBrush = remember(hsvColor.value, diameter) { + val wheelColors = arrayOf( + HsvColor(0f, saturation, value, alpha), + HsvColor(60f, saturation, value, alpha), + HsvColor(120f, saturation, value, alpha), + HsvColor(180f, saturation, value, alpha), + HsvColor(240f, saturation, value, alpha), + HsvColor(300f, saturation, value, alpha), + HsvColor(360f, saturation, value, alpha) + ).map { + it.toColor() + } + Brush.sweepGradient(wheelColors, Offset(radius, radius)) + } + val saturationGradientBrush = remember(diameter) { + Brush.radialGradient( + listOf(Color.White, Color.Transparent), + Offset(radius, radius), + radius, + TileMode.Clamp + ) + } + Canvas(modifier = Modifier.fillMaxSize()) { + // draw the hue bar + drawCircle(colorSweepGradientBrush) + // draw saturation radial overlay + drawCircle(saturationGradientBrush) + // account for "brightness/value" slider + drawCircle( + hsvColor.copy( + hue = 0f, + saturation = 0f + ).toColor(), + blendMode = BlendMode.Modulate + ) + } +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/colorpicker/HarmonyColorPicker.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/colorpicker/HarmonyColorPicker.kt new file mode 100644 index 00000000..bd7e81d6 --- /dev/null +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/colorpicker/HarmonyColorPicker.kt @@ -0,0 +1,335 @@ +package nl.tiebe.otarium.ui.utils.colorpicker + +import androidx.compose.animation.core.Animatable +import androidx.compose.animation.core.Spring +import androidx.compose.animation.core.VectorConverter +import androidx.compose.animation.core.animateDpAsState +import androidx.compose.animation.core.spring +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.gestures.awaitFirstDown +import androidx.compose.foundation.gestures.drag +import androidx.compose.foundation.gestures.forEachGesture +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Slider +import androidx.compose.material3.SliderDefaults +import androidx.compose.material3.Surface +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.input.pointer.consumePositionChange +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.IntSize +import androidx.compose.ui.unit.dp +import kotlin.math.* + + +@Deprecated( + message = "Use version with programmatic updates", + replaceWith = ReplaceWith( + """ + var updatedHsvColor by remember(color) { + mutableStateOf(HsvColor.from(color)) + } + + HarmonyColorPicker( + modifier = modifier, + harmonyMode = harmonyMode, + value = updatedHsvColor, + onValueChanged = { hsvColor -> + updatedHsvColor = hsvColor + onColorChanged(hsvColor) + }, + ) + """ + ) +) +@Composable +fun HarmonyColorPicker( + modifier: Modifier = Modifier, + harmonyMode: ColorHarmonyMode, + color: Color = Color.Red, + onColorChanged: (HsvColor) -> Unit +) { + var updatedHsvColor by remember(color) { + mutableStateOf(HsvColor.from(color)) + } + HarmonyColorPicker( + modifier = modifier, + harmonyMode = harmonyMode, + color = updatedHsvColor, + onColorChanged = { hsvColor -> + updatedHsvColor = hsvColor + onColorChanged(hsvColor) + } + ) +} + +/** + * + * Will show a brightness bar if [showBrightnessBar] is true + * otherwise all colors are given the provided brightness value + */ +@Composable +fun HarmonyColorPicker( + modifier: Modifier = Modifier, + harmonyMode: ColorHarmonyMode, + color: HsvColor, + showBrightnessBar: Boolean = true, + onColorChanged: (HsvColor) -> Unit +) { + BoxWithConstraints(modifier) { + Column( + Modifier + .padding(16.dp) + .fillMaxHeight() + .fillMaxWidth() + ) { + val updatedColor by rememberUpdatedState(color) + val updatedOnValueChanged by rememberUpdatedState(onColorChanged) + + HarmonyColorPickerWithMagnifiers( + modifier = Modifier + .fillMaxWidth() + .weight(0.8f), + hsvColor = updatedColor, + onColorChanged = { + updatedOnValueChanged(it) + }, + harmonyMode = harmonyMode + ) + + if (showBrightnessBar) { + BrightnessBar( + modifier = Modifier + .padding(top = 16.dp) + .fillMaxWidth() + .weight(0.2f), + onValueChange = { value -> + updatedOnValueChanged(updatedColor.copy(value = value)) + }, + currentColor = updatedColor + ) + } + } + } +} + +@Composable +private fun HarmonyColorPickerWithMagnifiers( + modifier: Modifier = Modifier, + hsvColor: HsvColor, + onColorChanged: (HsvColor) -> Unit, + harmonyMode: ColorHarmonyMode +) { + val hsvColorUpdated by rememberUpdatedState(hsvColor) + BoxWithConstraints( + modifier = modifier + .defaultMinSize(minWidth = 48.dp) + .wrapContentSize() + .aspectRatio(1f, matchHeightConstraintsFirst = true) + + ) { + val updatedOnColorChanged by rememberUpdatedState(onColorChanged) + val diameterPx by remember(constraints.maxWidth) { + mutableStateOf(constraints.maxWidth) + } + + var animateChanges by remember { + mutableStateOf(false) + } + var currentlyChangingInput by remember { + mutableStateOf(false) + } + + fun updateColorWheel(newPosition: Offset, animate: Boolean) { + // Work out if the new position is inside the circle we are drawing, and has a + // valid color associated to it. If not, keep the current position + val newColor = colorForPosition(newPosition, IntSize(diameterPx, diameterPx), hsvColorUpdated.value) + if (newColor != null) { + animateChanges = animate + updatedOnColorChanged(newColor) + } + } + + val inputModifier = Modifier.pointerInput(diameterPx) { + forEachGesture { + awaitPointerEventScope { + val down = awaitFirstDown(false) + currentlyChangingInput = true + updateColorWheel(down.position, animate = true) + drag(down.id) { change -> + updateColorWheel(change.position, animate = false) + change.consumePositionChange() + } + currentlyChangingInput = false + } + } + } + + Box(inputModifier.fillMaxSize()) { + ColorWheel(hsvColor = hsvColor, diameter = diameterPx) + HarmonyColorMagnifiers( + diameterPx, + hsvColor, + animateChanges, + currentlyChangingInput, + harmonyMode + ) + } + } +} + +private fun colorForPosition(position: Offset, size: IntSize, value: Float): HsvColor? { + val centerX: Double = size.width / 2.0 + val centerY: Double = size.height / 2.0 + val radius: Double = min(centerX, centerY) + val xOffset: Double = position.x - centerX + val yOffset: Double = position.y - centerY + val centerOffset = hypot(xOffset, yOffset) + val rawAngle = atan2(yOffset, xOffset).toDegree() + val centerAngle = (rawAngle + 360.0) % 360.0 + return if (centerOffset <= radius) { + HsvColor( + hue = centerAngle.toFloat(), + saturation = (centerOffset / radius).toFloat(), + value = value, + alpha = 1.0f + ) + } else { + null + } +} + +@Composable +internal fun BrightnessBar( + modifier: Modifier = Modifier, + onValueChange: (Float) -> Unit, + currentColor: HsvColor +) { + Slider( + modifier = modifier, + value = currentColor.value, + onValueChange = { + onValueChange(it) + }, + colors = SliderDefaults.colors( + activeTrackColor = MaterialTheme.colorScheme.primary, + thumbColor = MaterialTheme.colorScheme.primary + ) + ) +} + + +@Composable +internal fun HarmonyColorMagnifiers( + diameterPx: Int, + hsvColor: HsvColor, + animateChanges: Boolean, + currentlyChangingInput: Boolean, + harmonyMode: ColorHarmonyMode +) { + val size = IntSize(diameterPx, diameterPx) + val position = remember(hsvColor, size) { + positionForColor(hsvColor, size) + } + + val positionAnimated = remember { + Animatable(position, typeConverter = Offset.VectorConverter) + } + LaunchedEffect(hsvColor, size, animateChanges) { + if (!animateChanges) { + positionAnimated.snapTo(positionForColor(hsvColor, size)) + } else { + positionAnimated.animateTo( + positionForColor(hsvColor, size), + animationSpec = spring(dampingRatio = Spring.DampingRatioLowBouncy) + ) + } + } + + val diameterDp = with(LocalDensity.current) { + diameterPx.toDp() + } + + val animatedDiameter = animateDpAsState( + targetValue = if (!currentlyChangingInput) { + diameterDp * diameterMainColorDragging + } else { + diameterDp * diameterMainColor + } + ) + + hsvColor.getColors(harmonyMode).forEach { color -> + val positionForColor = remember { + Animatable(positionForColor(color, size), typeConverter = Offset.VectorConverter) + } + LaunchedEffect(color, size, animateChanges) { + if (!animateChanges) { + positionForColor.snapTo(positionForColor(color, size)) + } else { + positionForColor.animateTo( + positionForColor(color, size), + animationSpec = spring(dampingRatio = Spring.DampingRatioLowBouncy) + ) + } + } + Magnifier(position = positionForColor.value, color = color, diameter = diameterDp * diameterHarmonyColor) + } + Magnifier(position = positionAnimated.value, color = hsvColor, diameter = animatedDiameter.value) +} + +@Composable +internal fun Magnifier(position: Offset, color: HsvColor, diameter: Dp) { + val offset = with(LocalDensity.current) { + Modifier.offset( + position.x.toDp() - diameter / 2, + // Align with the center of the selection circle + position.y.toDp() - diameter / 2 + ) + } + + Column(offset.size(width = diameter, height = diameter)) { + MagnifierSelectionCircle(Modifier.size(diameter), color) + } +} + +/** + * Selection circle drawn over the currently selected pixel of the color wheel. + */ +@Composable +private fun MagnifierSelectionCircle(modifier: Modifier, color: HsvColor) { + Surface( + modifier = modifier, + shape = CircleShape, + tonalElevation = 4.dp, + color = color.toColor(), + border = BorderStroke(2.dp, SolidColor(Color.White)), + content = {} + ) +} + +internal fun positionForColor(color: HsvColor, size: IntSize): Offset { + val radians = color.hue.toRadian() + val phi = color.saturation + val x: Float = ((phi * cos(radians)) + 1) / 2f + val y: Float = ((phi * sin(radians)) + 1) / 2f + return Offset( + x = (x * size.width), + y = (y * size.height) + ) +} + +private const val diameterHarmonyColor = 0.10f +private const val diameterMainColorDragging = 0.18f +private const val diameterMainColor = 0.15f + +internal fun Float.toRadian(): Float = this / 180.0f * PI.toFloat() +internal fun Double.toRadian(): Double = this / 180 * PI +internal fun Float.toDegree(): Float = this * 180.0f / PI.toFloat() +internal fun Double.toDegree(): Double = this * 180 / PI \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/colorpicker/HsvColor.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/colorpicker/HsvColor.kt new file mode 100644 index 00000000..ee8dc446 --- /dev/null +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/colorpicker/HsvColor.kt @@ -0,0 +1,164 @@ +package nl.tiebe.otarium.ui.utils.colorpicker + +import androidx.compose.runtime.saveable.Saver +import androidx.compose.runtime.saveable.listSaver +import androidx.compose.ui.graphics.Color +import com.github.ajalt.colormath.model.HSV +import com.github.ajalt.colormath.model.RGB + +enum class ColorHarmonyMode { + NONE, + COMPLEMENTARY, + ANALOGOUS, + SPLIT_COMPLEMENTARY, + TRIADIC, + TETRADIC, + MONOCHROMATIC, + SHADES; +} + +/** + * A representation of Color in Hue, Saturation and Value form. + */ +data class HsvColor( + + // from = 0.0, to = 360.0 + val hue: Float, + + // from = 0.0, to = 1.0 + val saturation: Float, + + // from = 0.0, to = 1.0 + val value: Float, + + // from = 0.0, to = 1.0 + val alpha: Float +) { + + fun toColor(): Color { + val hsv = HSV(hue, saturation, value, alpha) + val rgb = hsv.toSRGB() + return Color(rgb.redInt, rgb.greenInt, rgb.blueInt, rgb.alphaInt) + } + + fun getComplementaryColor(): List { + return listOf( + this.copy(saturation = (saturation + 0.1f).coerceAtMost(1f), value = (value + 0.3f).coerceIn(0.0f, 1f)), + this.copy(saturation = (saturation - 0.1f).coerceAtMost(1f), value = (value - 0.3f).coerceIn(0.0f, 1f)), + this.copy(hue = (hue + 180) % 360), // actual complementary + this.copy(hue = (hue + 180) % 360, saturation = (saturation + 0.2f).coerceAtMost(1f), value = (value - 0.3f).coerceIn(0.0f, 1f)) + ) + } + + fun getSplitComplementaryColors(): List { + return listOf( + this.copy(hue = (hue + 150) % 360, saturation = (saturation - 0.05f).coerceIn(0.0f, 1f), value = (value - 0.3f).coerceIn(0.0f, 1f)), + this.copy(hue = (hue + 210) % 360, saturation = (saturation - 0.05f).coerceIn(0.0f, 1f), value = (value - 0.3f).coerceIn(0.0f, 1f)), + this.copy(hue = (hue + 150) % 360), // actual + this.copy(hue = (hue + 210) % 360) // actual + ) + } + + fun getTriadicColors(): List { + return listOf( + this.copy(hue = (hue + 120) % 360, saturation = (saturation - 0.05f).coerceIn(0.0f, 1f), value = (value - 0.3f).coerceIn(0.0f, 1f)), + this.copy(hue = (hue + 120) % 360), + this.copy(hue = (hue + 240) % 360, saturation = (saturation - 0.05f).coerceIn(0.0f, 1f), value = (value - 0.3f).coerceIn(0.0f, 1f)), + this.copy(hue = (hue + 240) % 360) + ) + } + + fun getTetradicColors(): List { + return listOf( + this.copy(saturation = (saturation + 0.2f).coerceIn(0.0f, 1f)), // bonus one + this.copy(hue = (hue + 90) % 360), + this.copy(hue = (hue + 180) % 360), + this.copy(hue = (hue + 270) % 360) + ) + } + + fun getAnalagousColors(): List { + return listOf( + this.copy(hue = (hue + 30) % 360), + this.copy(hue = (hue + 60) % 360), + this.copy(hue = (hue + 90) % 360), + this.copy(hue = (hue + 120) % 360) + ) + } + + fun getMonochromaticColors(): List { + return listOf( + this.copy(saturation = (saturation + 0.2f).mod(1f)), + this.copy(saturation = (saturation + 0.4f).mod(1f)), + this.copy(saturation = (saturation + 0.6f).mod(1f)), + this.copy(saturation = (saturation + 0.8f).mod(1f)) + ) + } + + fun getShadeColors(): List { + return listOf( + this.copy(value = (value - 0.10f).mod(1.0f).coerceAtLeast(0.2f)), + this.copy(value = (value + 0.55f).mod(1.0f).coerceAtLeast(0.55f)), + this.copy(value = (value + 0.30f).mod(1.0f).coerceAtLeast(0.3f)), + this.copy(value = (value + 0.05f).mod(1.0f).coerceAtLeast(0.2f)) + ) + } + + fun getColors(colorHarmonyMode: ColorHarmonyMode): List { + return when (colorHarmonyMode) { + ColorHarmonyMode.NONE -> emptyList() + ColorHarmonyMode.COMPLEMENTARY -> getComplementaryColor() + ColorHarmonyMode.ANALOGOUS -> getAnalagousColors() + ColorHarmonyMode.SPLIT_COMPLEMENTARY -> getSplitComplementaryColors() + ColorHarmonyMode.TRIADIC -> getTriadicColors() + ColorHarmonyMode.TETRADIC -> getTetradicColors() + ColorHarmonyMode.MONOCHROMATIC -> getMonochromaticColors() + ColorHarmonyMode.SHADES -> getShadeColors() + } + } + + companion object { + + val DEFAULT = HsvColor(360f, 1.0f, 1.0f, 1.0f) + + /** + * the color math hsv to local hsv color + */ + private fun HSV.toColor(): HsvColor { + return HsvColor( + hue = if (this.h.isNaN()) 0f else this.h, + saturation = this.s, + value = this.v, + alpha = this.alpha + ) + } + + fun from(color: Color): HsvColor { + return RGB( + color.red, + color.green, + color.blue, + color.alpha + ).toHSV().toColor() + } + + val Saver: Saver = listSaver( + save = { + listOf( + it.hue, + it.saturation, + it.value, + it.alpha + ) + }, + restore = { + HsvColor( + it[0], + it[1], + it[2], + it[3] + ) + } + ) + } +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/Utils.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/Utils.kt index 41d5b55b..c68a34e5 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/Utils.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/Utils.kt @@ -13,6 +13,8 @@ expect fun getDownloadFileLocation(id: String, fileName: String): ByteWriteChann expect fun openFileFromCache(id: String, fileName: String) +expect fun dynamicColorsPossible(): Boolean + fun LocalDateTime.toFormattedString(): String { val dateTime = this.toInstant(TimeZone.UTC) diff --git a/shared/src/commonMain/resources/MR/base/strings.xml b/shared/src/commonMain/resources/MR/base/strings.xml index 5c52a67b..a6349fb0 100644 --- a/shared/src/commonMain/resources/MR/base/strings.xml +++ b/shared/src/commonMain/resources/MR/base/strings.xml @@ -4,10 +4,14 @@ Grades Notifications for new grades - The app is now waiting for new grades and will notify you when it receives one. + ELO + Settings + Messages Timetable - Log in + Grades + + Otarium Welcome to Otarium Notifications @@ -16,18 +20,23 @@ You will need to sign in with your Magister account on the next screen to use the app Ads Would you like unobtrusive ads in this app? It would really help me out! + Show ads I am older than 16 - Settings + You don\'t need to be 16 to use the app or see ads. + + Log in + + mon tue wed thu fri - You don\'t need to be 16 to use the app or see ads. It\'s just about the type of ads you\'ll see. - Grades sat sun + + Averages New grade New average @@ -48,7 +57,7 @@ Entered on Amount of decimals shown in averages: UI - Messages + Colors Subject From @@ -57,7 +66,6 @@ CC BCC - ELO Study guides Assignments Learning resources diff --git a/shared/src/commonMain/resources/MR/nl/strings.xml b/shared/src/commonMain/resources/MR/nl/strings.xml index 821a3d47..3653d35f 100644 --- a/shared/src/commonMain/resources/MR/nl/strings.xml +++ b/shared/src/commonMain/resources/MR/nl/strings.xml @@ -78,4 +78,7 @@ Beoordeling Ingeleverd op Opmerkingen + + Kleuren + \ No newline at end of file diff --git a/shared/src/iosMain/kotlin/nl/tiebe/otarium/utils/Utils.kt b/shared/src/iosMain/kotlin/nl/tiebe/otarium/utils/Utils.kt index 7e45d74e..1a42ff83 100644 --- a/shared/src/iosMain/kotlin/nl/tiebe/otarium/utils/Utils.kt +++ b/shared/src/iosMain/kotlin/nl/tiebe/otarium/utils/Utils.kt @@ -20,4 +20,8 @@ actual fun getDownloadFileLocation(id: String, fileName: String): ByteWriteChann actual fun openFileFromCache(id: String, fileName: String) { TODO() +} + +actual fun dynamicColorsPossible(): Boolean { + return false } \ No newline at end of file From 0b84aaf22d9e11fbf4cd580a3b7003aa4f567b25 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Mon, 8 May 2023 19:13:47 +0200 Subject: [PATCH 23/82] changes --- .../nl/tiebe/otarium/utils/UtilsAndroid.kt | 6 +-- .../home/messages/message/MessageComponent.kt | 6 +-- .../kotlin/nl/tiebe/otarium/utils/Utils.kt | 6 ++- .../kotlin/nl/tiebe/otarium/utils/Utils.kt | 41 +++++++++++++++++-- 4 files changed, 47 insertions(+), 12 deletions(-) diff --git a/shared/src/androidMain/kotlin/nl/tiebe/otarium/utils/UtilsAndroid.kt b/shared/src/androidMain/kotlin/nl/tiebe/otarium/utils/UtilsAndroid.kt index 33c24743..1d6b25d4 100644 --- a/shared/src/androidMain/kotlin/nl/tiebe/otarium/utils/UtilsAndroid.kt +++ b/shared/src/androidMain/kotlin/nl/tiebe/otarium/utils/UtilsAndroid.kt @@ -3,8 +3,6 @@ package nl.tiebe.otarium.utils import android.content.Intent import android.net.Uri import androidx.core.content.FileProvider -import io.ktor.util.cio.* -import io.ktor.utils.io.* import nl.tiebe.otarium.utils.ui.Android import java.io.File @@ -31,14 +29,14 @@ actual fun openUrl(url: String) { Android.context.startActivity(intent) } -actual fun getDownloadFileLocation(id: String, fileName: String): ByteWriteChannel { +actual fun writeFile(id: String, fileName: String, data: ByteArray) { val directory = File(Android.context.cacheDir, id) if (!directory.exists()) { directory.mkdir() } val file = File(directory, fileName) - return file.writeChannel() + file.writeBytes(data) } actual fun openFileFromCache(id: String, fileName: String) { diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/message/MessageComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/message/MessageComponent.kt index e246dc8b..c7159467 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/message/MessageComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/message/MessageComponent.kt @@ -13,9 +13,9 @@ import kotlinx.coroutines.launch import nl.tiebe.otarium.Data import nl.tiebe.otarium.ui.home.messages.MessagesComponent import nl.tiebe.otarium.ui.root.componentCoroutineScope -import nl.tiebe.otarium.utils.getDownloadFileLocation import nl.tiebe.otarium.utils.openFileFromCache import nl.tiebe.otarium.utils.requestGET +import nl.tiebe.otarium.utils.writeFile interface MessageComponent { val parentComponent: MessagesComponent @@ -61,9 +61,9 @@ class DefaultMessageComponent( onDownload = { bytesSentTotal, contentLength -> attachmentDownloadProgress.value = attachmentDownloadProgress.value + Pair(attachment.id, bytesSentTotal.toFloat() / contentLength.toFloat()) } - ).bodyAsChannel() + ).readBytes() - response.copyAndClose(getDownloadFileLocation(attachment.id.toString(), attachment.name)) + writeFile(attachment.id.toString(), attachment.name, response) openFileFromCache(attachment.id.toString(), attachment.name) attachmentDownloadProgress.value = attachmentDownloadProgress.value.toMutableMap().also { diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/Utils.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/Utils.kt index 41d5b55b..992ad491 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/Utils.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/Utils.kt @@ -1,5 +1,9 @@ package nl.tiebe.otarium.utils +import kotlinx.datetime.LocalDateTime +import kotlinx.datetime.TimeZone +import kotlinx.datetime.toInstant +import kotlinx.datetime.toLocalDateTime import io.ktor.utils.io.* import kotlinx.datetime.* @@ -9,7 +13,7 @@ expect fun getClipboardText(): String expect fun openUrl(url: String) -expect fun getDownloadFileLocation(id: String, fileName: String): ByteWriteChannel +expect fun writeFile(id: String, fileName: String, data: ByteArray) expect fun openFileFromCache(id: String, fileName: String) diff --git a/shared/src/iosMain/kotlin/nl/tiebe/otarium/utils/Utils.kt b/shared/src/iosMain/kotlin/nl/tiebe/otarium/utils/Utils.kt index 7e45d74e..424b9e19 100644 --- a/shared/src/iosMain/kotlin/nl/tiebe/otarium/utils/Utils.kt +++ b/shared/src/iosMain/kotlin/nl/tiebe/otarium/utils/Utils.kt @@ -1,6 +1,14 @@ package nl.tiebe.otarium.utils -import io.ktor.utils.io.* +import kotlinx.cinterop.allocArrayOf +import kotlinx.cinterop.memScoped +import platform.Foundation.NSCachesDirectory +import platform.Foundation.NSData +import platform.Foundation.NSFileManager +import platform.Foundation.NSURL +import platform.Foundation.NSUserDomainMask +import platform.Foundation.create +import platform.UIKit.UIDocumentInteractionController actual fun copyToClipboard(text: String) { @@ -14,10 +22,35 @@ actual fun openUrl(url: String) { } -actual fun getDownloadFileLocation(id: String, fileName: String): ByteWriteChannel { - TODO("Not yet implemented") +actual fun writeFile(id: String, fileName: String, data: ByteArray) { + memScoped { + val nsData = NSData.create(bytes = allocArrayOf(data), length = data.size.toULong()) + + val cacheDir = NSFileManager.defaultManager.URLForDirectory( + NSCachesDirectory, + NSUserDomainMask, + null, + false, + null + )!! + + val directory = cacheDir.path!! + "/$id" + val file = "$directory/$fileName" + + NSFileManager.defaultManager.createDirectoryAtPath(directory, true, null, null) + NSFileManager.defaultManager.createFileAtPath(file, nsData, null) + } } actual fun openFileFromCache(id: String, fileName: String) { - TODO() + val cacheDir = NSFileManager.defaultManager.URLForDirectory(NSCachesDirectory, NSUserDomainMask, null, false, null)!! + + val directory = cacheDir.path!! + "/$id" + val file = "$directory/$fileName" + + UIDocumentInteractionController().apply { + URL = NSURL.fileURLWithPath(file) + presentPreviewAnimated(true) + } + } \ No newline at end of file From 40729804c174ab77f81e3cd87b14d787d51e4ce5 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Mon, 8 May 2023 19:35:24 +0200 Subject: [PATCH 24/82] colors --- .../items/ui/colors/ColorChildComponent.kt | 8 +++++-- .../kotlin/nl/tiebe/otarium/ui/theme/Theme.kt | 24 ++++++++++++++++--- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/colors/ColorChildComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/colors/ColorChildComponent.kt index 070b46fc..c12d9c53 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/colors/ColorChildComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/colors/ColorChildComponent.kt @@ -11,6 +11,8 @@ import nl.tiebe.otarium.ui.theme.defaultDarkTheme import nl.tiebe.otarium.ui.theme.defaultLightTheme import nl.tiebe.otarium.ui.utils.colorpicker.HsvColor +val colorSchemeChanged = MutableValue(false) + interface ColorChildComponent { fun navigate(child: SettingsComponent.Config) @@ -42,13 +44,13 @@ interface ColorChildComponent { tertiary = tertiaryLightColor.value.toColor().toArgb() ) - println(Data.customLightTheme) - Data.customDarkTheme = CustomTheme( primary = primaryDarkColor.value.toColor().toArgb(), secondary = secondaryDarkColor.value.toColor().toArgb(), tertiary = tertiaryDarkColor.value.toColor().toArgb() ) + + colorSchemeChanged.value = !colorSchemeChanged.value } } @@ -79,10 +81,12 @@ class DefaultColorChildComponent( init { dynamicColorState.subscribe { Data.dynamicTheme = it + colorSchemeChanged.value = !colorSchemeChanged.value if (it) customColorScheme.value = false } customColorScheme.subscribe { Data.customThemeEnabled = it + colorSchemeChanged.value = !colorSchemeChanged.value if (it) dynamicColorState.value = false } } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/theme/Theme.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/theme/Theme.kt index 94f0a1bd..1e88c306 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/theme/Theme.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/theme/Theme.kt @@ -2,19 +2,20 @@ package nl.tiebe.otarium.ui.theme import androidx.compose.material3.ColorScheme import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.darkColorScheme -import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.graphics.Color +import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState import nl.tiebe.otarium.Data import nl.tiebe.otarium.darkModeState +import nl.tiebe.otarium.ui.home.settings.items.ui.colors.colorSchemeChanged @Composable internal fun OtariumTheme( colorScheme: ColorScheme? = null, content: @Composable () -> Unit ) { - val selectedColorScheme = if (Data.dynamicTheme && colorScheme != null) { + var selectedColorScheme = if (Data.dynamicTheme && colorScheme != null) { colorScheme } else if (Data.customThemeEnabled) { when { @@ -28,6 +29,23 @@ internal fun OtariumTheme( } } + LaunchedEffect(colorSchemeChanged.subscribeAsState().value) { + selectedColorScheme = if (Data.dynamicTheme && colorScheme != null) { + colorScheme + } else if (Data.customThemeEnabled) { + when { + darkModeState.value -> Data.customDarkTheme.toDarkColorScheme() + else -> Data.customLightTheme.toLightColorScheme() + } + } else { + when { + darkModeState.value -> defaultDarkTheme.toDarkColorScheme() + else -> defaultLightTheme.toLightColorScheme() + } + } + } + + setWindowTheme(color = selectedColorScheme.primary) MaterialTheme( From 1d514608d98e639f46f2c5437524e3daf94c225f Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Mon, 8 May 2023 19:55:16 +0200 Subject: [PATCH 25/82] colors --- .../items/ui/colors/ColorChildScreen.kt | 13 +-- .../commonMain/resources/MR/la/strings.xml | 90 +++++++++++++++++++ 2 files changed, 98 insertions(+), 5 deletions(-) create mode 100644 shared/src/commonMain/resources/MR/la/strings.xml diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/colors/ColorChildScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/colors/ColorChildScreen.kt index 2b44ab46..e8eaa566 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/colors/ColorChildScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/colors/ColorChildScreen.kt @@ -23,6 +23,7 @@ import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState import nl.tiebe.otarium.ui.home.settings.utils.SettingRowIconButton import nl.tiebe.otarium.ui.home.settings.utils.SettingsColorPicker import nl.tiebe.otarium.ui.home.settings.utils.SettingsRowToggle +import nl.tiebe.otarium.utils.dynamicColorsPossible @Composable internal fun ColorChildScreen(component: ColorChildComponent) { @@ -35,11 +36,13 @@ internal fun ColorChildScreen(component: ColorChildComponent) { val dynamicColorScheme = component.dynamicColorState.subscribeAsState() val customColorScheme = component.customColorScheme.subscribeAsState() - SettingsRowToggle( - leftText = AnnotatedString("Dynamic color scheme"), - checked = dynamicColorScheme.value, - ) { - component.dynamicColorState.value = it + if (dynamicColorsPossible()) { + SettingsRowToggle( + leftText = AnnotatedString("Dynamic color scheme"), + checked = dynamicColorScheme.value, + ) { + component.dynamicColorState.value = it + } } SettingsRowToggle( diff --git a/shared/src/commonMain/resources/MR/la/strings.xml b/shared/src/commonMain/resources/MR/la/strings.xml new file mode 100644 index 00000000..7103a448 --- /dev/null +++ b/shared/src/commonMain/resources/MR/la/strings.xml @@ -0,0 +1,90 @@ + + Otarium + + Grades + Notifications for new grades + + ELO + Settings + Messages + Timetable + Grades + + + Otarium + Welcome to Otarium + Notifications + Otarium gives you notifications for grades. You can always disable these in the settings. + Login + You will need to sign in with your Magister account on the next screen to use the app + Ads + Would you like unobtrusive ads in this app? It would really help me out! + + Show ads + I am older than 16 + You don\'t need to be 16 to use the app or see ads. + + Log in + + + mon + tue + wed + thu + fri + sat + sun + + + Averages + New grade + New average + Weight + New grade + New average + Calculate + Grade + Average + Calculation + Graph + Report a bug + Before reporting a bug, try clearing your cache by pressing the button below. + If the bug persists, please contact us at otarium@tiebe.dev + Switch user + New account + Advertisements + Entered on + Amount of decimals shown in averages: + UI + Colors + + Subject + From + Date + To + CC + BCC + + Study guides + Assignments + Learning resources + + Loading... + Absence + + Title + Version + Deadline + Description + + All + Open + Submitted + Closed + Graded + + Graded on + Grade + Submitted on + Feedback + \ No newline at end of file From 22ac3f4311323e0ec672fa80135c7e59e50243b0 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Mon, 8 May 2023 20:06:06 +0200 Subject: [PATCH 26/82] CompleteKotlin, you're awesome --- build.gradle.kts | 4 ++++ buildSrc/src/main/kotlin/Dependencies.kt | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/build.gradle.kts b/build.gradle.kts index 9d5f4da1..6f3ea53f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,3 +1,7 @@ +plugins { + id(completeKotlin) version Version.complete_kotlin +} + buildscript { repositories { gradlePluginPortal() diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt index 27272263..05460c13 100755 --- a/buildSrc/src/main/kotlin/Dependencies.kt +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -22,6 +22,8 @@ object Version { const val admob = "22.0.0" const val guava = "31.1-android" const val guava_coroutines = "1.7.0-RC" + + const val complete_kotlin = "1.1.0" } object Compose { @@ -95,6 +97,8 @@ const val admob = "com.google.android.gms:play-services-ads:${Version.admob}" const val magisterAPI = "dev.tiebe:magisterapi:${Version.magister}" +const val completeKotlin = "com.louiscad.complete-kotlin" + object Guava { const val core = "com.google.guava:guava:${Version.guava}" const val coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-guava:${Version.guava_coroutines}" From ac96e269c35729991a7ead8f827b3a755e9fdc3d Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Mon, 8 May 2023 22:04:20 +0200 Subject: [PATCH 27/82] Create ios.yml --- .github/workflows/ios.yml | 47 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 .github/workflows/ios.yml diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml new file mode 100644 index 00000000..5fa50777 --- /dev/null +++ b/.github/workflows/ios.yml @@ -0,0 +1,47 @@ +name: iOS starter workflow + +on: + + +jobs: + build: + name: Build and Test default scheme using any available iPhone simulator + runs-on: macos-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Build and publish + uses: sparkfabrik/ios-build-action@v2.1.0 + with: + upload-to-testflight: true + increment-build-number: true + build-pods: true + pods-path: "iosApp/Podfile" + configuration: Release + export-method: app-store + workspace-path: iosApp/iosApp.xcodeproj + project-path: iosApp/iosApp.xcworkspace + scheme: iosApp + output-path: build-${{ github.sha }}.ipa + apple-key-id: ${{ secrets.APPLE_KEY_ID }} + apple-key-issuer-id: ${{ secrets.APPLE_KEY_ISSUER_ID }} + apple-key-content: ${{ secrets.APPLE_KEY_CONTENT }} + team-id: ${{ secrets.TEAM_ID }} + team-name: ${{ secrets.TEAM_NAME }} + match-password: ${{ secrets.MATCH_PASSWORD }} + match-git-url: ${{ secrets.MATCH_GIT_URL }} + match-git-basic-authorization: ${{ secrets.MATCH_GIT_BASIC_AUTHORIZATION }} + match-build-type: "appstore" + browserstack-upload: false + browserstack-username: ${{ secrets.BROWSERSTACK_USERNAME }} + browserstack-access-key: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} + fastlane-env: stage + ios-app-id: nl.tiebe.otarium + + - name: Upload a Build Artifact + uses: actions/upload-artifact@v3.1.2 + with: + name: Otarium.ipa + path: build-${{ github.sha }}.ipa From cab1063ca2f218e59ab4b7bdd86e6532736e76d1 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Mon, 8 May 2023 22:05:05 +0200 Subject: [PATCH 28/82] Update ios.yml --- .github/workflows/ios.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml index 5fa50777..e0c55441 100644 --- a/.github/workflows/ios.yml +++ b/.github/workflows/ios.yml @@ -1,7 +1,7 @@ name: iOS starter workflow on: - + workflow_dispatch: jobs: build: From 86845c9ed14cc5e6e237dd0b3758d091fc629dba Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Mon, 8 May 2023 22:20:52 +0200 Subject: [PATCH 29/82] Matchfile --- iosApp/Matchfile | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 iosApp/Matchfile diff --git a/iosApp/Matchfile b/iosApp/Matchfile new file mode 100644 index 00000000..22e64645 --- /dev/null +++ b/iosApp/Matchfile @@ -0,0 +1,13 @@ +git_url("https://github.com/Tiebe/match") + +storage_mode("git") + +type("development") # The default type, can be: appstore, adhoc, enterprise or development + +# app_identifier(["tools.fastlane.app", "tools.fastlane.app2"]) +# username("user@fastlane.tools") # Your Apple Developer Portal username + +# For all available options run `fastlane match --help` +# Remove the # in the beginning of the line to enable the other options + +# The docs are available on https://docs.fastlane.tools/actions/match From d05a02b57aec834b8547cec3220b942293e8567a Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Mon, 8 May 2023 22:24:57 +0200 Subject: [PATCH 30/82] Update Matchfile --- iosApp/Matchfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iosApp/Matchfile b/iosApp/Matchfile index 22e64645..43037156 100644 --- a/iosApp/Matchfile +++ b/iosApp/Matchfile @@ -4,7 +4,7 @@ storage_mode("git") type("development") # The default type, can be: appstore, adhoc, enterprise or development -# app_identifier(["tools.fastlane.app", "tools.fastlane.app2"]) +app_identifier("nl.tiebe.otarium") # username("user@fastlane.tools") # Your Apple Developer Portal username # For all available options run `fastlane match --help` From 4f4d0b3799bed2da19f56d68cae13c11823742d2 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Mon, 8 May 2023 22:28:15 +0200 Subject: [PATCH 31/82] Update ios.yml --- .github/workflows/ios.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml index e0c55441..5c6ccd4c 100644 --- a/.github/workflows/ios.yml +++ b/.github/workflows/ios.yml @@ -38,7 +38,7 @@ jobs: browserstack-username: ${{ secrets.BROWSERSTACK_USERNAME }} browserstack-access-key: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} fastlane-env: stage - ios-app-id: nl.tiebe.otarium + ios-app-id: ${{ secrets.APP_ID }} - name: Upload a Build Artifact uses: actions/upload-artifact@v3.1.2 From 979a1914aa4dbe1296844a39ee9e92fc0d44ce3d Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Mon, 8 May 2023 22:29:56 +0200 Subject: [PATCH 32/82] Update ios.yml --- .github/workflows/ios.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml index 5c6ccd4c..863ba27d 100644 --- a/.github/workflows/ios.yml +++ b/.github/workflows/ios.yml @@ -39,6 +39,8 @@ jobs: browserstack-access-key: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} fastlane-env: stage ios-app-id: ${{ secrets.APP_ID }} + env: + IOS_APP_ID: ${{ secrets.APP_ID }} - name: Upload a Build Artifact uses: actions/upload-artifact@v3.1.2 From e3b81c8f3ec9693164196cd6095c0584c5259c36 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Mon, 8 May 2023 22:32:41 +0200 Subject: [PATCH 33/82] Update ios.yml --- .github/workflows/ios.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml index 863ba27d..0ea1d3a4 100644 --- a/.github/workflows/ios.yml +++ b/.github/workflows/ios.yml @@ -40,7 +40,7 @@ jobs: fastlane-env: stage ios-app-id: ${{ secrets.APP_ID }} env: - IOS_APP_ID: ${{ secrets.APP_ID }} + APP_ID: ${{ secrets.APP_ID }} - name: Upload a Build Artifact uses: actions/upload-artifact@v3.1.2 From adb6c66312560a06db457bbb227e3ec30b483e68 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Mon, 8 May 2023 22:36:37 +0200 Subject: [PATCH 34/82] Update ios.yml --- .github/workflows/ios.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml index 0ea1d3a4..7c710a09 100644 --- a/.github/workflows/ios.yml +++ b/.github/workflows/ios.yml @@ -21,8 +21,8 @@ jobs: pods-path: "iosApp/Podfile" configuration: Release export-method: app-store - workspace-path: iosApp/iosApp.xcodeproj - project-path: iosApp/iosApp.xcworkspace + workspace-path: iosApp/iosApp.xcworkspace + project-path: iosApp/iosApp.xcodeproj scheme: iosApp output-path: build-${{ github.sha }}.ipa apple-key-id: ${{ secrets.APPLE_KEY_ID }} From 27bad0a8a4be2f6930a2b687ba564580089cd271 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Mon, 8 May 2023 22:42:08 +0200 Subject: [PATCH 35/82] Update ios.yml --- .github/workflows/ios.yml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml index 7c710a09..56ff7c24 100644 --- a/.github/workflows/ios.yml +++ b/.github/workflows/ios.yml @@ -1,16 +1,25 @@ -name: iOS starter workflow +name: iOS CI/CD on: workflow_dispatch: jobs: build: - name: Build and Test default scheme using any available iPhone simulator + name: Build and publish on app store runs-on: macos-latest steps: - name: Checkout uses: actions/checkout@v3 + + - name: set up JDK 11 + uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'temurin' + + - name: Grant execute permission for gradlew + run: chmod +x gradlew - name: Build and publish uses: sparkfabrik/ios-build-action@v2.1.0 From 5b413b4068b71b2c922f3218eb353175f9829995 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Mon, 8 May 2023 22:42:49 +0200 Subject: [PATCH 36/82] Update android.yml --- .github/workflows/android.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 123578af..4b74192b 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -1,7 +1,7 @@ name: Android CI on: - push: + workflow_dispatch: jobs: build: From a729edbbcf30ebca44c023117f15141ec699635b Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Mon, 8 May 2023 22:48:29 +0200 Subject: [PATCH 37/82] Update ios.yml --- .github/workflows/ios.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml index 56ff7c24..b0bce0ee 100644 --- a/.github/workflows/ios.yml +++ b/.github/workflows/ios.yml @@ -50,8 +50,15 @@ jobs: ios-app-id: ${{ secrets.APP_ID }} env: APP_ID: ${{ secrets.APP_ID }} + + - name: Upload logs + if: always() + uses: actions/upload-artifact@v3.1.2 + with: + name: build.log + path: /Users/runner/Library/Logs/gym/Otarium-iosApp.log - - name: Upload a Build Artifact + - name: Upload IPA uses: actions/upload-artifact@v3.1.2 with: name: Otarium.ipa From ce6a1bb62c431b1120b05ae123a2db8ec8031499 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Tue, 9 May 2023 17:27:25 +0200 Subject: [PATCH 38/82] fastlane --- iosApp/Gemfile | 3 + iosApp/Gemfile.lock | 218 ++++++++++++++++++ iosApp/fastlane/Appfile | 8 + iosApp/fastlane/Deliverfile | 3 + iosApp/fastlane/Fastfile | 25 ++ iosApp/fastlane/metadata/copyright.txt | 1 + .../en-US/apple_tv_privacy_policy.txt | 1 + .../fastlane/metadata/en-US/description.txt | 5 + iosApp/fastlane/metadata/en-US/keywords.txt | 1 + .../fastlane/metadata/en-US/marketing_url.txt | 1 + iosApp/fastlane/metadata/en-US/name.txt | 1 + .../fastlane/metadata/en-US/privacy_url.txt | 1 + .../metadata/en-US/promotional_text.txt | 1 + .../fastlane/metadata/en-US/release_notes.txt | 1 + iosApp/fastlane/metadata/en-US/subtitle.txt | 1 + .../fastlane/metadata/en-US/support_url.txt | 1 + iosApp/fastlane/metadata/primary_category.txt | 1 + .../metadata/primary_first_sub_category.txt | 1 + .../metadata/primary_second_sub_category.txt | 1 + .../review_information/demo_password.txt | 1 + .../metadata/review_information/demo_user.txt | 1 + .../review_information/email_address.txt | 1 + .../review_information/first_name.txt | 1 + .../metadata/review_information/last_name.txt | 1 + .../metadata/review_information/notes.txt | 1 + .../review_information/phone_number.txt | 1 + .../fastlane/metadata/secondary_category.txt | 1 + .../metadata/secondary_first_sub_category.txt | 1 + .../secondary_second_sub_category.txt | 1 + iosApp/fastlane/screenshots/README.txt | 30 +++ .../xcshareddata/xcschemes/iosApp.xcscheme | 77 +++++++ 31 files changed, 392 insertions(+) create mode 100644 iosApp/Gemfile create mode 100644 iosApp/Gemfile.lock create mode 100644 iosApp/fastlane/Appfile create mode 100644 iosApp/fastlane/Deliverfile create mode 100644 iosApp/fastlane/Fastfile create mode 100644 iosApp/fastlane/metadata/copyright.txt create mode 100644 iosApp/fastlane/metadata/en-US/apple_tv_privacy_policy.txt create mode 100644 iosApp/fastlane/metadata/en-US/description.txt create mode 100644 iosApp/fastlane/metadata/en-US/keywords.txt create mode 100644 iosApp/fastlane/metadata/en-US/marketing_url.txt create mode 100644 iosApp/fastlane/metadata/en-US/name.txt create mode 100644 iosApp/fastlane/metadata/en-US/privacy_url.txt create mode 100644 iosApp/fastlane/metadata/en-US/promotional_text.txt create mode 100644 iosApp/fastlane/metadata/en-US/release_notes.txt create mode 100644 iosApp/fastlane/metadata/en-US/subtitle.txt create mode 100644 iosApp/fastlane/metadata/en-US/support_url.txt create mode 100644 iosApp/fastlane/metadata/primary_category.txt create mode 100644 iosApp/fastlane/metadata/primary_first_sub_category.txt create mode 100644 iosApp/fastlane/metadata/primary_second_sub_category.txt create mode 100644 iosApp/fastlane/metadata/review_information/demo_password.txt create mode 100644 iosApp/fastlane/metadata/review_information/demo_user.txt create mode 100644 iosApp/fastlane/metadata/review_information/email_address.txt create mode 100644 iosApp/fastlane/metadata/review_information/first_name.txt create mode 100644 iosApp/fastlane/metadata/review_information/last_name.txt create mode 100644 iosApp/fastlane/metadata/review_information/notes.txt create mode 100644 iosApp/fastlane/metadata/review_information/phone_number.txt create mode 100644 iosApp/fastlane/metadata/secondary_category.txt create mode 100644 iosApp/fastlane/metadata/secondary_first_sub_category.txt create mode 100644 iosApp/fastlane/metadata/secondary_second_sub_category.txt create mode 100644 iosApp/fastlane/screenshots/README.txt create mode 100644 iosApp/iosApp.xcodeproj/xcshareddata/xcschemes/iosApp.xcscheme diff --git a/iosApp/Gemfile b/iosApp/Gemfile new file mode 100644 index 00000000..7a118b49 --- /dev/null +++ b/iosApp/Gemfile @@ -0,0 +1,3 @@ +source "https://rubygems.org" + +gem "fastlane" diff --git a/iosApp/Gemfile.lock b/iosApp/Gemfile.lock new file mode 100644 index 00000000..e1a4bc35 --- /dev/null +++ b/iosApp/Gemfile.lock @@ -0,0 +1,218 @@ +GEM + remote: https://rubygems.org/ + specs: + CFPropertyList (3.0.6) + rexml + addressable (2.8.4) + public_suffix (>= 2.0.2, < 6.0) + artifactory (3.0.15) + atomos (0.1.3) + aws-eventstream (1.2.0) + aws-partitions (1.761.0) + aws-sdk-core (3.172.0) + aws-eventstream (~> 1, >= 1.0.2) + aws-partitions (~> 1, >= 1.651.0) + aws-sigv4 (~> 1.5) + jmespath (~> 1, >= 1.6.1) + aws-sdk-kms (1.64.0) + aws-sdk-core (~> 3, >= 3.165.0) + aws-sigv4 (~> 1.1) + aws-sdk-s3 (1.122.0) + aws-sdk-core (~> 3, >= 3.165.0) + aws-sdk-kms (~> 1) + aws-sigv4 (~> 1.4) + aws-sigv4 (1.5.2) + aws-eventstream (~> 1, >= 1.0.2) + babosa (1.0.4) + claide (1.1.0) + colored (1.2) + colored2 (3.1.2) + commander (4.6.0) + highline (~> 2.0.0) + declarative (0.0.20) + digest-crc (0.6.4) + rake (>= 12.0.0, < 14.0.0) + domain_name (0.5.20190701) + unf (>= 0.0.5, < 1.0.0) + dotenv (2.8.1) + emoji_regex (3.2.3) + excon (0.99.0) + faraday (1.10.3) + faraday-em_http (~> 1.0) + faraday-em_synchrony (~> 1.0) + faraday-excon (~> 1.1) + faraday-httpclient (~> 1.0) + faraday-multipart (~> 1.0) + faraday-net_http (~> 1.0) + faraday-net_http_persistent (~> 1.0) + faraday-patron (~> 1.0) + faraday-rack (~> 1.0) + faraday-retry (~> 1.0) + ruby2_keywords (>= 0.0.4) + faraday-cookie_jar (0.0.7) + faraday (>= 0.8.0) + http-cookie (~> 1.0.0) + faraday-em_http (1.0.0) + faraday-em_synchrony (1.0.0) + faraday-excon (1.1.0) + faraday-httpclient (1.0.1) + faraday-multipart (1.0.4) + multipart-post (~> 2) + faraday-net_http (1.0.1) + faraday-net_http_persistent (1.2.0) + faraday-patron (1.0.0) + faraday-rack (1.0.0) + faraday-retry (1.0.3) + faraday_middleware (1.2.0) + faraday (~> 1.0) + fastimage (2.2.6) + fastlane (2.212.2) + CFPropertyList (>= 2.3, < 4.0.0) + addressable (>= 2.8, < 3.0.0) + artifactory (~> 3.0) + aws-sdk-s3 (~> 1.0) + babosa (>= 1.0.3, < 2.0.0) + bundler (>= 1.12.0, < 3.0.0) + colored + commander (~> 4.6) + dotenv (>= 2.1.1, < 3.0.0) + emoji_regex (>= 0.1, < 4.0) + excon (>= 0.71.0, < 1.0.0) + faraday (~> 1.0) + faraday-cookie_jar (~> 0.0.6) + faraday_middleware (~> 1.0) + fastimage (>= 2.1.0, < 3.0.0) + gh_inspector (>= 1.1.2, < 2.0.0) + google-apis-androidpublisher_v3 (~> 0.3) + google-apis-playcustomapp_v1 (~> 0.1) + google-cloud-storage (~> 1.31) + highline (~> 2.0) + json (< 3.0.0) + jwt (>= 2.1.0, < 3) + mini_magick (>= 4.9.4, < 5.0.0) + multipart-post (~> 2.0.0) + naturally (~> 2.2) + optparse (~> 0.1.1) + plist (>= 3.1.0, < 4.0.0) + rubyzip (>= 2.0.0, < 3.0.0) + security (= 0.1.3) + simctl (~> 1.6.3) + terminal-notifier (>= 2.0.0, < 3.0.0) + terminal-table (>= 1.4.5, < 2.0.0) + tty-screen (>= 0.6.3, < 1.0.0) + tty-spinner (>= 0.8.0, < 1.0.0) + word_wrap (~> 1.0.0) + xcodeproj (>= 1.13.0, < 2.0.0) + xcpretty (~> 0.3.0) + xcpretty-travis-formatter (>= 0.0.3) + gh_inspector (1.1.3) + google-apis-androidpublisher_v3 (0.41.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-core (0.11.0) + addressable (~> 2.5, >= 2.5.1) + googleauth (>= 0.16.2, < 2.a) + httpclient (>= 2.8.1, < 3.a) + mini_mime (~> 1.0) + representable (~> 3.0) + retriable (>= 2.0, < 4.a) + rexml + webrick + google-apis-iamcredentials_v1 (0.17.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-playcustomapp_v1 (0.13.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-storage_v1 (0.19.0) + google-apis-core (>= 0.9.0, < 2.a) + google-cloud-core (1.6.0) + google-cloud-env (~> 1.0) + google-cloud-errors (~> 1.0) + google-cloud-env (1.6.0) + faraday (>= 0.17.3, < 3.0) + google-cloud-errors (1.3.1) + google-cloud-storage (1.44.0) + addressable (~> 2.8) + digest-crc (~> 0.4) + google-apis-iamcredentials_v1 (~> 0.1) + google-apis-storage_v1 (~> 0.19.0) + google-cloud-core (~> 1.6) + googleauth (>= 0.16.2, < 2.a) + mini_mime (~> 1.0) + googleauth (1.5.2) + faraday (>= 0.17.3, < 3.a) + jwt (>= 1.4, < 3.0) + memoist (~> 0.16) + multi_json (~> 1.11) + os (>= 0.9, < 2.0) + signet (>= 0.16, < 2.a) + highline (2.0.3) + http-cookie (1.0.5) + domain_name (~> 0.5) + httpclient (2.8.3) + jmespath (1.6.2) + json (2.6.3) + jwt (2.7.0) + memoist (0.16.2) + mini_magick (4.12.0) + mini_mime (1.1.2) + multi_json (1.15.0) + multipart-post (2.0.0) + nanaimo (0.3.0) + naturally (2.2.1) + optparse (0.1.1) + os (1.1.4) + plist (3.7.0) + public_suffix (5.0.1) + rake (13.0.6) + representable (3.2.0) + declarative (< 0.1.0) + trailblazer-option (>= 0.1.1, < 0.2.0) + uber (< 0.2.0) + retriable (3.1.2) + rexml (3.2.5) + rouge (2.0.7) + ruby2_keywords (0.0.5) + rubyzip (2.3.2) + security (0.1.3) + signet (0.17.0) + addressable (~> 2.8) + faraday (>= 0.17.5, < 3.a) + jwt (>= 1.5, < 3.0) + multi_json (~> 1.10) + simctl (1.6.10) + CFPropertyList + naturally + terminal-notifier (2.0.0) + terminal-table (1.8.0) + unicode-display_width (~> 1.1, >= 1.1.1) + trailblazer-option (0.1.2) + tty-cursor (0.7.1) + tty-screen (0.8.1) + tty-spinner (0.9.3) + tty-cursor (~> 0.7) + uber (0.1.0) + unf (0.1.4) + unf_ext + unf_ext (0.0.8.2) + unicode-display_width (1.8.0) + webrick (1.8.1) + word_wrap (1.0.0) + xcodeproj (1.22.0) + CFPropertyList (>= 2.3.3, < 4.0) + atomos (~> 0.1.3) + claide (>= 1.0.2, < 2.0) + colored2 (~> 3.1) + nanaimo (~> 0.3.0) + rexml (~> 3.2.4) + xcpretty (0.3.0) + rouge (~> 2.0.7) + xcpretty-travis-formatter (1.0.1) + xcpretty (~> 0.2, >= 0.0.7) + +PLATFORMS + x86_64-darwin-22 + +DEPENDENCIES + fastlane + +BUNDLED WITH + 2.4.10 diff --git a/iosApp/fastlane/Appfile b/iosApp/fastlane/Appfile new file mode 100644 index 00000000..4abe995e --- /dev/null +++ b/iosApp/fastlane/Appfile @@ -0,0 +1,8 @@ +app_identifier("nl.tiebe.otarium") # The bundle identifier of your app +apple_id("tiebe.groosman@gmail.com") # Your Apple Developer Portal username + +itc_team_id("126352480") # App Store Connect Team ID +team_id("KQ89L64KUK") # Developer Portal Team ID + +# For more information about the Appfile, see: +# https://docs.fastlane.tools/advanced/#appfile diff --git a/iosApp/fastlane/Deliverfile b/iosApp/fastlane/Deliverfile new file mode 100644 index 00000000..74739f74 --- /dev/null +++ b/iosApp/fastlane/Deliverfile @@ -0,0 +1,3 @@ +# The Deliverfile allows you to store various App Store Connect metadata +# For more information, check out the docs +# https://docs.fastlane.tools/actions/deliver/ diff --git a/iosApp/fastlane/Fastfile b/iosApp/fastlane/Fastfile new file mode 100644 index 00000000..878b7249 --- /dev/null +++ b/iosApp/fastlane/Fastfile @@ -0,0 +1,25 @@ +# This file contains the fastlane.tools configuration +# You can find the documentation at https://docs.fastlane.tools +# +# For a list of all available actions, check out +# +# https://docs.fastlane.tools/actions +# +# For a list of all available plugins, check out +# +# https://docs.fastlane.tools/plugins/available-plugins +# + +# Uncomment the line if you want fastlane to automatically update itself +# update_fastlane + +default_platform(:ios) + +platform :ios do + desc "Push a new release build to the App Store" + lane :release do + increment_build_number(xcodeproj: "iosApp.xcodeproj") + build_app(workspace: "iosApp.xcworkspace", scheme: "iosApp") + upload_to_app_store + end +end diff --git a/iosApp/fastlane/metadata/copyright.txt b/iosApp/fastlane/metadata/copyright.txt new file mode 100644 index 00000000..7c34e5c4 --- /dev/null +++ b/iosApp/fastlane/metadata/copyright.txt @@ -0,0 +1 @@ +2023 Tiebe Groosman diff --git a/iosApp/fastlane/metadata/en-US/apple_tv_privacy_policy.txt b/iosApp/fastlane/metadata/en-US/apple_tv_privacy_policy.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/iosApp/fastlane/metadata/en-US/apple_tv_privacy_policy.txt @@ -0,0 +1 @@ + diff --git a/iosApp/fastlane/metadata/en-US/description.txt b/iosApp/fastlane/metadata/en-US/description.txt new file mode 100644 index 00000000..e755511d --- /dev/null +++ b/iosApp/fastlane/metadata/en-US/description.txt @@ -0,0 +1,5 @@ +A faster alternative for the Magister 6 app. + + + +If you have any feedback please email me with the email provided on the store listing. diff --git a/iosApp/fastlane/metadata/en-US/keywords.txt b/iosApp/fastlane/metadata/en-US/keywords.txt new file mode 100644 index 00000000..beb86fbe --- /dev/null +++ b/iosApp/fastlane/metadata/en-US/keywords.txt @@ -0,0 +1 @@ +magister,school diff --git a/iosApp/fastlane/metadata/en-US/marketing_url.txt b/iosApp/fastlane/metadata/en-US/marketing_url.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/iosApp/fastlane/metadata/en-US/marketing_url.txt @@ -0,0 +1 @@ + diff --git a/iosApp/fastlane/metadata/en-US/name.txt b/iosApp/fastlane/metadata/en-US/name.txt new file mode 100644 index 00000000..018a8f8e --- /dev/null +++ b/iosApp/fastlane/metadata/en-US/name.txt @@ -0,0 +1 @@ +Otarium diff --git a/iosApp/fastlane/metadata/en-US/privacy_url.txt b/iosApp/fastlane/metadata/en-US/privacy_url.txt new file mode 100644 index 00000000..c7b91795 --- /dev/null +++ b/iosApp/fastlane/metadata/en-US/privacy_url.txt @@ -0,0 +1 @@ +https://groosman.nl/privacy-policy diff --git a/iosApp/fastlane/metadata/en-US/promotional_text.txt b/iosApp/fastlane/metadata/en-US/promotional_text.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/iosApp/fastlane/metadata/en-US/promotional_text.txt @@ -0,0 +1 @@ + diff --git a/iosApp/fastlane/metadata/en-US/release_notes.txt b/iosApp/fastlane/metadata/en-US/release_notes.txt new file mode 100644 index 00000000..30c9b967 --- /dev/null +++ b/iosApp/fastlane/metadata/en-US/release_notes.txt @@ -0,0 +1 @@ +Fix some issues where extra lines would appear diff --git a/iosApp/fastlane/metadata/en-US/subtitle.txt b/iosApp/fastlane/metadata/en-US/subtitle.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/iosApp/fastlane/metadata/en-US/subtitle.txt @@ -0,0 +1 @@ + diff --git a/iosApp/fastlane/metadata/en-US/support_url.txt b/iosApp/fastlane/metadata/en-US/support_url.txt new file mode 100644 index 00000000..97598eeb --- /dev/null +++ b/iosApp/fastlane/metadata/en-US/support_url.txt @@ -0,0 +1 @@ +https://tiebe.me diff --git a/iosApp/fastlane/metadata/primary_category.txt b/iosApp/fastlane/metadata/primary_category.txt new file mode 100644 index 00000000..a6bcf8b6 --- /dev/null +++ b/iosApp/fastlane/metadata/primary_category.txt @@ -0,0 +1 @@ +EDUCATION diff --git a/iosApp/fastlane/metadata/primary_first_sub_category.txt b/iosApp/fastlane/metadata/primary_first_sub_category.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/iosApp/fastlane/metadata/primary_first_sub_category.txt @@ -0,0 +1 @@ + diff --git a/iosApp/fastlane/metadata/primary_second_sub_category.txt b/iosApp/fastlane/metadata/primary_second_sub_category.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/iosApp/fastlane/metadata/primary_second_sub_category.txt @@ -0,0 +1 @@ + diff --git a/iosApp/fastlane/metadata/review_information/demo_password.txt b/iosApp/fastlane/metadata/review_information/demo_password.txt new file mode 100644 index 00000000..39cdd0de --- /dev/null +++ b/iosApp/fastlane/metadata/review_information/demo_password.txt @@ -0,0 +1 @@ +- diff --git a/iosApp/fastlane/metadata/review_information/demo_user.txt b/iosApp/fastlane/metadata/review_information/demo_user.txt new file mode 100644 index 00000000..1da94f76 --- /dev/null +++ b/iosApp/fastlane/metadata/review_information/demo_user.txt @@ -0,0 +1 @@ +appstorelogin diff --git a/iosApp/fastlane/metadata/review_information/email_address.txt b/iosApp/fastlane/metadata/review_information/email_address.txt new file mode 100644 index 00000000..8ec3c0d7 --- /dev/null +++ b/iosApp/fastlane/metadata/review_information/email_address.txt @@ -0,0 +1 @@ +tiebe.groosman@gmail.com diff --git a/iosApp/fastlane/metadata/review_information/first_name.txt b/iosApp/fastlane/metadata/review_information/first_name.txt new file mode 100644 index 00000000..7b0322bf --- /dev/null +++ b/iosApp/fastlane/metadata/review_information/first_name.txt @@ -0,0 +1 @@ +Tiebe diff --git a/iosApp/fastlane/metadata/review_information/last_name.txt b/iosApp/fastlane/metadata/review_information/last_name.txt new file mode 100644 index 00000000..887fdb1e --- /dev/null +++ b/iosApp/fastlane/metadata/review_information/last_name.txt @@ -0,0 +1 @@ +Groosman diff --git a/iosApp/fastlane/metadata/review_information/notes.txt b/iosApp/fastlane/metadata/review_information/notes.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/iosApp/fastlane/metadata/review_information/notes.txt @@ -0,0 +1 @@ + diff --git a/iosApp/fastlane/metadata/review_information/phone_number.txt b/iosApp/fastlane/metadata/review_information/phone_number.txt new file mode 100644 index 00000000..1b712747 --- /dev/null +++ b/iosApp/fastlane/metadata/review_information/phone_number.txt @@ -0,0 +1 @@ ++31613005145 diff --git a/iosApp/fastlane/metadata/secondary_category.txt b/iosApp/fastlane/metadata/secondary_category.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/iosApp/fastlane/metadata/secondary_category.txt @@ -0,0 +1 @@ + diff --git a/iosApp/fastlane/metadata/secondary_first_sub_category.txt b/iosApp/fastlane/metadata/secondary_first_sub_category.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/iosApp/fastlane/metadata/secondary_first_sub_category.txt @@ -0,0 +1 @@ + diff --git a/iosApp/fastlane/metadata/secondary_second_sub_category.txt b/iosApp/fastlane/metadata/secondary_second_sub_category.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/iosApp/fastlane/metadata/secondary_second_sub_category.txt @@ -0,0 +1 @@ + diff --git a/iosApp/fastlane/screenshots/README.txt b/iosApp/fastlane/screenshots/README.txt new file mode 100644 index 00000000..948c580c --- /dev/null +++ b/iosApp/fastlane/screenshots/README.txt @@ -0,0 +1,30 @@ +## Screenshots Naming Rules + +Put all screenshots you want to use inside the folder of its language (e.g. `en-US`). +The device type will automatically be recognized using the image resolution. + +The screenshots can be named whatever you want, but keep in mind they are sorted +alphabetically, in a human-friendly way. See https://github.com/fastlane/fastlane/pull/18200 for more details. + +### Exceptions + +#### iPad Pro (3rd Gen) 12.9" + +Since iPad Pro (3rd Gen) 12.9" and iPad Pro (2nd Gen) 12.9" have the same image +resolution, screenshots of the iPad Pro (3rd gen) 12.9" must contain either the +string `iPad Pro (12.9-inch) (3rd generation)`, `IPAD_PRO_3GEN_129`, or `ipadPro129` +(App Store Connect's internal naming of the display family for the 3rd generation iPad Pro) +in its filename to be assigned the correct display family and to be uploaded to +the correct screenshot slot in your app's metadata. + +### Other Platforms + +#### Apple TV + +Apple TV screenshots should be stored in a subdirectory named `appleTV` with language +folders inside of it. + +#### iMessage + +iMessage screenshots, like the Apple TV ones, should also be stored in a subdirectory +named `iMessage`, with language folders inside of it. diff --git a/iosApp/iosApp.xcodeproj/xcshareddata/xcschemes/iosApp.xcscheme b/iosApp/iosApp.xcodeproj/xcshareddata/xcschemes/iosApp.xcscheme new file mode 100644 index 00000000..e837a0c4 --- /dev/null +++ b/iosApp/iosApp.xcodeproj/xcshareddata/xcschemes/iosApp.xcscheme @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From a7d289de14b1d1c8d85f5ec5c98e208c3ad8c2d5 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Tue, 9 May 2023 21:29:07 +0200 Subject: [PATCH 39/82] fastlane --- build.gradle.kts | 8 ++--- iosApp/Podfile.lock | 4 +-- .../Pods/Local Podspecs/shared.podspec.json | 2 +- iosApp/Pods/Manifest.lock | 4 +-- iosApp/fastlane/Fastfile | 1 + iosApp/{ => fastlane}/Matchfile | 0 iosApp/fastlane/README.md | 32 +++++++++++++++++ iosApp/fastlane/report.xml | 35 +++++++++++++++++++ iosApp/iosApp.xcodeproj/project.pbxproj | 4 +-- .../xcschemes/xcschememanagement.plist | 8 +++++ iosApp/iosApp/Info.plist | 2 +- .../assignment/AssignmentScreenComponent.kt | 17 ++++----- .../folder/StudyGuideFolderComponent.kt | 6 ++-- .../utils/colorpicker/HarmonyColorPicker.kt | 34 ++++++++++++++---- 14 files changed, 128 insertions(+), 29 deletions(-) rename iosApp/{ => fastlane}/Matchfile (100%) create mode 100644 iosApp/fastlane/README.md create mode 100644 iosApp/fastlane/report.xml diff --git a/build.gradle.kts b/build.gradle.kts index 6f3ea53f..c285c979 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,8 +6,8 @@ buildscript { repositories { gradlePluginPortal() google() - mavenLocal() mavenCentral() + mavenLocal() maven("https://jitpack.io") maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") } @@ -24,18 +24,18 @@ buildscript { allprojects { repositories { google() - mavenLocal() mavenCentral() + mavenLocal() maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") maven("https://jitpack.io") - maven { +/* maven { url = uri("https://maven.pkg.github.com/Tiebe/MagisterAPIKt") credentials { username = project.findProperty("gpr.user") as String? ?: System.getenv("USERNAME") password = project.findProperty("gpr.key") as String? ?: System.getenv("TOKEN") } - } + }*/ } } diff --git a/iosApp/Podfile.lock b/iosApp/Podfile.lock index 9fe2aeb3..51eb277a 100644 --- a/iosApp/Podfile.lock +++ b/iosApp/Podfile.lock @@ -77,7 +77,7 @@ PODS: - nanopb/decode (2.30909.0) - nanopb/encode (2.30909.0) - PromisesObjC (2.2.0) - - shared (3.2.0): + - shared (3.3.0-alpha01): - Google-Mobile-Ads-SDK DEPENDENCIES: @@ -113,7 +113,7 @@ SPEC CHECKSUMS: GoogleUtilities: 9aa0ad5a7bc171f8bae016300bfcfa3fb8425749 nanopb: b552cce312b6c8484180ef47159bc0f65a1f0431 PromisesObjC: 09985d6d70fbe7878040aa746d78236e6946d2ef - shared: c96ddc615f6643b8192054db922990f4358eae96 + shared: 9cbaecd98fcb21f2c8e6a9275d0a7b8f51449c5a PODFILE CHECKSUM: 1956413cc6e8dfcfe69cff320ed53605486f7cac diff --git a/iosApp/Pods/Local Podspecs/shared.podspec.json b/iosApp/Pods/Local Podspecs/shared.podspec.json index 4cda36a3..a7a6e03d 100644 --- a/iosApp/Pods/Local Podspecs/shared.podspec.json +++ b/iosApp/Pods/Local Podspecs/shared.podspec.json @@ -1,6 +1,6 @@ { "name": "shared", - "version": "3.2.0", + "version": "3.3.0-alpha01", "homepage": "https://otarium.groosman.nl", "source": { "http": "" diff --git a/iosApp/Pods/Manifest.lock b/iosApp/Pods/Manifest.lock index 9fe2aeb3..51eb277a 100644 --- a/iosApp/Pods/Manifest.lock +++ b/iosApp/Pods/Manifest.lock @@ -77,7 +77,7 @@ PODS: - nanopb/decode (2.30909.0) - nanopb/encode (2.30909.0) - PromisesObjC (2.2.0) - - shared (3.2.0): + - shared (3.3.0-alpha01): - Google-Mobile-Ads-SDK DEPENDENCIES: @@ -113,7 +113,7 @@ SPEC CHECKSUMS: GoogleUtilities: 9aa0ad5a7bc171f8bae016300bfcfa3fb8425749 nanopb: b552cce312b6c8484180ef47159bc0f65a1f0431 PromisesObjC: 09985d6d70fbe7878040aa746d78236e6946d2ef - shared: c96ddc615f6643b8192054db922990f4358eae96 + shared: 9cbaecd98fcb21f2c8e6a9275d0a7b8f51449c5a PODFILE CHECKSUM: 1956413cc6e8dfcfe69cff320ed53605486f7cac diff --git a/iosApp/fastlane/Fastfile b/iosApp/fastlane/Fastfile index 878b7249..87a4dadb 100644 --- a/iosApp/fastlane/Fastfile +++ b/iosApp/fastlane/Fastfile @@ -18,6 +18,7 @@ default_platform(:ios) platform :ios do desc "Push a new release build to the App Store" lane :release do + match(type: "appstore") increment_build_number(xcodeproj: "iosApp.xcodeproj") build_app(workspace: "iosApp.xcworkspace", scheme: "iosApp") upload_to_app_store diff --git a/iosApp/Matchfile b/iosApp/fastlane/Matchfile similarity index 100% rename from iosApp/Matchfile rename to iosApp/fastlane/Matchfile diff --git a/iosApp/fastlane/README.md b/iosApp/fastlane/README.md new file mode 100644 index 00000000..87ffeb24 --- /dev/null +++ b/iosApp/fastlane/README.md @@ -0,0 +1,32 @@ +fastlane documentation +---- + +# Installation + +Make sure you have the latest version of the Xcode command line tools installed: + +```sh +xcode-select --install +``` + +For _fastlane_ installation instructions, see [Installing _fastlane_](https://docs.fastlane.tools/#installing-fastlane) + +# Available Actions + +## iOS + +### ios release + +```sh +[bundle exec] fastlane ios release +``` + +Push a new release build to the App Store + +---- + +This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run. + +More information about _fastlane_ can be found on [fastlane.tools](https://fastlane.tools). + +The documentation of _fastlane_ can be found on [docs.fastlane.tools](https://docs.fastlane.tools). diff --git a/iosApp/fastlane/report.xml b/iosApp/fastlane/report.xml new file mode 100644 index 00000000..2fffaaec --- /dev/null +++ b/iosApp/fastlane/report.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iosApp/iosApp.xcodeproj/project.pbxproj b/iosApp/iosApp.xcodeproj/project.pbxproj index ce6278ba..6801f381 100644 --- a/iosApp/iosApp.xcodeproj/project.pbxproj +++ b/iosApp/iosApp.xcodeproj/project.pbxproj @@ -348,7 +348,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3.2.0; + CURRENT_PROJECT_VERSION = 5; DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; DEVELOPMENT_TEAM = KQ89L64KUK; ENABLE_PREVIEWS = YES; @@ -374,7 +374,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3.2.0; + CURRENT_PROJECT_VERSION = 5; DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; DEVELOPMENT_TEAM = KQ89L64KUK; ENABLE_PREVIEWS = YES; diff --git a/iosApp/iosApp.xcodeproj/xcuserdata/tiebe.xcuserdatad/xcschemes/xcschememanagement.plist b/iosApp/iosApp.xcodeproj/xcuserdata/tiebe.xcuserdatad/xcschemes/xcschememanagement.plist index c355b42f..187f5de6 100644 --- a/iosApp/iosApp.xcodeproj/xcuserdata/tiebe.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/iosApp/iosApp.xcodeproj/xcuserdata/tiebe.xcuserdatad/xcschemes/xcschememanagement.plist @@ -10,5 +10,13 @@ 12 + SuppressBuildableAutocreation + + 7555FF7A242A565900829871 + + primary + + + diff --git a/iosApp/iosApp/Info.plist b/iosApp/iosApp/Info.plist index 960211e0..403a9d75 100644 --- a/iosApp/iosApp/Info.plist +++ b/iosApp/iosApp/Info.plist @@ -5,7 +5,7 @@ CFBundleShortVersionString 3.2.0 CFBundleVersion - 3.2.0 + 5 BGTaskSchedulerPermittedIdentifiers nl.tiebe.otarium.graderefresh diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreenComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreenComponent.kt index 1bfd5c83..059f4259 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreenComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/AssignmentScreenComponent.kt @@ -8,15 +8,16 @@ import dev.tiebe.magisterapi.response.assignment.Assignment import dev.tiebe.magisterapi.response.assignment.AssignmentVersion import dev.tiebe.magisterapi.response.assignment.FeedbackBijlagen import dev.tiebe.magisterapi.response.assignment.LeerlingBijlagen -import io.ktor.client.statement.* -import io.ktor.http.* -import io.ktor.utils.io.* +import io.ktor.client.statement.readBytes +import io.ktor.http.URLBuilder +import io.ktor.http.Url +import io.ktor.http.appendEncodedPathSegments import kotlinx.coroutines.launch import nl.tiebe.otarium.Data import nl.tiebe.otarium.ui.root.componentCoroutineScope -import nl.tiebe.otarium.utils.getDownloadFileLocation import nl.tiebe.otarium.utils.openFileFromCache import nl.tiebe.otarium.utils.requestGET +import nl.tiebe.otarium.utils.writeFile interface AssignmentScreenComponent { val assignment: Value @@ -78,9 +79,9 @@ class DefaultAssignmentScreenComponent(componentContext: ComponentContext, overr onDownload = { bytesSentTotal, contentLength -> attachmentDownloadProgress.value = attachmentDownloadProgress.value + Pair(attachment.id, bytesSentTotal.toFloat() / contentLength.toFloat()) } - ).bodyAsChannel() + ).readBytes() - response.copyAndClose(getDownloadFileLocation(attachment.id.toString(), attachment.naam)) + writeFile(attachment.id.toString(), attachment.naam, response) openFileFromCache(attachment.id.toString(), attachment.naam) attachmentDownloadProgress.value = attachmentDownloadProgress.value.toMutableMap().also { @@ -97,9 +98,9 @@ class DefaultAssignmentScreenComponent(componentContext: ComponentContext, overr onDownload = { bytesSentTotal, contentLength -> attachmentDownloadProgress.value = attachmentDownloadProgress.value + Pair(attachment.id, bytesSentTotal.toFloat() / contentLength.toFloat()) } - ).bodyAsChannel() + ).readBytes() - response.copyAndClose(getDownloadFileLocation(attachment.id.toString(), attachment.naam)) + writeFile(attachment.id.toString(), attachment.naam, response) openFileFromCache(attachment.id.toString(), attachment.naam) attachmentDownloadProgress.value = attachmentDownloadProgress.value.toMutableMap().also { diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderComponent.kt index 58dc22f0..981681aa 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/folder/StudyGuideFolderComponent.kt @@ -13,9 +13,9 @@ import io.ktor.utils.io.* import kotlinx.coroutines.launch import nl.tiebe.otarium.Data import nl.tiebe.otarium.ui.root.componentCoroutineScope -import nl.tiebe.otarium.utils.getDownloadFileLocation import nl.tiebe.otarium.utils.openFileFromCache import nl.tiebe.otarium.utils.requestGET +import nl.tiebe.otarium.utils.writeFile interface StudyGuideFolderComponent { val content: Value @@ -70,9 +70,9 @@ class DefaultStudyGuideFolderComponent(componentContext: ComponentContext, overr onDownload = { bytesSentTotal, contentLength -> resourceDownloadProgress.value = resourceDownloadProgress.value + Pair(item.id, bytesSentTotal.toFloat() / contentLength.toFloat()) } - ).bodyAsChannel() + ).readBytes() - response.copyAndClose(getDownloadFileLocation(item.id.toString(), item.name)) + writeFile(item.id.toString(), item.name, response) openFileFromCache(item.id.toString(), item.name) resourceDownloadProgress.value = resourceDownloadProgress.value.toMutableMap().also { diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/colorpicker/HarmonyColorPicker.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/colorpicker/HarmonyColorPicker.kt index bd7e81d6..88a443b4 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/colorpicker/HarmonyColorPicker.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/colorpicker/HarmonyColorPicker.kt @@ -9,13 +9,30 @@ import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.gestures.awaitFirstDown import androidx.compose.foundation.gestures.drag import androidx.compose.foundation.gestures.forEachGesture -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.defaultMinSize +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Slider import androidx.compose.material3.SliderDefaults import androidx.compose.material3.Surface -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberUpdatedState +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color @@ -26,7 +43,12 @@ import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.dp -import kotlin.math.* +import kotlin.math.PI +import kotlin.math.atan2 +import kotlin.math.cos +import kotlin.math.hypot +import kotlin.math.min +import kotlin.math.sin @Deprecated( @@ -50,7 +72,7 @@ import kotlin.math.* ) ) @Composable -fun HarmonyColorPicker( +internal fun HarmonyColorPicker( modifier: Modifier = Modifier, harmonyMode: ColorHarmonyMode, color: Color = Color.Red, @@ -76,7 +98,7 @@ fun HarmonyColorPicker( * otherwise all colors are given the provided brightness value */ @Composable -fun HarmonyColorPicker( +internal fun HarmonyColorPicker( modifier: Modifier = Modifier, harmonyMode: ColorHarmonyMode, color: HsvColor, @@ -121,7 +143,7 @@ fun HarmonyColorPicker( } @Composable -private fun HarmonyColorPickerWithMagnifiers( +internal fun HarmonyColorPickerWithMagnifiers( modifier: Modifier = Modifier, hsvColor: HsvColor, onColorChanged: (HsvColor) -> Unit, From 63b029f18afd4d8a273d7403d4e772f41845ef23 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Tue, 9 May 2023 21:33:30 +0200 Subject: [PATCH 40/82] Update ios.yml --- .github/workflows/ios.yml | 35 +++++++---------------------------- 1 file changed, 7 insertions(+), 28 deletions(-) diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml index b0bce0ee..57154a53 100644 --- a/.github/workflows/ios.yml +++ b/.github/workflows/ios.yml @@ -21,35 +21,14 @@ jobs: - name: Grant execute permission for gradlew run: chmod +x gradlew - - name: Build and publish - uses: sparkfabrik/ios-build-action@v2.1.0 + - name: Fastlane Action + uses: maierj/fastlane-action@v3.0.0 with: - upload-to-testflight: true - increment-build-number: true - build-pods: true - pods-path: "iosApp/Podfile" - configuration: Release - export-method: app-store - workspace-path: iosApp/iosApp.xcworkspace - project-path: iosApp/iosApp.xcodeproj - scheme: iosApp - output-path: build-${{ github.sha }}.ipa - apple-key-id: ${{ secrets.APPLE_KEY_ID }} - apple-key-issuer-id: ${{ secrets.APPLE_KEY_ISSUER_ID }} - apple-key-content: ${{ secrets.APPLE_KEY_CONTENT }} - team-id: ${{ secrets.TEAM_ID }} - team-name: ${{ secrets.TEAM_NAME }} - match-password: ${{ secrets.MATCH_PASSWORD }} - match-git-url: ${{ secrets.MATCH_GIT_URL }} - match-git-basic-authorization: ${{ secrets.MATCH_GIT_BASIC_AUTHORIZATION }} - match-build-type: "appstore" - browserstack-upload: false - browserstack-username: ${{ secrets.BROWSERSTACK_USERNAME }} - browserstack-access-key: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} - fastlane-env: stage - ios-app-id: ${{ secrets.APP_ID }} + lane: release env: - APP_ID: ${{ secrets.APP_ID }} + MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} + MATCH_GIT_URL: ${{ secrets.MATCH_GIT_URL }} + MATCH_GIT_BASIC_AUTHORIZATION: ${{ secrets.MATCH_GIT_BASIC_AUTHORIZATION }} - name: Upload logs if: always() @@ -62,4 +41,4 @@ jobs: uses: actions/upload-artifact@v3.1.2 with: name: Otarium.ipa - path: build-${{ github.sha }}.ipa + path: Otarium.ipa From 777010848efdc158791f5963ac430cb6b8975cbe Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Tue, 9 May 2023 21:39:31 +0200 Subject: [PATCH 41/82] fastlane --- iosApp/iosApp/Info.plist | 4 ---- shared/build.gradle.kts | 3 +++ 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/iosApp/iosApp/Info.plist b/iosApp/iosApp/Info.plist index 403a9d75..e4beddc2 100644 --- a/iosApp/iosApp/Info.plist +++ b/iosApp/iosApp/Info.plist @@ -2,10 +2,6 @@ - CFBundleShortVersionString - 3.2.0 - CFBundleVersion - 5 BGTaskSchedulerPermittedIdentifiers nl.tiebe.otarium.graderefresh diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts index 98640b63..4449470f 100644 --- a/shared/build.gradle.kts +++ b/shared/build.gradle.kts @@ -48,6 +48,9 @@ kotlin { framework { baseName = "shared" isStatic = false + + binaryOption("bundleVersion", Version.appVersionCode.toString()) + binaryOption("bundleShortVersionString", Version.appVersion) } pod("Google-Mobile-Ads-SDK") { From 254a1204aea830dceda00a6d7afa7caf9369b2ef Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Tue, 9 May 2023 21:41:39 +0200 Subject: [PATCH 42/82] Update ios.yml --- .github/workflows/ios.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml index 57154a53..df40cdf6 100644 --- a/.github/workflows/ios.yml +++ b/.github/workflows/ios.yml @@ -25,6 +25,7 @@ jobs: uses: maierj/fastlane-action@v3.0.0 with: lane: release + subdirectory: iosApp env: MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} MATCH_GIT_URL: ${{ secrets.MATCH_GIT_URL }} From 3803947f7e01751b51bd7e57fe6fc2bda12822a2 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Tue, 9 May 2023 21:43:43 +0200 Subject: [PATCH 43/82] Update ios.yml --- .github/workflows/ios.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml index df40cdf6..07a43ebc 100644 --- a/.github/workflows/ios.yml +++ b/.github/workflows/ios.yml @@ -21,6 +21,13 @@ jobs: - name: Grant execute permission for gradlew run: chmod +x gradlew + - name: Setup Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.0' + bundler-cache: true + working-directory: 'iosApp' + - name: Fastlane Action uses: maierj/fastlane-action@v3.0.0 with: From 0ec931bb23a3bbf2c261bc78220022a5227e2266 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Tue, 9 May 2023 21:54:19 +0200 Subject: [PATCH 44/82] fastlane --- iosApp/Gemfile.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/iosApp/Gemfile.lock b/iosApp/Gemfile.lock index e1a4bc35..f1dda834 100644 --- a/iosApp/Gemfile.lock +++ b/iosApp/Gemfile.lock @@ -209,6 +209,7 @@ GEM xcpretty (~> 0.2, >= 0.0.7) PLATFORMS + x86_64-darwin-20 x86_64-darwin-22 DEPENDENCIES From d6d77678d2ee4705070f264a62fddef64f37b720 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Tue, 9 May 2023 21:59:08 +0200 Subject: [PATCH 45/82] Update ios.yml --- .github/workflows/ios.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml index 07a43ebc..589a7890 100644 --- a/.github/workflows/ios.yml +++ b/.github/workflows/ios.yml @@ -37,6 +37,7 @@ jobs: MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} MATCH_GIT_URL: ${{ secrets.MATCH_GIT_URL }} MATCH_GIT_BASIC_AUTHORIZATION: ${{ secrets.MATCH_GIT_BASIC_AUTHORIZATION }} + FASTLANE_PASSWORD: ${{ secrets.FASTLANE_PASSWORD }} - name: Upload logs if: always() From e4cfbd44fc4ab34d10f9b7e330e222c4404103ad Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Tue, 9 May 2023 22:12:57 +0200 Subject: [PATCH 46/82] Update Fastfile --- iosApp/fastlane/Fastfile | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/iosApp/fastlane/Fastfile b/iosApp/fastlane/Fastfile index 87a4dadb..9c47ce41 100644 --- a/iosApp/fastlane/Fastfile +++ b/iosApp/fastlane/Fastfile @@ -18,6 +18,13 @@ default_platform(:ios) platform :ios do desc "Push a new release build to the App Store" lane :release do + app_store_connect_api_key( + key_id: ENV['APPLE_KEY_ID'], + issuer_id: ENV['APPLE_KEY_ISSUER_ID'], + key_content: ENV['APPLE_KEY_CONTENT'], + in_house: false # optional but may be required if using match/sigh + ) + match(type: "appstore") increment_build_number(xcodeproj: "iosApp.xcodeproj") build_app(workspace: "iosApp.xcworkspace", scheme: "iosApp") From 2f32bbd5885605eff09565b98313de43a90b26e1 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Tue, 9 May 2023 22:14:08 +0200 Subject: [PATCH 47/82] Update ios.yml --- .github/workflows/ios.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml index 589a7890..ce900814 100644 --- a/.github/workflows/ios.yml +++ b/.github/workflows/ios.yml @@ -38,6 +38,9 @@ jobs: MATCH_GIT_URL: ${{ secrets.MATCH_GIT_URL }} MATCH_GIT_BASIC_AUTHORIZATION: ${{ secrets.MATCH_GIT_BASIC_AUTHORIZATION }} FASTLANE_PASSWORD: ${{ secrets.FASTLANE_PASSWORD }} + APPLE_KEY_ID: ${{ secrets.APPLE_KEY_ID }} + APPLE_KEY_ISSUER_ID: ${{ secrets.APPLE_KEY_ISSUER_ID }} + APPLE_KEY_CONTENT: ${{ secrets.APPLE_KEY_CONTENT }} - name: Upload logs if: always() From 00285722b6b2737d372b71c3748b92e6c9628bba Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Tue, 9 May 2023 22:18:22 +0200 Subject: [PATCH 48/82] Update Fastfile --- iosApp/fastlane/Fastfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/iosApp/fastlane/Fastfile b/iosApp/fastlane/Fastfile index 9c47ce41..5dc0fb42 100644 --- a/iosApp/fastlane/Fastfile +++ b/iosApp/fastlane/Fastfile @@ -26,6 +26,8 @@ platform :ios do ) match(type: "appstore") + + cocoapods increment_build_number(xcodeproj: "iosApp.xcodeproj") build_app(workspace: "iosApp.xcworkspace", scheme: "iosApp") upload_to_app_store From 1d1bc789eae55f151cf6f2c6f1e2a4dc8bcb59ea Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Tue, 9 May 2023 22:20:09 +0200 Subject: [PATCH 49/82] Update Gemfile --- iosApp/Gemfile | 1 + 1 file changed, 1 insertion(+) diff --git a/iosApp/Gemfile b/iosApp/Gemfile index 7a118b49..7e7493d5 100644 --- a/iosApp/Gemfile +++ b/iosApp/Gemfile @@ -1,3 +1,4 @@ source "https://rubygems.org" +gem "cocoapods" gem "fastlane" From df52e01987deeb290b902503c06dc0615dca1514 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Tue, 9 May 2023 22:23:59 +0200 Subject: [PATCH 50/82] fastlane --- iosApp/Gemfile.lock | 65 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/iosApp/Gemfile.lock b/iosApp/Gemfile.lock index f1dda834..22bc8ce1 100644 --- a/iosApp/Gemfile.lock +++ b/iosApp/Gemfile.lock @@ -3,8 +3,16 @@ GEM specs: CFPropertyList (3.0.6) rexml + activesupport (5.2.8.1) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 0.7, < 2) + minitest (~> 5.1) + tzinfo (~> 1.1) addressable (2.8.4) public_suffix (>= 2.0.2, < 6.0) + algoliasearch (1.27.5) + httpclient (~> 2.8, >= 2.8.3) + json (>= 1.5.1) artifactory (3.0.15) atomos (0.1.3) aws-eventstream (1.2.0) @@ -25,10 +33,48 @@ GEM aws-eventstream (~> 1, >= 1.0.2) babosa (1.0.4) claide (1.1.0) + cocoapods (1.10.2) + addressable (~> 2.6) + claide (>= 1.0.2, < 2.0) + cocoapods-core (= 1.10.2) + cocoapods-deintegrate (>= 1.0.3, < 2.0) + cocoapods-downloader (>= 1.4.0, < 2.0) + cocoapods-plugins (>= 1.0.0, < 2.0) + cocoapods-search (>= 1.0.0, < 2.0) + cocoapods-trunk (>= 1.4.0, < 2.0) + cocoapods-try (>= 1.1.0, < 2.0) + colored2 (~> 3.1) + escape (~> 0.0.4) + fourflusher (>= 2.3.0, < 3.0) + gh_inspector (~> 1.0) + molinillo (~> 0.6.6) + nap (~> 1.0) + ruby-macho (~> 1.4) + xcodeproj (>= 1.19.0, < 2.0) + cocoapods-core (1.10.2) + activesupport (> 5.0, < 6) + addressable (~> 2.6) + algoliasearch (~> 1.0) + concurrent-ruby (~> 1.1) + fuzzy_match (~> 2.0.4) + nap (~> 1.0) + netrc (~> 0.11) + public_suffix + typhoeus (~> 1.0) + cocoapods-deintegrate (1.0.5) + cocoapods-downloader (1.6.3) + cocoapods-plugins (1.0.0) + nap + cocoapods-search (1.0.1) + cocoapods-trunk (1.6.0) + nap (>= 0.8, < 2.0) + netrc (~> 0.11) + cocoapods-try (1.2.0) colored (1.2) colored2 (3.1.2) commander (4.6.0) highline (~> 2.0.0) + concurrent-ruby (1.2.2) declarative (0.0.20) digest-crc (0.6.4) rake (>= 12.0.0, < 14.0.0) @@ -36,6 +82,9 @@ GEM unf (>= 0.0.5, < 1.0.0) dotenv (2.8.1) emoji_regex (3.2.3) + escape (0.0.4) + ethon (0.16.0) + ffi (>= 1.15.0) excon (0.99.0) faraday (1.10.3) faraday-em_http (~> 1.0) @@ -105,6 +154,9 @@ GEM xcodeproj (>= 1.13.0, < 2.0.0) xcpretty (~> 0.3.0) xcpretty-travis-formatter (>= 0.0.3) + ffi (1.15.5) + fourflusher (2.3.1) + fuzzy_match (2.0.4) gh_inspector (1.1.3) google-apis-androidpublisher_v3 (0.41.0) google-apis-core (>= 0.11.0, < 2.a) @@ -148,16 +200,22 @@ GEM http-cookie (1.0.5) domain_name (~> 0.5) httpclient (2.8.3) + i18n (1.13.0) + concurrent-ruby (~> 1.0) jmespath (1.6.2) json (2.6.3) jwt (2.7.0) memoist (0.16.2) mini_magick (4.12.0) mini_mime (1.1.2) + minitest (5.18.0) + molinillo (0.6.6) multi_json (1.15.0) multipart-post (2.0.0) nanaimo (0.3.0) + nap (1.1.0) naturally (2.2.1) + netrc (0.11.0) optparse (0.1.1) os (1.1.4) plist (3.7.0) @@ -170,6 +228,7 @@ GEM retriable (3.1.2) rexml (3.2.5) rouge (2.0.7) + ruby-macho (1.4.0) ruby2_keywords (0.0.5) rubyzip (2.3.2) security (0.1.3) @@ -184,11 +243,16 @@ GEM terminal-notifier (2.0.0) terminal-table (1.8.0) unicode-display_width (~> 1.1, >= 1.1.1) + thread_safe (0.3.6) trailblazer-option (0.1.2) tty-cursor (0.7.1) tty-screen (0.8.1) tty-spinner (0.9.3) tty-cursor (~> 0.7) + typhoeus (1.4.0) + ethon (>= 0.9.0) + tzinfo (1.2.11) + thread_safe (~> 0.1) uber (0.1.0) unf (0.1.4) unf_ext @@ -213,6 +277,7 @@ PLATFORMS x86_64-darwin-22 DEPENDENCIES + cocoapods fastlane BUNDLED WITH From b6679772c5617ecc897f746ea56944a1bad89043 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Tue, 9 May 2023 22:29:31 +0200 Subject: [PATCH 51/82] fastlane --- iosApp/iosApp.xcodeproj/project.pbxproj | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/iosApp/iosApp.xcodeproj/project.pbxproj b/iosApp/iosApp.xcodeproj/project.pbxproj index 6801f381..358fe683 100644 --- a/iosApp/iosApp.xcodeproj/project.pbxproj +++ b/iosApp/iosApp.xcodeproj/project.pbxproj @@ -347,10 +347,12 @@ baseConfigurationReference = 5D21C11D38A226BF7511F8FD /* Pods-iosApp.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_STYLE = Automatic; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 5; DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; - DEVELOPMENT_TEAM = KQ89L64KUK; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = KQ89L64KUK; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = iosApp/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = Otarium; @@ -363,6 +365,8 @@ MARKETING_VERSION = 3.2.0; PRODUCT_BUNDLE_IDENTIFIER = nl.tiebe.otarium; PRODUCT_NAME = Otarium; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore nl.tiebe.otarium"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -373,10 +377,12 @@ baseConfigurationReference = CF0A5D02B3CDC59B06E5DB5F /* Pods-iosApp.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_STYLE = Automatic; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 5; DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; - DEVELOPMENT_TEAM = KQ89L64KUK; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = KQ89L64KUK; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = iosApp/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = Otarium; @@ -389,6 +395,8 @@ MARKETING_VERSION = 3.2.0; PRODUCT_BUNDLE_IDENTIFIER = nl.tiebe.otarium; PRODUCT_NAME = Otarium; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore nl.tiebe.otarium"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; From 1b90a60f48733745afc63a15fbab97da0449b857 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Wed, 10 May 2023 16:21:07 +0200 Subject: [PATCH 52/82] fix --- .../children/assignments/listscreen/AssignmentListScreen.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/listscreen/AssignmentListScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/listscreen/AssignmentListScreen.kt index b7aea742..e0ca0f2a 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/listscreen/AssignmentListScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/listscreen/AssignmentListScreen.kt @@ -41,11 +41,11 @@ internal fun AssignmentListScreen(component: AssignmentListComponent) { Text(getLocalizedString(MR.strings.assignment_all), style = TextStyle(fontSize = 14.sp)) } - Tab(selected = selectedTab == 1, onClick = { component.selectedTab.value = 2 }, modifier = Modifier.height(53.dp)) { + Tab(selected = selectedTab == 1, onClick = { component.selectedTab.value = 1 }, modifier = Modifier.height(53.dp)) { Text(getLocalizedString(MR.strings.assignment_open), style = TextStyle(fontSize = 14.sp)) } - Tab(selected = selectedTab == 2, onClick = { component.selectedTab.value = 1 }, modifier = Modifier.height(53.dp)) { + Tab(selected = selectedTab == 2, onClick = { component.selectedTab.value = 2 }, modifier = Modifier.height(53.dp)) { Text(getLocalizedString(MR.strings.assignment_submitted), style = TextStyle(fontSize = 14.sp)) } @@ -82,4 +82,4 @@ internal fun AssignmentListScreen(component: AssignmentListComponent) { modifier = Modifier.align(Alignment.TopCenter) ) } -} \ No newline at end of file +} From 09d2d6978384167094fcd3193e74d3f32ff03310 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Wed, 10 May 2023 17:38:17 +0200 Subject: [PATCH 53/82] commit --- iosApp/Podfile.lock | 2 +- iosApp/Pods/Manifest.lock | 2 +- iosApp/Pods/Pods.xcodeproj/project.pbxproj | 710 ++++++++++-------- .../Pods-iosApp/Pods-iosApp-Info.plist | 2 +- .../Pods-iosApp/Pods-iosApp.debug.xcconfig | 5 +- .../Pods-iosApp/Pods-iosApp.release.xcconfig | 5 +- .../shared/shared.debug.xcconfig | 3 +- .../shared/shared.release.xcconfig | 3 +- iosApp/fastlane/Fastfile | 7 + iosApp/fastlane/README.md | 8 + iosApp/fastlane/report.xml | 15 +- 11 files changed, 405 insertions(+), 357 deletions(-) diff --git a/iosApp/Podfile.lock b/iosApp/Podfile.lock index 51eb277a..69c7fc05 100644 --- a/iosApp/Podfile.lock +++ b/iosApp/Podfile.lock @@ -117,4 +117,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 1956413cc6e8dfcfe69cff320ed53605486f7cac -COCOAPODS: 1.12.1 +COCOAPODS: 1.10.2 diff --git a/iosApp/Pods/Manifest.lock b/iosApp/Pods/Manifest.lock index 51eb277a..69c7fc05 100644 --- a/iosApp/Pods/Manifest.lock +++ b/iosApp/Pods/Manifest.lock @@ -117,4 +117,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 1956413cc6e8dfcfe69cff320ed53605486f7cac -COCOAPODS: 1.12.1 +COCOAPODS: 1.10.2 diff --git a/iosApp/Pods/Pods.xcodeproj/project.pbxproj b/iosApp/Pods/Pods.xcodeproj/project.pbxproj index 2b0a55cd..7a8eeb75 100644 --- a/iosApp/Pods/Pods.xcodeproj/project.pbxproj +++ b/iosApp/Pods/Pods.xcodeproj/project.pbxproj @@ -19,12 +19,13 @@ }; 8777C9F6889E59EFFD631D80AEE9048B /* shared */ = { isa = PBXAggregateTarget; - buildConfigurationList = 3442D90E7F5067221336A7DB9B972C30 /* Build configuration list for PBXAggregateTarget "shared" */; + buildConfigurationList = D32B7ABA4A4D006DB97DE2087CBEB65D /* Build configuration list for PBXAggregateTarget "shared" */; buildPhases = ( - B921D95D8F7C055BFF178D5F7086615A /* [CP-User] Build shared */, + DD416B42161958195EB2D2C00E58D9B5 /* [CP-User] Build shared */, + 9ACE8A73447CB9B790294378EA16A8EE /* [CP] Copy dSYMs */, ); dependencies = ( - 9F74628501F547383F470D0AC87CDB26 /* PBXTargetDependency */, + B8165BDA273840902291CAC6808E5E83 /* PBXTargetDependency */, ); name = shared; }; @@ -73,6 +74,7 @@ 004F5BCEFE275603773D91906959433D /* GULNetworkInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 7031B8EADC8709308A9F20A13014CDD2 /* GULNetworkInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; 00B14581F8A9DF77A1CCABF4F38FDCC6 /* GULLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 63F5E4FEC58B641AAE263D1561C3D353 /* GULLogger.h */; settings = {ATTRIBUTES = (Public, ); }; }; 029FFCF48B4798884E84B56112093EEA /* FIRInstallationsItem+RegisterInstallationAPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 675C823867BD14A3FADEB635EECBC9C5 /* FIRInstallationsItem+RegisterInstallationAPI.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 02DBBFE6415C5992B59E330C0CB7CBCC /* GoogleUtilities.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FE1A31337B0FAEB70E99E236BA66A468 /* GoogleUtilities.framework */; }; 039C1BC3004335F65ED6F81DC5064941 /* FIRComponentContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = 81CCE26FDAE62501E14609814E44F23B /* FIRComponentContainer.m */; }; 0423DC7F79FBB19C2F0FB077650BF17C /* FIRInstallationsSingleOperationPromiseCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 8E33AA5C47025736F7BA97F6AFBE11A9 /* FIRInstallationsSingleOperationPromiseCache.h */; settings = {ATTRIBUTES = (Project, ); }; }; 045E113700A9C9BF22FA57281B1DA16D /* GULHeartbeatDateStorageUserDefaults.m in Sources */ = {isa = PBXBuildFile; fileRef = B634C3ECC3AA33FE6F9B04F50E29059B /* GULHeartbeatDateStorageUserDefaults.m */; }; @@ -83,7 +85,6 @@ 06D87E7645F590B5447685F23803A841 /* FIRLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 6A19DF0B5DAE63150DFAAFF6EC5BD014 /* FIRLogger.h */; settings = {ATTRIBUTES = (Project, ); }; }; 07BDF48F4F46CE9582ED82F6211B9C12 /* GULNetworkInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 174791D473D7D2E89E78D440C5B1D6B7 /* GULNetworkInternal.h */; settings = {ATTRIBUTES = (Project, ); }; }; 093DEB8B19B62BA54EEABDFB5B3407AE /* FBLPromise+Do.m in Sources */ = {isa = PBXBuildFile; fileRef = C8F66EDBEF34CCD0CC0775823C80BBC9 /* FBLPromise+Do.m */; }; - 0C9D25134C98367EE9E24ADB21D3F27C /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 67B07E9904CAAD5AD2278E2CBF9E13DD /* Foundation.framework */; }; 0DE64462DD133B50775DDE61D77F51C5 /* FIROptionsInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = A27A28CB3E36EDE171ABC0631FEE8F20 /* FIROptionsInternal.h */; settings = {ATTRIBUTES = (Project, ); }; }; 114FD92C3D8565AF2603225114B6BB16 /* FIRComponentContainerInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 91F3C6391919DA08C6795F404EEA4EB6 /* FIRComponentContainerInternal.h */; settings = {ATTRIBUTES = (Project, ); }; }; 11569766A5ACAA8562FF2280891744AF /* FBLPromise+Always.h in Headers */ = {isa = PBXBuildFile; fileRef = 289771BDBDD0769783F1E246DAB15D44 /* FBLPromise+Always.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -104,7 +105,7 @@ 2066D648A4754195AD234F3B8AA01966 /* FirebaseInstallations.h in Headers */ = {isa = PBXBuildFile; fileRef = 3CCB87CFC4CE32ABC15A8BEC83C53D81 /* FirebaseInstallations.h */; settings = {ATTRIBUTES = (Public, ); }; }; 20BCCB2DCE423540D0E672DB45DAD2E0 /* _ObjC_HeartbeatController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C7AADA67DBE6A6F858F3272227C6C28 /* _ObjC_HeartbeatController.swift */; }; 22F30C2E707344BD951AAFDC78262485 /* FIRInstallationsBackoffController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3CE3A5565674ACCA207BECC54A66CE1C /* FIRInstallationsBackoffController.m */; }; - 263A276BC241899FD8DF8F1443BFDE88 /* Pods-iosApp-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 54DC7F9924F99FEE67A9AD3BEE09A10C /* Pods-iosApp-dummy.m */; }; + 25BE72244FAEDAE5507471612448F6E1 /* FBLPromises.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E9B4A3EF13B1698D0495A6706C23EF19 /* FBLPromises.framework */; }; 266451E371FA6561D706F07F0D1FA651 /* FIRLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 7038AF786FA7C1BDD55816630BE4CE06 /* FIRLogger.h */; settings = {ATTRIBUTES = (Project, ); }; }; 2A63ABB221D262618322C53395543641 /* FIRInstallationsIIDStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 628D78DB1DEDAD8D048CD50636A86AE2 /* FIRInstallationsIIDStore.m */; }; 2A74E1087EB4507C01192F7E08A38631 /* FIRAnalyticsConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = B7FC504C9DAE99800FC080DD1150F709 /* FIRAnalyticsConfiguration.m */; }; @@ -112,6 +113,7 @@ 2CAFBA387276BBF57FFB6092C4AF1356 /* FBLPromise+Wrap.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A8F60A591C9E93E34A7A4DB3D538C34 /* FBLPromise+Wrap.m */; }; 2D525CEEEB4212128317A95A7ADE3499 /* FIRComponentType.m in Sources */ = {isa = PBXBuildFile; fileRef = 29E38B8EB91AD818AA1D218405346339 /* FIRComponentType.m */; }; 2D6BFD9440D2F5577ED8A7284ABF7A22 /* FIROptionsInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 52B032C25ADA8E85E33A642414145C71 /* FIROptionsInternal.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 2DF20BF5B0A2DDE2C3CE06190DCFC5B9 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B19433800784CD7CF9CC033CCD10D5BC /* Foundation.framework */; }; 2E1F01CE7EEF42D4E7113F6F66184DA6 /* GULURLSessionDataResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = DADEB7CA0D89E05306D6A054512E7395 /* GULURLSessionDataResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; 2ED64AF2C4D7C7CA8CAD87BA25312B35 /* FIRInstallationsAPIService.h in Headers */ = {isa = PBXBuildFile; fileRef = 182C2A989802E13276AACA88F092EA83 /* FIRInstallationsAPIService.h */; settings = {ATTRIBUTES = (Project, ); }; }; 2F05DB99CE7F6151F2A9B70D22278FF2 /* FIRInstallationsErrorUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = C65D9B3322CF40598E1D86735D1C9D46 /* FIRInstallationsErrorUtil.h */; settings = {ATTRIBUTES = (Project, ); }; }; @@ -119,20 +121,22 @@ 3012DAF46ACAA0D28DB54BDA4C334A0B /* FIRAnalyticsConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 3738D75F32A8AD3AF11EEE20310597E1 /* FIRAnalyticsConfiguration.h */; settings = {ATTRIBUTES = (Project, ); }; }; 30465761E0A1A05690177FB20977DAB0 /* FIRInstallationsHTTPError.m in Sources */ = {isa = PBXBuildFile; fileRef = 43C5BB0F87EC29C5DF45C79A25EA735C /* FIRInstallationsHTTPError.m */; }; 30DFE4557E0E16A1E7414D6DCBB9ED60 /* FIROptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 71EC7AB5B0962EA1AFA3C49B77205B94 /* FIROptions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 31BEEC24AE7BAEE349E5EA2FFC7E2D10 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 67B07E9904CAAD5AD2278E2CBF9E13DD /* Foundation.framework */; }; + 31BEEC24AE7BAEE349E5EA2FFC7E2D10 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B19433800784CD7CF9CC033CCD10D5BC /* Foundation.framework */; }; 31FFFB924A4A9B074AAE42DC3CA2DF3A /* FIRInstallationsBackoffController.h in Headers */ = {isa = PBXBuildFile; fileRef = AE66F2C8F2DEF86C3D05B060FB8A36C3 /* FIRInstallationsBackoffController.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 334C3AA66C26197A144AAE81CD46FFA3 /* GoogleUtilities.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FE1A31337B0FAEB70E99E236BA66A468 /* GoogleUtilities.framework */; }; 335C05F53CCAFE1D717B8CD1FB5FCD57 /* FBLPromise+Recover.h in Headers */ = {isa = PBXBuildFile; fileRef = 91EA6ACA2DE18083648BA54D58C121FE /* FBLPromise+Recover.h */; settings = {ATTRIBUTES = (Public, ); }; }; 349CEDB6E2906A4A4C86FBB3E69954BE /* pb.h in Headers */ = {isa = PBXBuildFile; fileRef = 8D0119E7E46D080B47146EBAF76DA853 /* pb.h */; settings = {ATTRIBUTES = (Public, ); }; }; 36C66462C6EB23D990AD35B1D1383161 /* FIRHeartbeatLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 70A121213A30CE83995624D591F96B68 /* FIRHeartbeatLogger.h */; settings = {ATTRIBUTES = (Project, ); }; }; 36CDCD6418422BEC7AD0289E48397D2E /* FBLPromise+All.m in Sources */ = {isa = PBXBuildFile; fileRef = DDF49266C54A6C99D67004610AE5775E /* FBLPromise+All.m */; }; + 37AA40A4EDB0593675DA62220372A149 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B19433800784CD7CF9CC033CCD10D5BC /* Foundation.framework */; }; 37BE89A73482750D3F57FDD05FAAC280 /* FirebaseCore.h in Headers */ = {isa = PBXBuildFile; fileRef = 01F5C87F2D94B96ACF04CF848E643826 /* FirebaseCore.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3A9A3A4A1E970C7D4CE1B9762BF8E06D /* NSURLSession+GULPromises.h in Headers */ = {isa = PBXBuildFile; fileRef = 0121A3CD1A7EA5F122CD951171D9979C /* NSURLSession+GULPromises.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3AB7C75FDFB254AADB0C5066448CB5AF /* StorageFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 947A9AF9A82B3A4CFB9883B7BD5B6294 /* StorageFactory.swift */; }; 3B5F7D93BA1140DBC968FC62FE116C15 /* FIRDependency.h in Headers */ = {isa = PBXBuildFile; fileRef = 7DF3042C05F8120877712DB85D79AC71 /* FIRDependency.h */; settings = {ATTRIBUTES = (Project, ); }; }; 3C19E85C9F6927CB36AD8ABF04F5A8A1 /* FIRInstallationsHTTPError.h in Headers */ = {isa = PBXBuildFile; fileRef = FEE9E11571A34737DE67E784A6459200 /* FIRInstallationsHTTPError.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 3CC39B64DC6D709E754934AF2B1CECAF /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 67B07E9904CAAD5AD2278E2CBF9E13DD /* Foundation.framework */; }; 3F153E58AEA77288129C109B39A6BB09 /* FBLPromiseError.h in Headers */ = {isa = PBXBuildFile; fileRef = 8630AF901F648EC5B6A22C98ED25AFFD /* FBLPromiseError.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3F6990A37575D7910E9AA9A53163ABBE /* FirebaseCore-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 765A7761B79985DE9D23A6A666CA23EA /* FirebaseCore-dummy.m */; }; + 3FDFEE6521F1D5BD502F83CDD9659CD9 /* Pods-iosApp-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = F87A626222A8D9B26448A9FEB04FD0DF /* Pods-iosApp-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; 450A466E165D34794381742696D7058B /* FBLPromise+Delay.m in Sources */ = {isa = PBXBuildFile; fileRef = 32B947B515509CDFE5448CDFC266AD4C /* FBLPromise+Delay.m */; }; 4731E42E1C23BDA72E33A35304236EDF /* pb_encode.h in Headers */ = {isa = PBXBuildFile; fileRef = CA10B14B200CBBDD70F046459A21BA9A /* pb_encode.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4934F7F2E93BC5942280423FE7EF1011 /* FIRHeartbeatLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C27B670067918CA9EAD46FE14364CB6 /* FIRHeartbeatLogger.h */; settings = {ATTRIBUTES = (Project, ); }; }; @@ -146,7 +150,6 @@ 511C46211C09E13A1F25F2FBC9FEE7F2 /* FIRInstallationsAPIService.m in Sources */ = {isa = PBXBuildFile; fileRef = CC37AF2EDA0A3C8CF1A128ECC60F2B54 /* FIRInstallationsAPIService.m */; }; 51D9B80DF2DDA77B7F3046F70AB64235 /* FBLPromisePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = D5A6505AFEEB14BC1A8BA6633FC46B2D /* FBLPromisePrivate.h */; settings = {ATTRIBUTES = (Private, ); }; }; 52525B97CE89EC4A4C37C72DEC718395 /* GULLoggerCodes.h in Headers */ = {isa = PBXBuildFile; fileRef = 81DA2EB65AF59171C8F458415150F62E /* GULLoggerCodes.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 55E5BC2797D7588D1CCC40A4F44F4BDD /* Pods-iosApp-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = F87A626222A8D9B26448A9FEB04FD0DF /* Pods-iosApp-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; 560F5B2685ACC4D2B01FB5BDF64745BC /* FIRVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = 302A9C673A29DC906CA565FC05238E25 /* FIRVersion.h */; settings = {ATTRIBUTES = (Public, ); }; }; 562A84B038074FF216CCB38E2892561A /* FIRInstallationsIIDStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 84CF96770DA6D791B5A5537DF0F68671 /* FIRInstallationsIIDStore.h */; settings = {ATTRIBUTES = (Project, ); }; }; 56D29B4D355687DAF91ABB7BFF001FF6 /* nanopb-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = A167F5E41290D3780AEA6F31100D128C /* nanopb-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -159,9 +162,9 @@ 5E2DEA28BF3D89A9BD58DA33E5B2ACAF /* HeartbeatStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D56DBF937CDCEA7FD658C6CA925002F8 /* HeartbeatStorage.swift */; }; 5E70EE05CB4C923534C506FB87033965 /* FirebaseInstallations-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = DC69107262690A516BB1C1A5BCA3FCFD /* FirebaseInstallations-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; 5E7283FA2BAFF3DA36CE1B394564506B /* GULNSData+zlib.h in Headers */ = {isa = PBXBuildFile; fileRef = 73E27FDAC78B842CE6A202BB37BE68EC /* GULNSData+zlib.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 5F220E5099EA88F1A8D9975CEE1F78C9 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F37EF8D61BF172AB143158A5442AB923 /* UIKit.framework */; }; - 5F6A7F147321C28562075638809758A3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 67B07E9904CAAD5AD2278E2CBF9E13DD /* Foundation.framework */; }; 5FC07DA90623D72B9E4C55B6DA80BC18 /* FBLPromise+Catch.h in Headers */ = {isa = PBXBuildFile; fileRef = ABFE0C07CD2D1A61C9BC6687D6711089 /* FBLPromise+Catch.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6087648EA2CAF181DC254EBCCCCC567F /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B89AC0F55CFBADF08007EDA3C7D13512 /* Security.framework */; }; + 6092AED85BA0045B9AE6378FFF1BC2F8 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B19433800784CD7CF9CC033CCD10D5BC /* Foundation.framework */; }; 614D1893363CB77E110A8DEE5556CC1E /* FIRFirebaseUserAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = BDCCF5C7BC556FFF9B6DFA1B58EDDCD5 /* FIRFirebaseUserAgent.h */; settings = {ATTRIBUTES = (Project, ); }; }; 621BD8004C84798563F37F1A7337A24F /* GULOriginalIMPConvenienceMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = 6E3292759CA85D3BE3AA97AAE29AEC73 /* GULOriginalIMPConvenienceMacros.h */; settings = {ATTRIBUTES = (Public, ); }; }; 6284EEBE6BC915B8626B1D9774D78AD5 /* FBLPromise+Reduce.m in Sources */ = {isa = PBXBuildFile; fileRef = C8AAB9B48E2F6B2DFA514A79F0A1BE68 /* FBLPromise+Reduce.m */; }; @@ -170,9 +173,11 @@ 6564B2BA6559F60599A5D3F5ACBE0C27 /* FIRInstallationsStoredAuthToken.h in Headers */ = {isa = PBXBuildFile; fileRef = B33E6D6ED41737AF33884CE06E6FFFE2 /* FIRInstallationsStoredAuthToken.h */; settings = {ATTRIBUTES = (Project, ); }; }; 6698597B98DA35323AF850DC07E9B869 /* GULAppEnvironmentUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 9EC225D8FF805EFA9B2E7D8F4FFEAB10 /* GULAppEnvironmentUtil.h */; settings = {ATTRIBUTES = (Public, ); }; }; 66BEFA07B6134333E732AB6C595D560A /* GULAppDelegateSwizzler.m in Sources */ = {isa = PBXBuildFile; fileRef = A6B955015B6D025D118F800CCC77C156 /* GULAppDelegateSwizzler.m */; }; + 68785A1E78BF8077D48D8FC9A4493E38 /* FBLPromises.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E9B4A3EF13B1698D0495A6706C23EF19 /* FBLPromises.framework */; }; 69AA1D00B8422EBBD24F6B24BE59BEF7 /* FBLPromise+Await.h in Headers */ = {isa = PBXBuildFile; fileRef = 7AE1ACC00B59B18D22974A7E74F1FD9A /* FBLPromise+Await.h */; settings = {ATTRIBUTES = (Public, ); }; }; 6C553EF950E90A78454FA31ADF464E91 /* FBLPromise+Delay.h in Headers */ = {isa = PBXBuildFile; fileRef = E688477742C93A0DAE9473FBAC62BC5C /* FBLPromise+Delay.h */; settings = {ATTRIBUTES = (Public, ); }; }; 6F1ABCA91B6BC54512C54D40F9B4DA44 /* FBLPromise.m in Sources */ = {isa = PBXBuildFile; fileRef = 02B537EA06E477E47E9075F93734A747 /* FBLPromise.m */; }; + 6F8CA3CCE43321A0A5CFB31FCD12D9C2 /* Pods-iosApp-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 54DC7F9924F99FEE67A9AD3BEE09A10C /* Pods-iosApp-dummy.m */; }; 718035D12ED948AE2807FEC3F781AC77 /* GoogleUtilities-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 041677156AC63B88B27EC9716A0AAC86 /* GoogleUtilities-dummy.m */; }; 7243DDB2D15BA1C3430FE98665B1C1F5 /* FIRConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 27DB907F360292904F1540ECFBD2C9A7 /* FIRConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; }; 72DFC56EA710B77C0B5C28E756494013 /* FBLPromise.h in Headers */ = {isa = PBXBuildFile; fileRef = F9D2978FC99E07A04D836158DB963407 /* FBLPromise.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -188,9 +193,11 @@ 80AA772CCEC0EE0CE860B232AD0DCBC3 /* GULApplication.h in Headers */ = {isa = PBXBuildFile; fileRef = F23B31EF23969DB60EF977D0C5CA2216 /* GULApplication.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8120C96279524FAD10B7ECB1F746DC38 /* FBLPromise+Retry.h in Headers */ = {isa = PBXBuildFile; fileRef = 176983373E276F98CDDD18B3C3EE273C /* FBLPromise+Retry.h */; settings = {ATTRIBUTES = (Public, ); }; }; 81DF200F5C52CF6D9FCB25562FE03373 /* GULReachabilityMessageCode.h in Headers */ = {isa = PBXBuildFile; fileRef = CC1A0EAE537376579657227FE35F8F7B /* GULReachabilityMessageCode.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 8829BA2B31A9AE21BFE24DD6FEF44132 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 67B07E9904CAAD5AD2278E2CBF9E13DD /* Foundation.framework */; }; + 86B78819619D50591A96427B7E55E48E /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7E6C11F87347D30AEE0964D1C757C440 /* SystemConfiguration.framework */; }; + 8829BA2B31A9AE21BFE24DD6FEF44132 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B19433800784CD7CF9CC033CCD10D5BC /* Foundation.framework */; }; 8B007998E7F89121688D95514515EACE /* nanopb-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 5CAF6F1BE3FB8438B531F1D2EDF67D67 /* nanopb-dummy.m */; }; 8B38C7F92D5394D112FCDD898B82B043 /* GULNetwork.h in Headers */ = {isa = PBXBuildFile; fileRef = B7979D3D32021F5767F836B40BCEE3D3 /* GULNetwork.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8CF3CCAD0E6072D421DDC883EECEBBA1 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B002213E206FA15DD30F26AFF0CD6DEE /* UIKit.framework */; }; 8E407FDCAB70640DF8592AF816011697 /* FIRInstallationsStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 25E7C7175C3B0CBFB7DCD754BFE6AC30 /* FIRInstallationsStore.h */; settings = {ATTRIBUTES = (Project, ); }; }; 8FA90DF3DE914D42680C950F2C05A784 /* FIRDependency.h in Headers */ = {isa = PBXBuildFile; fileRef = 89F72989D83ED490F6BD3525C8697890 /* FIRDependency.h */; settings = {ATTRIBUTES = (Project, ); }; }; 9049AEE5C61B0C0C22331CC626DDCA36 /* RingBuffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32657CDD29CB29E4D9EAC9F3DFFF7F9B /* RingBuffer.swift */; }; @@ -200,8 +207,8 @@ 950BEA2DD8CBD21EA6A1EFF1941C2BC4 /* FIRComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = EC1A1287C11E2AC550C11AE957B85BED /* FIRComponent.h */; settings = {ATTRIBUTES = (Project, ); }; }; 97ADCF15FCD48FF64F0F1B101C287EF9 /* FIRBundleUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 5980CDFA4E39A2A7765B7EFF45659FB1 /* FIRBundleUtil.m */; }; 97C2A306683FFE378446F1BF620CE992 /* FIRInstallationsStoredItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A197F31892B4FC47546E6C78F03B504 /* FIRInstallationsStoredItem.m */; }; - 9A1F328345A9AC9079B18F3CD2A632F0 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1849556DA704B3114935A27CAD427E6C /* Security.framework */; }; 9BBDD622B5795ECD38CA4ACCD70C263C /* GULHeartbeatDateStorageUserDefaults.h in Headers */ = {isa = PBXBuildFile; fileRef = 8E2F59020C5D89312EEFA5E6046EB6A0 /* GULHeartbeatDateStorageUserDefaults.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C8CF266C98EA49D589F2FB7607805A6 /* FirebaseCoreInternal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D1555C539BC742E4F643ED7B7AEE297A /* FirebaseCoreInternal.framework */; }; 9D69E995D208AC9A95924DC3FA97CE4D /* FIRConfigurationInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 5FBF4122C43E6D4D4A64A3347D576A7E /* FIRConfigurationInternal.h */; settings = {ATTRIBUTES = (Project, ); }; }; 9EC25251C697E5E458B55757A082D399 /* FIRInstallations.h in Headers */ = {isa = PBXBuildFile; fileRef = 8DDD7DA14B336E2720E9423F37F2B29B /* FIRInstallations.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9FCA9891A84A6781B4711A77B07E6394 /* GULSwizzler.h in Headers */ = {isa = PBXBuildFile; fileRef = 7B6687999D41FFE9C8B8182FABD8D551 /* GULSwizzler.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -210,7 +217,6 @@ A0EEDA3717A889692F636F530F963E6E /* FIRConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = BDC351349AD8FCC1DD7CA884A35DEA9D /* FIRConfiguration.m */; }; A1A281AA2152FA204254CB7C609C49C6 /* pb_decode.c in Sources */ = {isa = PBXBuildFile; fileRef = E813A5DFF987F1AD9B56DA3C16CBC79E /* pb_decode.c */; settings = {COMPILER_FLAGS = "-fno-objc-arc -fno-objc-arc"; }; }; A1B831642CE06C614C8B433147655EC9 /* FBLPromise+Validate.h in Headers */ = {isa = PBXBuildFile; fileRef = BB27DDC09BB64B97ADD0C817C4813FD5 /* FBLPromise+Validate.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A1C3A8D392921576139CF7694A2B6DD8 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1849556DA704B3114935A27CAD427E6C /* Security.framework */; }; A26CB1424B6C77BFCA2520897A8B76E4 /* FIRInstallationsAuthTokenResultInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = E212CA1152DC7BD0A8D36A5464D8063A /* FIRInstallationsAuthTokenResultInternal.h */; settings = {ATTRIBUTES = (Project, ); }; }; A2A10D4F64C70763146A157521CADA10 /* FBLPromise+Then.m in Sources */ = {isa = PBXBuildFile; fileRef = AFEE4AF151DABB93170A24D3D6230C88 /* FBLPromise+Then.m */; }; A2B6EEA5A6810D3D714532B7C9F9141D /* FirebaseInstallationsInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 9AFEDCD048225A80ECB828726CE3AB6C /* FirebaseInstallationsInternal.h */; settings = {ATTRIBUTES = (Project, ); }; }; @@ -226,17 +232,19 @@ AF9EF94B534F1E6041498E9DCB35965C /* FBLPromise+Timeout.m in Sources */ = {isa = PBXBuildFile; fileRef = 6A9A160CDC9B97ED69C94292B750727E /* FBLPromise+Timeout.m */; }; AFF735A0BAD27511CBBA2AF7190CF7D2 /* GULSecureCoding.m in Sources */ = {isa = PBXBuildFile; fileRef = 84F574E58AFC747F706C38576594128F /* GULSecureCoding.m */; }; B03675FF43DB995BAE35890923A6B38C /* FIRInstallationsAuthTokenResult.m in Sources */ = {isa = PBXBuildFile; fileRef = C5BAF2D24C4393D5551ACFEF6ABABBFF /* FIRInstallationsAuthTokenResult.m */; }; + B2412057FC7B0842F3FFAFCB254370C1 /* FirebaseCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 35D858A0EF42E9D24F35AD51B50F1504 /* FirebaseCore.framework */; }; B499B5F7DE0009941231B6412E0CBBE4 /* FIRBundleUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 90F0B10AD4D28755F7444D2F3FD23E52 /* FIRBundleUtil.h */; settings = {ATTRIBUTES = (Project, ); }; }; - B61542C8A4F7DB0A4711D5A455A0233F /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7E343B046961E2648346BE23F8986EE7 /* SystemConfiguration.framework */; }; B68CD4520F701153489422DECC35F171 /* FIRInstallationsItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 39FF88046B16C864A6C5638473BB5C55 /* FIRInstallationsItem.h */; settings = {ATTRIBUTES = (Project, ); }; }; B6D44D44D047CFDF9FF5802D56E80C14 /* HeartbeatsBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5924BF64553E5C0C975E49695B2386D2 /* HeartbeatsBundle.swift */; }; B6E4E2C8CFD850CDF16B9D699489D558 /* GULLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D14337E2C96B4A2A99BFDA463F961C3 /* GULLogger.m */; }; B72123169D7B37A0933CD6EC68879C42 /* FIRInstallations.m in Sources */ = {isa = PBXBuildFile; fileRef = B737D65AD1F5BE4A1B2E96FBA2B7D990 /* FIRInstallations.m */; }; B75130F312E69A08D37E4688C7711E69 /* GULKeychainUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 65DEDBCC7A86258E2E547DDD97EF9221 /* GULKeychainUtils.h */; settings = {ATTRIBUTES = (Public, ); }; }; B7B867158FCF0F470F2AAFCE4F2C6A36 /* GULReachabilityChecker.h in Headers */ = {isa = PBXBuildFile; fileRef = 77EF77EC02F997D68D6BBE47A8915E90 /* GULReachabilityChecker.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B7C74C61589B73F8737691A11B9FC17F /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B89AC0F55CFBADF08007EDA3C7D13512 /* Security.framework */; }; B851A606993263285D8C8449E10A9FA9 /* FIRInstallationsStatus.h in Headers */ = {isa = PBXBuildFile; fileRef = D20FBDF8030C3295ADB1A745785FDFE9 /* FIRInstallationsStatus.h */; settings = {ATTRIBUTES = (Project, ); }; }; B90AC5A1BBA14717897EF3F2C762BAF7 /* GULKeychainUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 61420B536F050230057C1FE32E9F8338 /* GULKeychainUtils.m */; }; BBB7299A59994DA2BB16AC58E8493461 /* GULAppDelegateSwizzler_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 0A029C2318D02322C9CAD4482BC99E56 /* GULAppDelegateSwizzler_Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + BDFC42E6275F4A967F21F1C39BF16140 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B19433800784CD7CF9CC033CCD10D5BC /* Foundation.framework */; }; BEC481FAF78BC5C1E7A6FEBF1D747C03 /* GULReachabilityChecker+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 8F0FC650244DC3718811D909D3151EE3 /* GULReachabilityChecker+Internal.h */; settings = {ATTRIBUTES = (Project, ); }; }; BF443984E44CE6FF5E0535F64CE1D7EB /* FIRCurrentDateProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = C473845F6F2D89DBB9AB618F970E3F92 /* FIRCurrentDateProvider.m */; }; C0D2816BB5719514BEDC9B7FA0553531 /* FBLPromise+Race.h in Headers */ = {isa = PBXBuildFile; fileRef = C425988C08DB47674D60EC09BDBC9EE0 /* FBLPromise+Race.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -249,15 +257,14 @@ C973FA246E31255B6C42242AAEE50F84 /* FBLPromise+Reduce.h in Headers */ = {isa = PBXBuildFile; fileRef = B5DF0E1F8C3EF44CDDF9C6DB3927787F /* FBLPromise+Reduce.h */; settings = {ATTRIBUTES = (Public, ); }; }; CB377C80514FACAF3AE0009C164BE1B5 /* FIRInstallationsItem+RegisterInstallationAPI.m in Sources */ = {isa = PBXBuildFile; fileRef = 51E94BB6CB0C7F58DFE36CC424B82F94 /* FIRInstallationsItem+RegisterInstallationAPI.m */; }; CC38E51D16E92111007BF4BAAFE38F0B /* GULNetworkInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 5983E3C3B5C4A568A167FADEACD18FD6 /* GULNetworkInfo.m */; }; + CE4DB8ED6DF42A1D81177884BF596FD3 /* GoogleUtilities.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FE1A31337B0FAEB70E99E236BA66A468 /* GoogleUtilities.framework */; }; CEB3FEFB775C93022AF689A65ACE66F1 /* FIRComponentType.h in Headers */ = {isa = PBXBuildFile; fileRef = 4914E23D4DB66A078D5EB6B699F6FD42 /* FIRComponentType.h */; settings = {ATTRIBUTES = (Project, ); }; }; D2EC2BB6045973F02E4DA4C568B67FA9 /* GULNetworkURLSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 84149A1AC3E5F94E2EA5680BAE99A948 /* GULNetworkURLSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; D338A364B9476038FFB6FD80EFCE9500 /* FIRComponentContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = F79AC76C2A2284BCF7A3402F188C545B /* FIRComponentContainer.h */; settings = {ATTRIBUTES = (Project, ); }; }; D35C781D98A6EE06597D00EB4D639182 /* NSURLSession+GULPromises.m in Sources */ = {isa = PBXBuildFile; fileRef = 9854A92A0212AC964F0C604AA19AF320 /* NSURLSession+GULPromises.m */; }; D5CF19816AAA1133A6CBC8F38A1AB824 /* GULHeartbeatDateStorage.h in Headers */ = {isa = PBXBuildFile; fileRef = 9B6E8B46AC06A742EC0B7A9BAED570EC /* GULHeartbeatDateStorage.h */; settings = {ATTRIBUTES = (Public, ); }; }; D5E9C588D9431E54F06C1EF865BEFA22 /* FBLPromise+Testing.m in Sources */ = {isa = PBXBuildFile; fileRef = 8621B3A8BD9D31C86A4123F749334481 /* FBLPromise+Testing.m */; }; - D619D6E64715E7EA2E70A0B49A5C2309 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 67B07E9904CAAD5AD2278E2CBF9E13DD /* Foundation.framework */; }; D6A40D73A98555313840B0D31FEB6CE2 /* FirebaseCore-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 3094BE50ED374FDC8BFD0D9607193BB8 /* FirebaseCore-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D79FFBCCDE8CCA7E53DA3CEBBD898168 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 67B07E9904CAAD5AD2278E2CBF9E13DD /* Foundation.framework */; }; D93267A052FEB4B34B33062E262A1567 /* FIRInstallationsStore.m in Sources */ = {isa = PBXBuildFile; fileRef = AD176C4B1B26CF25A491526E6C48D9EF /* FIRInstallationsStore.m */; }; DADBA3B4B29CE5CE6DFDF32AC196AB7B /* GULLoggerLevel.h in Headers */ = {isa = PBXBuildFile; fileRef = 9161041B3C6BEF32FBB899095EA16236 /* GULLoggerLevel.h */; settings = {ATTRIBUTES = (Public, ); }; }; DC5554563B323469349AB1094F2C978A /* HeartbeatsPayload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C1263AAEC332EF76BD075B15401A950 /* HeartbeatsPayload.swift */; }; @@ -283,6 +290,7 @@ F22697CEFEB60900A416B97B8E769E66 /* GULURLSessionDataResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 75FDFB7693D77825F26BE01426D836E1 /* GULURLSessionDataResponse.m */; }; F28495D37CBF8E62DBE0BC17954B8118 /* FirebaseCoreInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = FFFC95668E3ECEED4EADB9E66E869C52 /* FirebaseCoreInternal.h */; settings = {ATTRIBUTES = (Project, ); }; }; F4532808E811C0B45183AE7D76EA20F2 /* pb_decode.h in Headers */ = {isa = PBXBuildFile; fileRef = 63B3D9799528D9D16EDEE930BA5CBA09 /* pb_decode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F4DED5D635C1764B61D83C3723E7B16E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B19433800784CD7CF9CC033CCD10D5BC /* Foundation.framework */; }; F79439D7CCBC6B01B715A58E1F65610F /* PromisesObjC-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 41B17356AC1CCA6C43EB56831F6896CD /* PromisesObjC-dummy.m */; }; FA28BC76653E2DF02E304F3663EEC786 /* FIRInstallationsLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = DCA12A05FC5EB3CD898684489C53284F /* FIRInstallationsLogger.m */; }; FA693CE7784D65DFF0E19E57C476AB6D /* _ObjC_HeartbeatsPayload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82A31B43F616C7368B03D7ABEBFE309A /* _ObjC_HeartbeatsPayload.swift */; }; @@ -298,19 +306,19 @@ remoteGlobalIDString = B53D977A951AFC38B21751B706C1DF83; remoteInfo = GoogleAppMeasurement; }; - 16DE3BC1037378FF7B6C47BE4C9B7841 /* PBXContainerItemProxy */ = { + 11B81494225C46E1DCFD4CA6193C328C /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; proxyType = 1; - remoteGlobalIDString = 87803597EB3F20FC46472B85392EC4FD; - remoteInfo = FirebaseInstallations; + remoteGlobalIDString = D2B5E7DCCBBFB32341D857D01211A1A3; + remoteInfo = nanopb; }; - 1CEB9D8AB30046A999AC84CFD532F9B0 /* PBXContainerItemProxy */ = { + 16DE3BC1037378FF7B6C47BE4C9B7841 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; proxyType = 1; - remoteGlobalIDString = B53D977A951AFC38B21751B706C1DF83; - remoteInfo = GoogleAppMeasurement; + remoteGlobalIDString = 87803597EB3F20FC46472B85392EC4FD; + remoteInfo = FirebaseInstallations; }; 241B2ADF661F95A8F00600EF4BD52524 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; @@ -333,13 +341,6 @@ remoteGlobalIDString = 8D7F5D5DD528D21A72DC87ADA5B12E2D; remoteInfo = GoogleUtilities; }; - 402DB2486B9F7702333974F81A9E978A /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 458B188365A307B3C128ABF524D1A3E3; - remoteInfo = GoogleUserMessagingPlatform; - }; 466198A6F3FF8EC8CC3DF15F581A77F6 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; @@ -347,13 +348,6 @@ remoteGlobalIDString = 25E9E9A17BC3F670357D7385C521E48E; remoteInfo = FirebaseCoreInternal; }; - 509BAC0C6F53B04C14A29D2139D16B5C /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 87803597EB3F20FC46472B85392EC4FD; - remoteInfo = FirebaseInstallations; - }; 5208F8DE77795387E086EA5EC171A0F3 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; @@ -361,33 +355,26 @@ remoteGlobalIDString = 4402AFF83DBDC4DD07E198685FDC2DF2; remoteInfo = FirebaseCore; }; - 54966192C2A24DFD90A2F81F7A9832C9 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; - proxyType = 1; - remoteGlobalIDString = D2B5E7DCCBBFB32341D857D01211A1A3; - remoteInfo = nanopb; - }; - 5603DBC4EDAF64F8820933C15721B91D /* PBXContainerItemProxy */ = { + 63D187B926B68FA83ECE584BC19FDB53 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; proxyType = 1; - remoteGlobalIDString = FEA3B3A570634836C0457F3D7CEF1699; - remoteInfo = "Google-Mobile-Ads-SDK"; + remoteGlobalIDString = C49E7A4D59E5C8BE8DE9FB1EFB150185; + remoteInfo = FirebaseAnalytics; }; - 79CB2F46803DA67090E0C51564C61052 /* PBXContainerItemProxy */ = { + 71E7B7777A04B903BCD198C61E2677D2 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; proxyType = 1; - remoteGlobalIDString = 4402AFF83DBDC4DD07E198685FDC2DF2; - remoteInfo = FirebaseCore; + remoteGlobalIDString = 8D7F5D5DD528D21A72DC87ADA5B12E2D; + remoteInfo = GoogleUtilities; }; - 845BF25808213B117C9B6FD34DDC6CA4 /* PBXContainerItemProxy */ = { + 8A854CD367BF94F0D4890F16205DC65C /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; proxyType = 1; - remoteGlobalIDString = 8D7F5D5DD528D21A72DC87ADA5B12E2D; - remoteInfo = GoogleUtilities; + remoteGlobalIDString = 458B188365A307B3C128ABF524D1A3E3; + remoteInfo = GoogleUserMessagingPlatform; }; 8AA5D1E35E68AF801665885BAB39BE7C /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; @@ -410,26 +397,40 @@ remoteGlobalIDString = 2BBF7206D7FAC92C82A042A99C4A98F8; remoteInfo = PromisesObjC; }; - B2C29E8875D89CC5D8BDE2C68F3CE32E /* PBXContainerItemProxy */ = { + 9EAC1EF71AF43778463B1343C855256D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; proxyType = 1; - remoteGlobalIDString = 8D7F5D5DD528D21A72DC87ADA5B12E2D; - remoteInfo = GoogleUtilities; + remoteGlobalIDString = 87803597EB3F20FC46472B85392EC4FD; + remoteInfo = FirebaseInstallations; }; - C230DD0778618FC5FAF689A375C6E712 /* PBXContainerItemProxy */ = { + A554B405FD46BE64C95886F04C91FE15 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4402AFF83DBDC4DD07E198685FDC2DF2; + remoteInfo = FirebaseCore; + }; + AEB6C87FF73C1B2BA566E98248A72E33 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FEA3B3A570634836C0457F3D7CEF1699; + remoteInfo = "Google-Mobile-Ads-SDK"; + }; + B2C29E8875D89CC5D8BDE2C68F3CE32E /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; proxyType = 1; remoteGlobalIDString = 8D7F5D5DD528D21A72DC87ADA5B12E2D; remoteInfo = GoogleUtilities; }; - C3A66FD77FC2EB7B449E8FB3E5B7DD89 /* PBXContainerItemProxy */ = { + C230DD0778618FC5FAF689A375C6E712 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; proxyType = 1; - remoteGlobalIDString = 8777C9F6889E59EFFD631D80AEE9048B; - remoteInfo = shared; + remoteGlobalIDString = 8D7F5D5DD528D21A72DC87ADA5B12E2D; + remoteInfo = GoogleUtilities; }; C3FC09889CCD9B12EF2ADF8A9EB5E38E /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; @@ -445,12 +446,19 @@ remoteGlobalIDString = D2B5E7DCCBBFB32341D857D01211A1A3; remoteInfo = nanopb; }; - CCE33DC78446010606214BB7D709EF98 /* PBXContainerItemProxy */ = { + C9EC65EBD32F6C5F302F5B7DFF889384 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; proxyType = 1; - remoteGlobalIDString = C49E7A4D59E5C8BE8DE9FB1EFB150185; - remoteInfo = FirebaseAnalytics; + remoteGlobalIDString = 25E9E9A17BC3F670357D7385C521E48E; + remoteInfo = FirebaseCoreInternal; + }; + CC05A166FCD70558DB4AA08092F92B31 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8777C9F6889E59EFFD631D80AEE9048B; + remoteInfo = shared; }; D3E4F8E4A30D3CD9C0734946A1B33C71 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; @@ -459,26 +467,26 @@ remoteGlobalIDString = 4402AFF83DBDC4DD07E198685FDC2DF2; remoteInfo = FirebaseCore; }; - D47854864D0B34D38172BEFF1EC23A84 /* PBXContainerItemProxy */ = { + DA86F512A924FA46A726C705DA08DE05 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; proxyType = 1; remoteGlobalIDString = FEA3B3A570634836C0457F3D7CEF1699; remoteInfo = "Google-Mobile-Ads-SDK"; }; - D9C4EFB2A1183FEE0FEAFEB40115C80F /* PBXContainerItemProxy */ = { + E33E7D1DD8656567DE47B207FF6C0BC3 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; proxyType = 1; - remoteGlobalIDString = 25E9E9A17BC3F670357D7385C521E48E; - remoteInfo = FirebaseCoreInternal; + remoteGlobalIDString = 2BBF7206D7FAC92C82A042A99C4A98F8; + remoteInfo = PromisesObjC; }; - EF251258910026107F311DC58F9E09D4 /* PBXContainerItemProxy */ = { + EACE8956249FE5EFE01B39078BD52390 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; proxyType = 1; - remoteGlobalIDString = 2BBF7206D7FAC92C82A042A99C4A98F8; - remoteInfo = PromisesObjC; + remoteGlobalIDString = B53D977A951AFC38B21751B706C1DF83; + remoteInfo = GoogleAppMeasurement; }; FB753648ACF49877D7B56A799442DBCD /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; @@ -498,7 +506,7 @@ 041677156AC63B88B27EC9716A0AAC86 /* GoogleUtilities-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "GoogleUtilities-dummy.m"; sourceTree = ""; }; 05E8E0C3662B0AC4859DAF12D00175F6 /* FirebaseInstallations.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = FirebaseInstallations.debug.xcconfig; sourceTree = ""; }; 0655019242944F77EA4373E50567932C /* FirebaseCore.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = FirebaseCore.debug.xcconfig; sourceTree = ""; }; - 06FC5C9CF96D60C50FCD47D339C91951 /* nanopb */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = nanopb; path = nanopb.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 06FC5C9CF96D60C50FCD47D339C91951 /* nanopb.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = nanopb.framework; path = nanopb.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 0745B7878B7ECE37B2246C52CFC8D4EC /* FIRApp.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRApp.m; path = FirebaseCore/Sources/FIRApp.m; sourceTree = ""; }; 09F2EE77189896066F96C1D32FCC30BC /* FIRLibrary.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRLibrary.h; path = FirebaseCore/Extension/FIRLibrary.h; sourceTree = ""; }; 0A029C2318D02322C9CAD4482BC99E56 /* GULAppDelegateSwizzler_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULAppDelegateSwizzler_Private.h; path = GoogleUtilities/AppDelegateSwizzler/Internal/GULAppDelegateSwizzler_Private.h; sourceTree = ""; }; @@ -510,8 +518,8 @@ 0E55D5E04787FD5D12DB474116F07D3A /* GULHeartbeatDateStorage.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULHeartbeatDateStorage.m; path = GoogleUtilities/Environment/GULHeartbeatDateStorage.m; sourceTree = ""; }; 0F712EFB438E0703DF8789446E38514B /* FirebaseCore.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = FirebaseCore.modulemap; sourceTree = ""; }; 12409E0DE92B60629628A2F26336279A /* pb_encode.c */ = {isa = PBXFileReference; includeInIndex = 1; path = pb_encode.c; sourceTree = ""; }; - 13C8C8B254851998F9289F71229B28A2 /* FirebaseInstallations */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = FirebaseInstallations; path = FirebaseInstallations.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 148D0F9E8C7373FEAF40D800FC5F1BAA /* FirebaseCoreInternal */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = FirebaseCoreInternal; path = FirebaseCoreInternal.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 13C8C8B254851998F9289F71229B28A2 /* FirebaseInstallations.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = FirebaseInstallations.framework; path = FirebaseInstallations.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 148D0F9E8C7373FEAF40D800FC5F1BAA /* FirebaseCoreInternal.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = FirebaseCoreInternal.framework; path = FirebaseCoreInternal.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 163691785EB8A8446E28BBE08F5B0017 /* GULSceneDelegateSwizzler_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULSceneDelegateSwizzler_Private.h; path = GoogleUtilities/AppDelegateSwizzler/Internal/GULSceneDelegateSwizzler_Private.h; sourceTree = ""; }; 1729C489A788BD58C0D2297EC8D264AB /* GoogleMobileAds.xcframework */ = {isa = PBXFileReference; includeInIndex = 1; name = GoogleMobileAds.xcframework; path = Frameworks/GoogleMobileAdsFramework/GoogleMobileAds.xcframework; sourceTree = ""; }; 174791D473D7D2E89E78D440C5B1D6B7 /* GULNetworkInternal.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULNetworkInternal.h; path = GoogleUtilities/Network/GULNetworkInternal.h; sourceTree = ""; }; @@ -519,13 +527,13 @@ 179BE3E7E71E0605336E1598EDC031CD /* GoogleUtilities.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = GoogleUtilities.debug.xcconfig; sourceTree = ""; }; 182C2A989802E13276AACA88F092EA83 /* FIRInstallationsAPIService.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstallationsAPIService.h; path = FirebaseInstallations/Source/Library/InstallationsAPI/FIRInstallationsAPIService.h; sourceTree = ""; }; 18441CECB49FE7BD3888E185655406AB /* FIRDependency.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRDependency.m; path = FirebaseCore/Sources/FIRDependency.m; sourceTree = ""; }; - 1849556DA704B3114935A27CAD427E6C /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/Security.framework; sourceTree = DEVELOPER_DIR; }; 18590DCC84A3FE821C1C7F5EE54B3B1D /* FIRInstallationsStoredItem.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstallationsStoredItem.h; path = FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStoredItem.h; sourceTree = ""; }; 19ABA9BF6F0E4DDA991DE4D3793BC610 /* GoogleUtilities.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = GoogleUtilities.release.xcconfig; sourceTree = ""; }; 19F9C8124C5E77FCB2827F0C63B52963 /* FIRInstallationsSingleOperationPromiseCache.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstallationsSingleOperationPromiseCache.m; path = FirebaseInstallations/Source/Library/InstallationsIDController/FIRInstallationsSingleOperationPromiseCache.m; sourceTree = ""; }; 1B9A9C1287DE4D2AE03909357BB2B56B /* FBLPromiseError.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FBLPromiseError.m; path = Sources/FBLPromises/FBLPromiseError.m; sourceTree = ""; }; 1CE7D922D866CED1E2B06F38CAF8BE4A /* GULAppEnvironmentUtil.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULAppEnvironmentUtil.m; path = GoogleUtilities/Environment/third_party/GULAppEnvironmentUtil.m; sourceTree = ""; }; 1E4B02A67454B35E49CC1BBA4564B99F /* FirebaseAnalytics.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = FirebaseAnalytics.debug.xcconfig; sourceTree = ""; }; + 2021CCF073C758BA34A85D72EDB5881D /* shared-copy-dsyms.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "shared-copy-dsyms.sh"; sourceTree = ""; }; 202263E1C231D542C94DB073B7B1B8E2 /* FBLPromise+Timeout.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "FBLPromise+Timeout.h"; path = "Sources/FBLPromises/include/FBLPromise+Timeout.h"; sourceTree = ""; }; 2109C38E6294D1B865E19635AEE4BF5A /* FIRAppInternal.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRAppInternal.h; path = FirebaseCore/Extension/FIRAppInternal.h; sourceTree = ""; }; 232AFCC1D739556579DA21A7ED96D9D9 /* FirebaseCoreInternal-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "FirebaseCoreInternal-umbrella.h"; sourceTree = ""; }; @@ -545,10 +553,10 @@ 3094BE50ED374FDC8BFD0D9607193BB8 /* FirebaseCore-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "FirebaseCore-umbrella.h"; sourceTree = ""; }; 32657CDD29CB29E4D9EAC9F3DFFF7F9B /* RingBuffer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RingBuffer.swift; path = FirebaseCore/Internal/Sources/HeartbeatLogging/RingBuffer.swift; sourceTree = ""; }; 32B947B515509CDFE5448CDFC266AD4C /* FBLPromise+Delay.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "FBLPromise+Delay.m"; path = "Sources/FBLPromises/FBLPromise+Delay.m"; sourceTree = ""; }; - 3347A1AB6546F0A3977529B8F199DC41 /* PromisesObjC */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = PromisesObjC; path = FBLPromises.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 3347A1AB6546F0A3977529B8F199DC41 /* FBLPromises.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = FBLPromises.framework; path = PromisesObjC.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 350D5580F2A23579FA26202DB4340FE9 /* Google-Mobile-Ads-SDK-xcframeworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Google-Mobile-Ads-SDK-xcframeworks.sh"; sourceTree = ""; }; - 35548E3BD8DA30925E8FE97E67B84868 /* shared.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = shared.release.xcconfig; sourceTree = ""; }; 35AD687466BFAC0E48F198357852962E /* GULSecureCoding.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULSecureCoding.h; path = GoogleUtilities/Environment/Public/GoogleUtilities/GULSecureCoding.h; sourceTree = ""; }; + 35D858A0EF42E9D24F35AD51B50F1504 /* FirebaseCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FirebaseCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3657E1D70221356479656863CBB974C0 /* PromisesObjC.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = PromisesObjC.modulemap; sourceTree = ""; }; 36A409EEBF51AE11106302B2E9A4DA7F /* FIRInstallationsErrorUtil.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstallationsErrorUtil.m; path = FirebaseInstallations/Source/Library/Errors/FIRInstallationsErrorUtil.m; sourceTree = ""; }; 3738D75F32A8AD3AF11EEE20310597E1 /* FIRAnalyticsConfiguration.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRAnalyticsConfiguration.h; path = FirebaseCore/Sources/FIRAnalyticsConfiguration.h; sourceTree = ""; }; @@ -593,7 +601,6 @@ 657C4065ABCFB53CBFD507A2426AE82A /* FBLPromise+Always.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "FBLPromise+Always.m"; path = "Sources/FBLPromises/FBLPromise+Always.m"; sourceTree = ""; }; 65DEDBCC7A86258E2E547DDD97EF9221 /* GULKeychainUtils.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULKeychainUtils.h; path = GoogleUtilities/Environment/Public/GoogleUtilities/GULKeychainUtils.h; sourceTree = ""; }; 675C823867BD14A3FADEB635EECBC9C5 /* FIRInstallationsItem+RegisterInstallationAPI.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "FIRInstallationsItem+RegisterInstallationAPI.h"; path = "FirebaseInstallations/Source/Library/InstallationsAPI/FIRInstallationsItem+RegisterInstallationAPI.h"; sourceTree = ""; }; - 67B07E9904CAAD5AD2278E2CBF9E13DD /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; 68C6336CBA0D24C512CA09ABD0004AF0 /* FIRVersion.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRVersion.m; path = FirebaseCore/Sources/FIRVersion.m; sourceTree = ""; }; 6A19DF0B5DAE63150DFAAFF6EC5BD014 /* FIRLogger.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRLogger.h; path = FirebaseCore/Extension/FIRLogger.h; sourceTree = ""; }; 6A32A702BB662E36610DC7A72AF804EF /* FBLPromise+Await.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "FBLPromise+Await.m"; path = "Sources/FBLPromises/FBLPromise+Await.m"; sourceTree = ""; }; @@ -604,11 +611,12 @@ 7031B8EADC8709308A9F20A13014CDD2 /* GULNetworkInfo.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULNetworkInfo.h; path = GoogleUtilities/Environment/Public/GoogleUtilities/GULNetworkInfo.h; sourceTree = ""; }; 7038AF786FA7C1BDD55816630BE4CE06 /* FIRLogger.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRLogger.h; path = FirebaseCore/Extension/FIRLogger.h; sourceTree = ""; }; 70A121213A30CE83995624D591F96B68 /* FIRHeartbeatLogger.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRHeartbeatLogger.h; path = FirebaseCore/Extension/FIRHeartbeatLogger.h; sourceTree = ""; }; + 70D6E483B23F0B0627D6EE7BD5E32B58 /* shared.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = shared.framework; path = build/cocoapods/framework/shared.framework; sourceTree = ""; }; 71158C9437DAA6488611E4900AC56442 /* FBLPromise+Validate.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "FBLPromise+Validate.m"; path = "Sources/FBLPromises/FBLPromise+Validate.m"; sourceTree = ""; }; 71199DCB47E99280227FBCD527A631A4 /* FIRLibrary.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRLibrary.h; path = FirebaseCore/Extension/FIRLibrary.h; sourceTree = ""; }; 71EC7AB5B0962EA1AFA3C49B77205B94 /* FIROptions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIROptions.h; path = FirebaseCore/Sources/Public/FirebaseCore/FIROptions.h; sourceTree = ""; }; 720D56807FB9B8DD3D02649FFBCF5BFA /* FIRLoggerLevel.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRLoggerLevel.h; path = FirebaseCore/Sources/Public/FirebaseCore/FIRLoggerLevel.h; sourceTree = ""; }; - 73D4D08B8C9FAD17B10E8E5C0EB49A76 /* shared.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = shared.framework; path = build/cocoapods/framework/shared.framework; sourceTree = ""; }; + 734A7EB7736A65B8A61B7858DBED0A43 /* shared.podspec */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; lastKnownFileType = text; path = shared.podspec; sourceTree = ""; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 73E27FDAC78B842CE6A202BB37BE68EC /* GULNSData+zlib.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "GULNSData+zlib.h"; path = "GoogleUtilities/NSData+zlib/Public/GoogleUtilities/GULNSData+zlib.h"; sourceTree = ""; }; 750CAB3880D0B0911C841587CA66DDF9 /* GoogleUserMessagingPlatform.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = GoogleUserMessagingPlatform.debug.xcconfig; sourceTree = ""; }; 75FDFB7693D77825F26BE01426D836E1 /* GULURLSessionDataResponse.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULURLSessionDataResponse.m; path = GoogleUtilities/Environment/URLSessionPromiseWrapper/GULURLSessionDataResponse.m; sourceTree = ""; }; @@ -623,7 +631,7 @@ 7B6687999D41FFE9C8B8182FABD8D551 /* GULSwizzler.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULSwizzler.h; path = GoogleUtilities/MethodSwizzler/Public/GoogleUtilities/GULSwizzler.h; sourceTree = ""; }; 7C7AADA67DBE6A6F858F3272227C6C28 /* _ObjC_HeartbeatController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = _ObjC_HeartbeatController.swift; path = FirebaseCore/Internal/Sources/HeartbeatLogging/_ObjC_HeartbeatController.swift; sourceTree = ""; }; 7DF3042C05F8120877712DB85D79AC71 /* FIRDependency.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRDependency.h; path = FirebaseCore/Extension/FIRDependency.h; sourceTree = ""; }; - 7E343B046961E2648346BE23F8986EE7 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/SystemConfiguration.framework; sourceTree = DEVELOPER_DIR; }; + 7E6C11F87347D30AEE0964D1C757C440 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/SystemConfiguration.framework; sourceTree = DEVELOPER_DIR; }; 81CCE26FDAE62501E14609814E44F23B /* FIRComponentContainer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRComponentContainer.m; path = FirebaseCore/Sources/FIRComponentContainer.m; sourceTree = ""; }; 81DA2EB65AF59171C8F458415150F62E /* GULLoggerCodes.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULLoggerCodes.h; path = GoogleUtilities/Common/GULLoggerCodes.h; sourceTree = ""; }; 82A31B43F616C7368B03D7ABEBFE309A /* _ObjC_HeartbeatsPayload.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = _ObjC_HeartbeatsPayload.swift; path = FirebaseCore/Internal/Sources/HeartbeatLogging/_ObjC_HeartbeatsPayload.swift; sourceTree = ""; }; @@ -652,7 +660,6 @@ 91F3C6391919DA08C6795F404EEA4EB6 /* FIRComponentContainerInternal.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRComponentContainerInternal.h; path = FirebaseCore/Sources/FIRComponentContainerInternal.h; sourceTree = ""; }; 9341C00E7F2F64ADE4CC0E5523B27847 /* FIROptions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIROptions.m; path = FirebaseCore/Sources/FIROptions.m; sourceTree = ""; }; 947A9AF9A82B3A4CFB9883B7BD5B6294 /* StorageFactory.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = StorageFactory.swift; path = FirebaseCore/Internal/Sources/HeartbeatLogging/StorageFactory.swift; sourceTree = ""; }; - 95B09EA82E7AF9ACCCCAF3E55C859116 /* shared.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = shared.debug.xcconfig; sourceTree = ""; }; 97285F68FF8A47EBB9672BA4B108B0C0 /* FirebaseCoreInternal.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = FirebaseCoreInternal.debug.xcconfig; sourceTree = ""; }; 976EE95337AC91EF2804DDE5581D0220 /* PromisesObjC.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = PromisesObjC.release.xcconfig; sourceTree = ""; }; 9854A92A0212AC964F0C604AA19AF320 /* NSURLSession+GULPromises.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSURLSession+GULPromises.m"; path = "GoogleUtilities/Environment/URLSessionPromiseWrapper/NSURLSession+GULPromises.m"; sourceTree = ""; }; @@ -675,15 +682,17 @@ AE66F2C8F2DEF86C3D05B060FB8A36C3 /* FIRInstallationsBackoffController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstallationsBackoffController.h; path = FirebaseInstallations/Source/Library/InstallationsIDController/FIRInstallationsBackoffController.h; sourceTree = ""; }; AE67B042BEE3DEFD070137E297653EB9 /* FIRComponentContainer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRComponentContainer.h; path = FirebaseCore/Extension/FIRComponentContainer.h; sourceTree = ""; }; AFEE4AF151DABB93170A24D3D6230C88 /* FBLPromise+Then.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "FBLPromise+Then.m"; path = "Sources/FBLPromises/FBLPromise+Then.m"; sourceTree = ""; }; - B097DD7534E741D5C41838011D755842 /* Pods-iosApp */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = "Pods-iosApp"; path = Pods_iosApp.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + B002213E206FA15DD30F26AFF0CD6DEE /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; + B097DD7534E741D5C41838011D755842 /* Pods_iosApp.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_iosApp.framework; path = "Pods-iosApp.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; B0BD0AC09E05597D7E71514C1F689B6E /* FirebaseAnalytics-xcframeworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "FirebaseAnalytics-xcframeworks.sh"; sourceTree = ""; }; B0E9C805A891FD1BF01E63871A0326F6 /* FBLPromise+Async.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "FBLPromise+Async.m"; path = "Sources/FBLPromises/FBLPromise+Async.m"; sourceTree = ""; }; B1782D62469F1B577F7B8D0AB4B35EC0 /* FIRComponent.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRComponent.m; path = FirebaseCore/Sources/FIRComponent.m; sourceTree = ""; }; + B19433800784CD7CF9CC033CCD10D5BC /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; B21E9142E55B1B0CCFF57AAEB61B802B /* GULNetworkURLSession.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULNetworkURLSession.m; path = GoogleUtilities/Network/GULNetworkURLSession.m; sourceTree = ""; }; B24B17E9B33ACB5A1D9BC25C3DC74401 /* FirebaseCoreInternal-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "FirebaseCoreInternal-prefix.pch"; sourceTree = ""; }; B33E6D6ED41737AF33884CE06E6FFFE2 /* FIRInstallationsStoredAuthToken.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstallationsStoredAuthToken.h; path = FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStoredAuthToken.h; sourceTree = ""; }; B3DB7947D2D7E57100C3499F1ACC4FA4 /* nanopb.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = nanopb.modulemap; sourceTree = ""; }; - B43874C6CBB50E7134FBEC24BABFE14F /* GoogleUtilities */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = GoogleUtilities; path = GoogleUtilities.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + B43874C6CBB50E7134FBEC24BABFE14F /* GoogleUtilities.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = GoogleUtilities.framework; path = GoogleUtilities.framework; sourceTree = BUILT_PRODUCTS_DIR; }; B48DCEF4919180F5F4B029FAC5FDEBD8 /* FBLPromise+Retry.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "FBLPromise+Retry.m"; path = "Sources/FBLPromises/FBLPromise+Retry.m"; sourceTree = ""; }; B5DF0E1F8C3EF44CDDF9C6DB3927787F /* FBLPromise+Reduce.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "FBLPromise+Reduce.h"; path = "Sources/FBLPromises/include/FBLPromise+Reduce.h"; sourceTree = ""; }; B634C3ECC3AA33FE6F9B04F50E29059B /* GULHeartbeatDateStorageUserDefaults.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULHeartbeatDateStorageUserDefaults.m; path = GoogleUtilities/Environment/GULHeartbeatDateStorageUserDefaults.m; sourceTree = ""; }; @@ -691,6 +700,7 @@ B7979D3D32021F5767F836B40BCEE3D3 /* GULNetwork.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULNetwork.h; path = GoogleUtilities/Network/Public/GoogleUtilities/GULNetwork.h; sourceTree = ""; }; B7FC504C9DAE99800FC080DD1150F709 /* FIRAnalyticsConfiguration.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRAnalyticsConfiguration.m; path = FirebaseCore/Sources/FIRAnalyticsConfiguration.m; sourceTree = ""; }; B802DE83C4686486CB7FBE662F5574C4 /* GULUserDefaults.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULUserDefaults.h; path = GoogleUtilities/UserDefaults/Public/GoogleUtilities/GULUserDefaults.h; sourceTree = ""; }; + B89AC0F55CFBADF08007EDA3C7D13512 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/Security.framework; sourceTree = DEVELOPER_DIR; }; BAA1734452EA91231F7F718B7D765A71 /* FIRInstallationsErrors.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstallationsErrors.h; path = FirebaseInstallations/Source/Library/Public/FirebaseInstallations/FIRInstallationsErrors.h; sourceTree = ""; }; BB27DDC09BB64B97ADD0C817C4813FD5 /* FBLPromise+Validate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "FBLPromise+Validate.h"; path = "Sources/FBLPromises/include/FBLPromise+Validate.h"; sourceTree = ""; }; BD136F43D748282CA1ABB2FA16D84DE8 /* GoogleUtilities.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = GoogleUtilities.modulemap; sourceTree = ""; }; @@ -716,28 +726,32 @@ CDB2EA7331D51882542E152D5ACE320E /* Storage.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Storage.swift; path = FirebaseCore/Internal/Sources/HeartbeatLogging/Storage.swift; sourceTree = ""; }; CF230FB2DA2476009008C3202D7B53B2 /* FBLPromise+Catch.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "FBLPromise+Catch.m"; path = "Sources/FBLPromises/FBLPromise+Catch.m"; sourceTree = ""; }; CF5737609C9D5A5F36C2A5E26F386D2E /* GoogleUtilities-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "GoogleUtilities-Info.plist"; sourceTree = ""; }; + D1555C539BC742E4F643ED7B7AEE297A /* FirebaseCoreInternal.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FirebaseCoreInternal.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D20FBDF8030C3295ADB1A745785FDFE9 /* FIRInstallationsStatus.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstallationsStatus.h; path = FirebaseInstallations/Source/Library/InstallationsIDController/FIRInstallationsStatus.h; sourceTree = ""; }; D39EB2D7CAB4A6A23C3A2896BB39D827 /* GULSceneDelegateSwizzler.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULSceneDelegateSwizzler.m; path = GoogleUtilities/AppDelegateSwizzler/GULSceneDelegateSwizzler.m; sourceTree = ""; }; D3E7F89E9D69D0FEF4DEE66F3C9DDD88 /* GoogleAppMeasurement.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = GoogleAppMeasurement.debug.xcconfig; sourceTree = ""; }; D56DBF937CDCEA7FD658C6CA925002F8 /* HeartbeatStorage.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = HeartbeatStorage.swift; path = FirebaseCore/Internal/Sources/HeartbeatLogging/HeartbeatStorage.swift; sourceTree = ""; }; D5A6505AFEEB14BC1A8BA6633FC46B2D /* FBLPromisePrivate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FBLPromisePrivate.h; path = Sources/FBLPromises/include/FBLPromisePrivate.h; sourceTree = ""; }; D74228B38875AD90DC34E82BAE930616 /* GoogleUserMessagingPlatform.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = GoogleUserMessagingPlatform.release.xcconfig; sourceTree = ""; }; + D830325D4FB0D56847E40BBD616A7635 /* shared.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = shared.release.xcconfig; sourceTree = ""; }; D98150D25FF37BDC7D58B076A71E33F4 /* FIRAppInternal.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRAppInternal.h; path = FirebaseCore/Extension/FIRAppInternal.h; sourceTree = ""; }; DA57337F988B543E71A535E71C7D8CE1 /* GULAppDelegateSwizzler.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULAppDelegateSwizzler.h; path = GoogleUtilities/AppDelegateSwizzler/Public/GoogleUtilities/GULAppDelegateSwizzler.h; sourceTree = ""; }; DADEB7CA0D89E05306D6A054512E7395 /* GULURLSessionDataResponse.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULURLSessionDataResponse.h; path = GoogleUtilities/Environment/Public/GoogleUtilities/GULURLSessionDataResponse.h; sourceTree = ""; }; DB0F24D3BDFF34C7EA8012DA2402B6BB /* FIRInstallationsAuthTokenResult.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstallationsAuthTokenResult.h; path = FirebaseInstallations/Source/Library/Public/FirebaseInstallations/FIRInstallationsAuthTokenResult.h; sourceTree = ""; }; DBC0C776BA466F46652BC4CC8A56AA9D /* FIRFirebaseUserAgent.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRFirebaseUserAgent.m; path = FirebaseCore/Sources/FIRFirebaseUserAgent.m; sourceTree = ""; }; DC69107262690A516BB1C1A5BCA3FCFD /* FirebaseInstallations-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "FirebaseInstallations-umbrella.h"; sourceTree = ""; }; + DC694ABF344CE2D9D645124A32F45D91 /* shared.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = shared.debug.xcconfig; sourceTree = ""; }; DCA12A05FC5EB3CD898684489C53284F /* FIRInstallationsLogger.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstallationsLogger.m; path = FirebaseInstallations/Source/Library/FIRInstallationsLogger.m; sourceTree = ""; }; DDF49266C54A6C99D67004610AE5775E /* FBLPromise+All.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "FBLPromise+All.m"; path = "Sources/FBLPromises/FBLPromise+All.m"; sourceTree = ""; }; E212CA1152DC7BD0A8D36A5464D8063A /* FIRInstallationsAuthTokenResultInternal.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstallationsAuthTokenResultInternal.h; path = FirebaseInstallations/Source/Library/FIRInstallationsAuthTokenResultInternal.h; sourceTree = ""; }; - E2B63D462DB7F827C4B11FD51E4F8E2D /* FirebaseCore */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = FirebaseCore; path = FirebaseCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + E2B63D462DB7F827C4B11FD51E4F8E2D /* FirebaseCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = FirebaseCore.framework; path = FirebaseCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; E3972D3B88FBA26D2CC7343ECCBDDDE1 /* FIRInstallationsIDController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = FIRInstallationsIDController.m; path = FirebaseInstallations/Source/Library/InstallationsIDController/FIRInstallationsIDController.m; sourceTree = ""; }; E688477742C93A0DAE9473FBAC62BC5C /* FBLPromise+Delay.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "FBLPromise+Delay.h"; path = "Sources/FBLPromises/include/FBLPromise+Delay.h"; sourceTree = ""; }; E813A5DFF987F1AD9B56DA3C16CBC79E /* pb_decode.c */ = {isa = PBXFileReference; includeInIndex = 1; path = pb_decode.c; sourceTree = ""; }; E87C69D6A8C83CDD087C5BBE6AB8050C /* FirebaseInstallations-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "FirebaseInstallations-dummy.m"; sourceTree = ""; }; E8EEBECB08EDF7A240F6AE8B0C6A1A90 /* GULNetworkConstants.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULNetworkConstants.m; path = GoogleUtilities/Network/GULNetworkConstants.m; sourceTree = ""; }; E8F4B4272A71C166D33CFB12C2E62A63 /* FirebaseInstallations-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "FirebaseInstallations-Info.plist"; sourceTree = ""; }; + E9B4A3EF13B1698D0495A6706C23EF19 /* FBLPromises.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FBLPromises.framework; sourceTree = BUILT_PRODUCTS_DIR; }; EA2EBAC39BC8327B0F88CC88AE68C27C /* PromisesObjC-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "PromisesObjC-umbrella.h"; sourceTree = ""; }; EB55DBAFE01CE4E39A0676FEEDF357C2 /* GULUserDefaults.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULUserDefaults.m; path = GoogleUtilities/UserDefaults/GULUserDefaults.m; sourceTree = ""; }; EBE7F9880821B607B9D84836578144DA /* GULNetworkMessageCode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULNetworkMessageCode.h; path = GoogleUtilities/Network/Public/GoogleUtilities/GULNetworkMessageCode.h; sourceTree = ""; }; @@ -746,7 +760,6 @@ F03542A3B43629E3E1A5A2DEF5EF8B68 /* PromisesObjC-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "PromisesObjC-Info.plist"; sourceTree = ""; }; F1A1178306BE380ADC6B7F0E104F570F /* FirebaseCoreInternal.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FirebaseCoreInternal.h; path = FirebaseCore/Extension/FirebaseCoreInternal.h; sourceTree = ""; }; F23B31EF23969DB60EF977D0C5CA2216 /* GULApplication.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULApplication.h; path = GoogleUtilities/AppDelegateSwizzler/Public/GoogleUtilities/GULApplication.h; sourceTree = ""; }; - F37EF8D61BF172AB143158A5442AB923 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; F3B992EBA8578DD653EFAAF2E3CB1627 /* Heartbeat.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Heartbeat.swift; path = FirebaseCore/Internal/Sources/HeartbeatLogging/Heartbeat.swift; sourceTree = ""; }; F453D5613F98EDF6566FEE00C8D010E1 /* FIRCurrentDateProvider.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRCurrentDateProvider.h; path = FirebaseInstallations/Source/Library/InstallationsIDController/FIRCurrentDateProvider.h; sourceTree = ""; }; F5F377A45DA36A443A83B3C794CE83C3 /* GoogleUserMessagingPlatform-xcframeworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "GoogleUserMessagingPlatform-xcframeworks.sh"; sourceTree = ""; }; @@ -756,56 +769,61 @@ F9D2978FC99E07A04D836158DB963407 /* FBLPromise.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FBLPromise.h; path = Sources/FBLPromises/include/FBLPromise.h; sourceTree = ""; }; FA47EB5B5D255D41576713C9CA2656AB /* FIRInstallationsIIDTokenStore.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstallationsIIDTokenStore.h; path = FirebaseInstallations/Source/Library/IIDMigration/FIRInstallationsIIDTokenStore.h; sourceTree = ""; }; FAEF86A841924AE597ACCB86E3636D39 /* FBLPromise+Recover.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "FBLPromise+Recover.m"; path = "Sources/FBLPromises/FBLPromise+Recover.m"; sourceTree = ""; }; - FB1BF8BE937671FB0F330C2D28634477 /* shared.podspec */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; lastKnownFileType = text; path = shared.podspec; sourceTree = ""; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; FB411A050B2B2B500AE988000EB01E6F /* Google-Mobile-Ads-SDK.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Google-Mobile-Ads-SDK.debug.xcconfig"; sourceTree = ""; }; FC8EB6834E362DD01AC136465AAB1FD7 /* GULMutableDictionary.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = GULMutableDictionary.m; path = GoogleUtilities/Network/GULMutableDictionary.m; sourceTree = ""; }; FD041D13A32B5505C04AA8BE550BB404 /* FBLPromise+Any.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "FBLPromise+Any.h"; path = "Sources/FBLPromises/include/FBLPromise+Any.h"; sourceTree = ""; }; FDAA128405DC9D963FDE07AB8542E61F /* GULMutableDictionary.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = GULMutableDictionary.h; path = GoogleUtilities/Network/Public/GoogleUtilities/GULMutableDictionary.h; sourceTree = ""; }; + FE1A31337B0FAEB70E99E236BA66A468 /* GoogleUtilities.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = GoogleUtilities.framework; sourceTree = BUILT_PRODUCTS_DIR; }; FEE9E11571A34737DE67E784A6459200 /* FIRInstallationsHTTPError.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FIRInstallationsHTTPError.h; path = FirebaseInstallations/Source/Library/Errors/FIRInstallationsHTTPError.h; sourceTree = ""; }; FFFC95668E3ECEED4EADB9E66E869C52 /* FirebaseCoreInternal.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = FirebaseCoreInternal.h; path = FirebaseCore/Extension/FirebaseCoreInternal.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 19A222FC88081FB3DD2F70502C7F3653 /* Frameworks */ = { + 002436A3B83EECCFE2570CBAAD0A4EF8 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 8829BA2B31A9AE21BFE24DD6FEF44132 /* Foundation.framework in Frameworks */, + F4DED5D635C1764B61D83C3723E7B16E /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; - 39CF113F5672038E9DA1BA8D0EAE86B2 /* Frameworks */ = { + 0ADFF0FAA8774CD572CE2A733509CE81 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D79FFBCCDE8CCA7E53DA3CEBBD898168 /* Foundation.framework in Frameworks */, - 5F220E5099EA88F1A8D9975CEE1F78C9 /* UIKit.framework in Frameworks */, + 68785A1E78BF8077D48D8FC9A4493E38 /* FBLPromises.framework in Frameworks */, + B2412057FC7B0842F3FFAFCB254370C1 /* FirebaseCore.framework in Frameworks */, + 37AA40A4EDB0593675DA62220372A149 /* Foundation.framework in Frameworks */, + 02DBBFE6415C5992B59E330C0CB7CBCC /* GoogleUtilities.framework in Frameworks */, + B7C74C61589B73F8737691A11B9FC17F /* Security.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; - 76FDE13B9567F153A91BD6DD4ABF4B2E /* Frameworks */ = { + 19A222FC88081FB3DD2F70502C7F3653 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D619D6E64715E7EA2E70A0B49A5C2309 /* Foundation.framework in Frameworks */, - 9A1F328345A9AC9079B18F3CD2A632F0 /* Security.framework in Frameworks */, - B61542C8A4F7DB0A4711D5A455A0233F /* SystemConfiguration.framework in Frameworks */, + 8829BA2B31A9AE21BFE24DD6FEF44132 /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; - 8D7950586988288D9CC63C913CC01DF7 /* Frameworks */ = { + 4060AA48B736E4AE167860CB3FEB30E4 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 3CC39B64DC6D709E754934AF2B1CECAF /* Foundation.framework in Frameworks */, + 25BE72244FAEDAE5507471612448F6E1 /* FBLPromises.framework in Frameworks */, + 2DF20BF5B0A2DDE2C3CE06190DCFC5B9 /* Foundation.framework in Frameworks */, + 6087648EA2CAF181DC254EBCCCCC567F /* Security.framework in Frameworks */, + 86B78819619D50591A96427B7E55E48E /* SystemConfiguration.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; - C6702ED1BAE4DBFA7E1256707BA30DE7 /* Frameworks */ = { + 54CBB8C05368603FBA236149E0DB2DFE /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 0C9D25134C98367EE9E24ADB21D3F27C /* Foundation.framework in Frameworks */, + BDFC42E6275F4A967F21F1C39BF16140 /* Foundation.framework in Frameworks */, + CE4DB8ED6DF42A1D81177884BF596FD3 /* GoogleUtilities.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -817,12 +835,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - EDA0CAE27C462E1FC95A3F2225C314FB /* Frameworks */ = { + E1630A2842FDC5A103240BDFED47E7C8 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 5F6A7F147321C28562075638809758A3 /* Foundation.framework in Frameworks */, - A1C3A8D392921576139CF7694A2B6DD8 /* Security.framework in Frameworks */, + 9C8CF266C98EA49D589F2FB7607805A6 /* FirebaseCoreInternal.framework in Frameworks */, + 6092AED85BA0045B9AE6378FFF1BC2F8 /* Foundation.framework in Frameworks */, + 334C3AA66C26197A144AAE81CD46FFA3 /* GoogleUtilities.framework in Frameworks */, + 8CF3CCAD0E6072D421DDC883EECEBBA1 /* UIKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -948,6 +968,18 @@ name = Frameworks; sourceTree = ""; }; + 268C59CAF7516100CA326A93F80A7522 /* Frameworks */ = { + isa = PBXGroup; + children = ( + E9B4A3EF13B1698D0495A6706C23EF19 /* FBLPromises.framework */, + 35D858A0EF42E9D24F35AD51B50F1504 /* FirebaseCore.framework */, + D1555C539BC742E4F643ED7B7AEE297A /* FirebaseCoreInternal.framework */, + FE1A31337B0FAEB70E99E236BA66A468 /* GoogleUtilities.framework */, + F518261A56B4E9DB6C1EA84AA7FBEC34 /* iOS */, + ); + name = Frameworks; + sourceTree = ""; + }; 2A1F190F16C43F374ED9D2A2187575E9 /* Logger */ = { isa = PBXGroup; children = ( @@ -958,10 +990,18 @@ name = Logger; sourceTree = ""; }; + 301E7E01BDCE486E3B1785C9B8188C70 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 70D6E483B23F0B0627D6EE7BD5E32B58 /* shared.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; 313FE5FE915A4A924C55AAC02A910D61 /* Development Pods */ = { isa = PBXGroup; children = ( - EEF3277DCE2E4B31CF76A57AB68C2BA1 /* shared */, + D8A02EC6C8DF3366897E8E9AFF0F6E82 /* shared */, ); name = "Development Pods"; sourceTree = ""; @@ -1032,14 +1072,6 @@ path = "../Target Support Files/FirebaseAnalytics"; sourceTree = ""; }; - 569B56E5B05425CAE32676C6049DC8CB /* Pod */ = { - isa = PBXGroup; - children = ( - FB1BF8BE937671FB0F330C2D28634477 /* shared.podspec */, - ); - name = Pod; - sourceTree = ""; - }; 56B660CF9D97A02C1DFD02DE8C01A47C /* Environment */ = { isa = PBXGroup; children = ( @@ -1066,6 +1098,14 @@ name = Environment; sourceTree = ""; }; + 58DAE3B502B145BFD26ADEF120E929E9 /* Pod */ = { + isa = PBXGroup; + children = ( + 734A7EB7736A65B8A61B7858DBED0A43 /* shared.podspec */, + ); + name = Pod; + sourceTree = ""; + }; 5A432ED3550675743641097897737F9C /* Support Files */ = { isa = PBXGroup; children = ( @@ -1091,16 +1131,6 @@ name = Reachability; sourceTree = ""; }; - 5CB66AAACA3977B45E9869C360E9F289 /* Support Files */ = { - isa = PBXGroup; - children = ( - 95B09EA82E7AF9ACCCCAF3E55C859116 /* shared.debug.xcconfig */, - 35548E3BD8DA30925E8FE97E67B84868 /* shared.release.xcconfig */, - ); - name = "Support Files"; - path = "../iosApp/Pods/Target Support Files/shared"; - sourceTree = ""; - }; 60D7121D958D613AAEFEAEC6D66C2EC3 /* Support Files */ = { isa = PBXGroup; children = ( @@ -1137,14 +1167,6 @@ path = GoogleUtilities; sourceTree = ""; }; - 7DBF81461E18ACE1B4BD971BAC841CF7 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 73D4D08B8C9FAD17B10E8E5C0EB49A76 /* shared.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; 890CB263DBAD75A59C55798605E0D667 /* PromisesObjC */ = { isa = PBXGroup; children = ( @@ -1212,17 +1234,6 @@ name = Network; sourceTree = ""; }; - 9134D6817AC4DBFB37D3D183C5E3D9B1 /* iOS */ = { - isa = PBXGroup; - children = ( - 67B07E9904CAAD5AD2278E2CBF9E13DD /* Foundation.framework */, - 1849556DA704B3114935A27CAD427E6C /* Security.framework */, - 7E343B046961E2648346BE23F8986EE7 /* SystemConfiguration.framework */, - F37EF8D61BF172AB143158A5442AB923 /* UIKit.framework */, - ); - name = iOS; - sourceTree = ""; - }; 9BCD571095B275D0CEC164E4DB2273A3 /* Pods */ = { isa = PBXGroup; children = ( @@ -1240,20 +1251,6 @@ name = Pods; sourceTree = ""; }; - 9E25BFBA31DF4415ED9BE3503A2E04A9 /* Products */ = { - isa = PBXGroup; - children = ( - E2B63D462DB7F827C4B11FD51E4F8E2D /* FirebaseCore */, - 148D0F9E8C7373FEAF40D800FC5F1BAA /* FirebaseCoreInternal */, - 13C8C8B254851998F9289F71229B28A2 /* FirebaseInstallations */, - B43874C6CBB50E7134FBEC24BABFE14F /* GoogleUtilities */, - 06FC5C9CF96D60C50FCD47D339C91951 /* nanopb */, - B097DD7534E741D5C41838011D755842 /* Pods-iosApp */, - 3347A1AB6546F0A3977529B8F199DC41 /* PromisesObjC */, - ); - name = Products; - sourceTree = ""; - }; 9F26B2457D6EEFA11C8199585BA01B54 /* AppDelegateSwizzler */ = { isa = PBXGroup; children = ( @@ -1269,6 +1266,20 @@ name = AppDelegateSwizzler; sourceTree = ""; }; + A13C0A79379EBEC38D41531810697AE9 /* Products */ = { + isa = PBXGroup; + children = ( + 3347A1AB6546F0A3977529B8F199DC41 /* FBLPromises.framework */, + E2B63D462DB7F827C4B11FD51E4F8E2D /* FirebaseCore.framework */, + 148D0F9E8C7373FEAF40D800FC5F1BAA /* FirebaseCoreInternal.framework */, + 13C8C8B254851998F9289F71229B28A2 /* FirebaseInstallations.framework */, + B43874C6CBB50E7134FBEC24BABFE14F /* GoogleUtilities.framework */, + 06FC5C9CF96D60C50FCD47D339C91951 /* nanopb.framework */, + B097DD7534E741D5C41838011D755842 /* Pods_iosApp.framework */, + ); + name = Products; + sourceTree = ""; + }; A4E0996F38DD5E149C86328943F9D235 /* FirebaseAnalytics */ = { isa = PBXGroup; children = ( @@ -1286,12 +1297,15 @@ name = encode; sourceTree = ""; }; - BA4F31F07263C99FC76E66D632A59F09 /* Frameworks */ = { + B3112B9B2074A9812D0CC44DABC96498 /* Support Files */ = { isa = PBXGroup; children = ( - 9134D6817AC4DBFB37D3D183C5E3D9B1 /* iOS */, + 2021CCF073C758BA34A85D72EDB5881D /* shared-copy-dsyms.sh */, + DC694ABF344CE2D9D645124A32F45D91 /* shared.debug.xcconfig */, + D830325D4FB0D56847E40BBD616A7635 /* shared.release.xcconfig */, ); - name = Frameworks; + name = "Support Files"; + path = "../iosApp/Pods/Target Support Files/shared"; sourceTree = ""; }; C25B005353D2AF779E5B728D569C0B42 /* Support Files */ = { @@ -1380,9 +1394,9 @@ children = ( 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */, 313FE5FE915A4A924C55AAC02A910D61 /* Development Pods */, - BA4F31F07263C99FC76E66D632A59F09 /* Frameworks */, + 268C59CAF7516100CA326A93F80A7522 /* Frameworks */, 9BCD571095B275D0CEC164E4DB2273A3 /* Pods */, - 9E25BFBA31DF4415ED9BE3503A2E04A9 /* Products */, + A13C0A79379EBEC38D41531810697AE9 /* Products */, 11C970DEAE48C6D0282DFE54684F53F1 /* Targets Support Files */, ); sourceTree = ""; @@ -1432,6 +1446,17 @@ path = "Google-Mobile-Ads-SDK"; sourceTree = ""; }; + D8A02EC6C8DF3366897E8E9AFF0F6E82 /* shared */ = { + isa = PBXGroup; + children = ( + 301E7E01BDCE486E3B1785C9B8188C70 /* Frameworks */, + 58DAE3B502B145BFD26ADEF120E929E9 /* Pod */, + B3112B9B2074A9812D0CC44DABC96498 /* Support Files */, + ); + name = shared; + path = ../../shared; + sourceTree = ""; + }; E360AB45F6449DB10BA53BA8D6D918A6 /* FirebaseCoreInternal */ = { isa = PBXGroup; children = ( @@ -1476,17 +1501,6 @@ name = MethodSwizzler; sourceTree = ""; }; - EEF3277DCE2E4B31CF76A57AB68C2BA1 /* shared */ = { - isa = PBXGroup; - children = ( - 7DBF81461E18ACE1B4BD971BAC841CF7 /* Frameworks */, - 569B56E5B05425CAE32676C6049DC8CB /* Pod */, - 5CB66AAACA3977B45E9869C360E9F289 /* Support Files */, - ); - name = shared; - path = ../../shared; - sourceTree = ""; - }; EF16DC3D1F784697A119298491787BE0 /* Support Files */ = { isa = PBXGroup; children = ( @@ -1509,6 +1523,17 @@ name = Frameworks; sourceTree = ""; }; + F518261A56B4E9DB6C1EA84AA7FBEC34 /* iOS */ = { + isa = PBXGroup; + children = ( + B19433800784CD7CF9CC033CCD10D5BC /* Foundation.framework */, + B89AC0F55CFBADF08007EDA3C7D13512 /* Security.framework */, + 7E6C11F87347D30AEE0964D1C757C440 /* SystemConfiguration.framework */, + B002213E206FA15DD30F26AFF0CD6DEE /* UIKit.framework */, + ); + name = iOS; + sourceTree = ""; + }; F630DCC20D27F375AE6F0311C69985C6 /* Support Files */ = { isa = PBXGroup; children = ( @@ -1643,11 +1668,11 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - B1D812AEB2BD03AF1054260236E4B54B /* Headers */ = { + EAFD8549B8247746BFD4C41494AB2F58 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 55E5BC2797D7588D1CCC40A4F44F4BDD /* Pods-iosApp-umbrella.h in Headers */, + 3FDFEE6521F1D5BD502F83CDD9659CD9 /* Pods-iosApp-umbrella.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1701,7 +1726,7 @@ buildPhases = ( 2EFC16F115A48BBF639DDF4D4DA30A1D /* Headers */, 7698604CA3EC796F1664213BDC864CFF /* Sources */, - C6702ED1BAE4DBFA7E1256707BA30DE7 /* Frameworks */, + 54CBB8C05368603FBA236149E0DB2DFE /* Frameworks */, 2FF854FB4F493FB7369D77EF500611B8 /* Resources */, ); buildRules = ( @@ -1711,7 +1736,7 @@ ); name = FirebaseCoreInternal; productName = FirebaseCoreInternal; - productReference = 148D0F9E8C7373FEAF40D800FC5F1BAA /* FirebaseCoreInternal */; + productReference = 148D0F9E8C7373FEAF40D800FC5F1BAA /* FirebaseCoreInternal.framework */; productType = "com.apple.product-type.framework"; }; 2BBF7206D7FAC92C82A042A99C4A98F8 /* PromisesObjC */ = { @@ -1728,8 +1753,8 @@ dependencies = ( ); name = PromisesObjC; - productName = FBLPromises; - productReference = 3347A1AB6546F0A3977529B8F199DC41 /* PromisesObjC */; + productName = PromisesObjC; + productReference = 3347A1AB6546F0A3977529B8F199DC41 /* FBLPromises.framework */; productType = "com.apple.product-type.framework"; }; 4402AFF83DBDC4DD07E198685FDC2DF2 /* FirebaseCore */ = { @@ -1738,7 +1763,7 @@ buildPhases = ( 60198A89D75117422E57923D1D83FEB8 /* Headers */, A6B2AF1F2A3ED7A366050B7AE7FABF52 /* Sources */, - 39CF113F5672038E9DA1BA8D0EAE86B2 /* Frameworks */, + E1630A2842FDC5A103240BDFED47E7C8 /* Frameworks */, 21E0DB327B7C261EA96B8D330EECD625 /* Resources */, ); buildRules = ( @@ -1749,7 +1774,7 @@ ); name = FirebaseCore; productName = FirebaseCore; - productReference = E2B63D462DB7F827C4B11FD51E4F8E2D /* FirebaseCore */; + productReference = E2B63D462DB7F827C4B11FD51E4F8E2D /* FirebaseCore.framework */; productType = "com.apple.product-type.framework"; }; 87803597EB3F20FC46472B85392EC4FD /* FirebaseInstallations */ = { @@ -1758,7 +1783,7 @@ buildPhases = ( A1AE2DD68288D08D6C7C59513D218760 /* Headers */, 543D3C603F844D3BC04169AD5E97694E /* Sources */, - EDA0CAE27C462E1FC95A3F2225C314FB /* Frameworks */, + 0ADFF0FAA8774CD572CE2A733509CE81 /* Frameworks */, E94729425E46CEC86F6234C1D1460994 /* Resources */, ); buildRules = ( @@ -1770,7 +1795,7 @@ ); name = FirebaseInstallations; productName = FirebaseInstallations; - productReference = 13C8C8B254851998F9289F71229B28A2 /* FirebaseInstallations */; + productReference = 13C8C8B254851998F9289F71229B28A2 /* FirebaseInstallations.framework */; productType = "com.apple.product-type.framework"; }; 8D7F5D5DD528D21A72DC87ADA5B12E2D /* GoogleUtilities */ = { @@ -1779,7 +1804,7 @@ buildPhases = ( 89AB0E027C9FACB602A3815BC09FEF1C /* Headers */, 6D4DFBA2C1B33F108E9695F62E059FEA /* Sources */, - 76FDE13B9567F153A91BD6DD4ABF4B2E /* Frameworks */, + 4060AA48B736E4AE167860CB3FEB30E4 /* Frameworks */, D6D0002CD3C7006437C4BAE3DE5FABB0 /* Resources */, ); buildRules = ( @@ -1789,7 +1814,7 @@ ); name = GoogleUtilities; productName = GoogleUtilities; - productReference = B43874C6CBB50E7134FBEC24BABFE14F /* GoogleUtilities */; + productReference = B43874C6CBB50E7134FBEC24BABFE14F /* GoogleUtilities.framework */; productType = "com.apple.product-type.framework"; }; D2B5E7DCCBBFB32341D857D01211A1A3 /* nanopb */ = { @@ -1807,36 +1832,36 @@ ); name = nanopb; productName = nanopb; - productReference = 06FC5C9CF96D60C50FCD47D339C91951 /* nanopb */; + productReference = 06FC5C9CF96D60C50FCD47D339C91951 /* nanopb.framework */; productType = "com.apple.product-type.framework"; }; ED39C638569286489CD697A6C8964146 /* Pods-iosApp */ = { isa = PBXNativeTarget; - buildConfigurationList = C1E531B18FF042262D4D2AAE781640F2 /* Build configuration list for PBXNativeTarget "Pods-iosApp" */; + buildConfigurationList = D85BF9F6FF41C893F67914EFCFFD9A4C /* Build configuration list for PBXNativeTarget "Pods-iosApp" */; buildPhases = ( - B1D812AEB2BD03AF1054260236E4B54B /* Headers */, - BBBD5D8A121B7E5183A56F9B4DB15EF0 /* Sources */, - 8D7950586988288D9CC63C913CC01DF7 /* Frameworks */, - A57E60599B87FB07D7253DBBBE491040 /* Resources */, + EAFD8549B8247746BFD4C41494AB2F58 /* Headers */, + 54F36A2B151026BBD9D325BEF4237822 /* Sources */, + 002436A3B83EECCFE2570CBAAD0A4EF8 /* Frameworks */, + 353A611BB26C677838243396D76F2199 /* Resources */, ); buildRules = ( ); dependencies = ( - 33209BDE2DF53405A8B772BA8A3F26BB /* PBXTargetDependency */, - 1485F12D875108FA72E14B418AD99A55 /* PBXTargetDependency */, - 8A7304A5367F2E2319F76E7AEAB0C74B /* PBXTargetDependency */, - 12F2A9DF1BA739191414B64830A82624 /* PBXTargetDependency */, - 4673118F57DAE9113942441A46CEBDB6 /* PBXTargetDependency */, - 74FE54C0BD7C40AD0ACB81BB7D7173B6 /* PBXTargetDependency */, - 9680596CF5BA72B769B32441159E55AF /* PBXTargetDependency */, - 8E34AAAE886C608639754C909144D945 /* PBXTargetDependency */, - DE5DF4BA21E21D6C5ACFF964EF8E364C /* PBXTargetDependency */, - 3E253770563F2D6771A20C7F7FDC3FFA /* PBXTargetDependency */, - 07860F13F3E7F35D8EC5A9FF32D1FF10 /* PBXTargetDependency */, + 6138316F745E6CDBAAAD88EE794DFEF0 /* PBXTargetDependency */, + EE7DF03127408EDF953C79B687007A76 /* PBXTargetDependency */, + F1A789E242781A840B4D50606B4F32A3 /* PBXTargetDependency */, + C1A0477563E5E143F1FEC1A5FB036525 /* PBXTargetDependency */, + B165E99BD673C95B932D277C966FB371 /* PBXTargetDependency */, + B9170F56C0BBA343C0B267A2F9ACCB2B /* PBXTargetDependency */, + 079FF4B318084911389186E337334B27 /* PBXTargetDependency */, + 5F8B190BCFB67A0D735014DCAC3F7D94 /* PBXTargetDependency */, + CC2BF516BF672A850116A70CB8BFA8FC /* PBXTargetDependency */, + EE39E0CEDFD595A5BB01985767168A73 /* PBXTargetDependency */, + E85F77C5D7837C7A9AF62048FE7D6EDF /* PBXTargetDependency */, ); name = "Pods-iosApp"; - productName = Pods_iosApp; - productReference = B097DD7534E741D5C41838011D755842 /* Pods-iosApp */; + productName = "Pods-iosApp"; + productReference = B097DD7534E741D5C41838011D755842 /* Pods_iosApp.framework */; productType = "com.apple.product-type.framework"; }; /* End PBXNativeTarget section */ @@ -1853,11 +1878,11 @@ developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( - Base, en, + Base, ); mainGroup = CF1408CF629C7361332E53B88F7BD30C; - productRefGroup = 9E25BFBA31DF4415ED9BE3503A2E04A9 /* Products */; + productRefGroup = A13C0A79379EBEC38D41531810697AE9 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( @@ -1892,21 +1917,21 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 454BC062946A726B0659D0FA742B3D05 /* Resources */ = { + 353A611BB26C677838243396D76F2199 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; - 5E6210385CCBF823E825B7A4E053B35E /* Resources */ = { + 454BC062946A726B0659D0FA742B3D05 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; - A57E60599B87FB07D7253DBBBE491040 /* Resources */ = { + 5E6210385CCBF823E825B7A4E053B35E /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( @@ -1964,15 +1989,22 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Google-Mobile-Ads-SDK/Google-Mobile-Ads-SDK-xcframeworks.sh\"\n"; showEnvVarsInLog = 0; }; - B921D95D8F7C055BFF178D5F7086615A /* [CP-User] Build shared */ = { + 9ACE8A73447CB9B790294378EA16A8EE /* [CP] Copy dSYMs */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); - name = "[CP-User] Build shared"; + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/shared/shared-copy-dsyms-input-files.xcfilelist", + ); + name = "[CP] Copy dSYMs"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/shared/shared-copy-dsyms-output-files.xcfilelist", + ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = " if [ \"YES\" = \"$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED\" ]; then\n echo \"Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \"YES\"\"\n exit 0\n fi\n set -ev\n REPO_ROOT=\"$PODS_TARGET_SRCROOT\"\n \"$REPO_ROOT/../gradlew\" -p \"$REPO_ROOT\" $KOTLIN_PROJECT_PATH:syncFramework -Pkotlin.native.cocoapods.platform=$PLATFORM_NAME -Pkotlin.native.cocoapods.archs=\"$ARCHS\" -Pkotlin.native.cocoapods.configuration=\"$CONFIGURATION\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/shared/shared-copy-dsyms.sh\"\n"; + showEnvVarsInLog = 0; }; BECE8EADBBEEF6E4641C0FB30ED69EE6 /* [CP] Copy XCFrameworks */ = { isa = PBXShellScriptBuildPhase; @@ -2008,6 +2040,16 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/GoogleUserMessagingPlatform/GoogleUserMessagingPlatform-xcframeworks.sh\"\n"; showEnvVarsInLog = 0; }; + DD416B42161958195EB2D2C00E58D9B5 /* [CP-User] Build shared */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + name = "[CP-User] Build shared"; + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = " if [ \"YES\" = \"$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED\" ]; then\n echo \"Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \"YES\"\"\n exit 0\n fi\n set -ev\n REPO_ROOT=\"$PODS_TARGET_SRCROOT\"\n \"$REPO_ROOT/../gradlew\" -p \"$REPO_ROOT\" $KOTLIN_PROJECT_PATH:syncFramework -Pkotlin.native.cocoapods.platform=$PLATFORM_NAME -Pkotlin.native.cocoapods.archs=\"$ARCHS\" -Pkotlin.native.cocoapods.configuration=\"$CONFIGURATION\"\n"; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -2036,6 +2078,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 54F36A2B151026BBD9D325BEF4237822 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6F8CA3CCE43321A0A5CFB31FCD12D9C2 /* Pods-iosApp-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 5B17D6BAD238F9F141003C9060FE30A0 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -2142,34 +2192,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - BBBD5D8A121B7E5183A56F9B4DB15EF0 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 263A276BC241899FD8DF8F1443BFDE88 /* Pods-iosApp-dummy.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ - 07860F13F3E7F35D8EC5A9FF32D1FF10 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = shared; - target = 8777C9F6889E59EFFD631D80AEE9048B /* shared */; - targetProxy = C3A66FD77FC2EB7B449E8FB3E5B7DD89 /* PBXContainerItemProxy */; - }; - 12F2A9DF1BA739191414B64830A82624 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = FirebaseInstallations; - target = 87803597EB3F20FC46472B85392EC4FD /* FirebaseInstallations */; - targetProxy = 509BAC0C6F53B04C14A29D2139D16B5C /* PBXContainerItemProxy */; - }; - 1485F12D875108FA72E14B418AD99A55 /* PBXTargetDependency */ = { + 079FF4B318084911389186E337334B27 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - name = FirebaseCore; - target = 4402AFF83DBDC4DD07E198685FDC2DF2 /* FirebaseCore */; - targetProxy = 79CB2F46803DA67090E0C51564C61052 /* PBXContainerItemProxy */; + name = GoogleUserMessagingPlatform; + target = 458B188365A307B3C128ABF524D1A3E3 /* GoogleUserMessagingPlatform */; + targetProxy = 8A854CD367BF94F0D4890F16205DC65C /* PBXContainerItemProxy */; }; 2DDA4E0F18FFD59063BB2FBD8F0AE9A5 /* PBXTargetDependency */ = { isa = PBXTargetDependency; @@ -2177,48 +2207,36 @@ target = 25E9E9A17BC3F670357D7385C521E48E /* FirebaseCoreInternal */; targetProxy = 466198A6F3FF8EC8CC3DF15F581A77F6 /* PBXContainerItemProxy */; }; - 33209BDE2DF53405A8B772BA8A3F26BB /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = FirebaseAnalytics; - target = C49E7A4D59E5C8BE8DE9FB1EFB150185 /* FirebaseAnalytics */; - targetProxy = CCE33DC78446010606214BB7D709EF98 /* PBXContainerItemProxy */; - }; 39DD91314EA900466231C7F9F459A6FC /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = GoogleUtilities; target = 8D7F5D5DD528D21A72DC87ADA5B12E2D /* GoogleUtilities */; targetProxy = 95BEC747017A5DF165E8EE954D7D8524 /* PBXContainerItemProxy */; }; - 3E253770563F2D6771A20C7F7FDC3FFA /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = nanopb; - target = D2B5E7DCCBBFB32341D857D01211A1A3 /* nanopb */; - targetProxy = 54966192C2A24DFD90A2F81F7A9832C9 /* PBXContainerItemProxy */; - }; - 4673118F57DAE9113942441A46CEBDB6 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = "Google-Mobile-Ads-SDK"; - target = FEA3B3A570634836C0457F3D7CEF1699 /* Google-Mobile-Ads-SDK */; - targetProxy = D47854864D0B34D38172BEFF1EC23A84 /* PBXContainerItemProxy */; - }; 4C216DC596B0F250A309689B0E2D3EE6 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = FirebaseCore; target = 4402AFF83DBDC4DD07E198685FDC2DF2 /* FirebaseCore */; targetProxy = 5208F8DE77795387E086EA5EC171A0F3 /* PBXContainerItemProxy */; }; + 5F8B190BCFB67A0D735014DCAC3F7D94 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = GoogleUtilities; + target = 8D7F5D5DD528D21A72DC87ADA5B12E2D /* GoogleUtilities */; + targetProxy = 71E7B7777A04B903BCD198C61E2677D2 /* PBXContainerItemProxy */; + }; + 6138316F745E6CDBAAAD88EE794DFEF0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = FirebaseAnalytics; + target = C49E7A4D59E5C8BE8DE9FB1EFB150185 /* FirebaseAnalytics */; + targetProxy = 63D187B926B68FA83ECE584BC19FDB53 /* PBXContainerItemProxy */; + }; 6E1D22052F25092877EDD357CB0F5133 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = nanopb; target = D2B5E7DCCBBFB32341D857D01211A1A3 /* nanopb */; targetProxy = 241B2ADF661F95A8F00600EF4BD52524 /* PBXContainerItemProxy */; }; - 74FE54C0BD7C40AD0ACB81BB7D7173B6 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = GoogleAppMeasurement; - target = B53D977A951AFC38B21751B706C1DF83 /* GoogleAppMeasurement */; - targetProxy = 1CEB9D8AB30046A999AC84CFD532F9B0 /* PBXContainerItemProxy */; - }; 7C5400AEAABB9129079B8DE029282835 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = nanopb; @@ -2237,53 +2255,53 @@ target = 2BBF7206D7FAC92C82A042A99C4A98F8 /* PromisesObjC */; targetProxy = 96EC8928458564224711A029E1E9663B /* PBXContainerItemProxy */; }; - 8A7304A5367F2E2319F76E7AEAB0C74B /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = FirebaseCoreInternal; - target = 25E9E9A17BC3F670357D7385C521E48E /* FirebaseCoreInternal */; - targetProxy = D9C4EFB2A1183FEE0FEAFEB40115C80F /* PBXContainerItemProxy */; - }; - 8E34AAAE886C608639754C909144D945 /* PBXTargetDependency */ = { + 99F5CA9CC4D29171F4F00752CFBFE807 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - name = GoogleUtilities; - target = 8D7F5D5DD528D21A72DC87ADA5B12E2D /* GoogleUtilities */; - targetProxy = 845BF25808213B117C9B6FD34DDC6CA4 /* PBXContainerItemProxy */; + name = FirebaseInstallations; + target = 87803597EB3F20FC46472B85392EC4FD /* FirebaseInstallations */; + targetProxy = 16DE3BC1037378FF7B6C47BE4C9B7841 /* PBXContainerItemProxy */; }; - 9680596CF5BA72B769B32441159E55AF /* PBXTargetDependency */ = { + AEAEC99FFF18FE1D0F46E3080B728F39 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = GoogleUserMessagingPlatform; target = 458B188365A307B3C128ABF524D1A3E3 /* GoogleUserMessagingPlatform */; - targetProxy = 402DB2486B9F7702333974F81A9E978A /* PBXContainerItemProxy */; + targetProxy = 8AA5D1E35E68AF801665885BAB39BE7C /* PBXContainerItemProxy */; }; - 99F5CA9CC4D29171F4F00752CFBFE807 /* PBXTargetDependency */ = { + B165E99BD673C95B932D277C966FB371 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - name = FirebaseInstallations; - target = 87803597EB3F20FC46472B85392EC4FD /* FirebaseInstallations */; - targetProxy = 16DE3BC1037378FF7B6C47BE4C9B7841 /* PBXContainerItemProxy */; + name = "Google-Mobile-Ads-SDK"; + target = FEA3B3A570634836C0457F3D7CEF1699 /* Google-Mobile-Ads-SDK */; + targetProxy = AEB6C87FF73C1B2BA566E98248A72E33 /* PBXContainerItemProxy */; }; - 9F74628501F547383F470D0AC87CDB26 /* PBXTargetDependency */ = { + B8165BDA273840902291CAC6808E5E83 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = "Google-Mobile-Ads-SDK"; target = FEA3B3A570634836C0457F3D7CEF1699 /* Google-Mobile-Ads-SDK */; - targetProxy = 5603DBC4EDAF64F8820933C15721B91D /* PBXContainerItemProxy */; + targetProxy = DA86F512A924FA46A726C705DA08DE05 /* PBXContainerItemProxy */; }; - AEAEC99FFF18FE1D0F46E3080B728F39 /* PBXTargetDependency */ = { + B9170F56C0BBA343C0B267A2F9ACCB2B /* PBXTargetDependency */ = { isa = PBXTargetDependency; - name = GoogleUserMessagingPlatform; - target = 458B188365A307B3C128ABF524D1A3E3 /* GoogleUserMessagingPlatform */; - targetProxy = 8AA5D1E35E68AF801665885BAB39BE7C /* PBXContainerItemProxy */; + name = GoogleAppMeasurement; + target = B53D977A951AFC38B21751B706C1DF83 /* GoogleAppMeasurement */; + targetProxy = EACE8956249FE5EFE01B39078BD52390 /* PBXContainerItemProxy */; }; - DD7EDB2D4CB6B42D16418EA65DE31758 /* PBXTargetDependency */ = { + C1A0477563E5E143F1FEC1A5FB036525 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - name = GoogleUtilities; - target = 8D7F5D5DD528D21A72DC87ADA5B12E2D /* GoogleUtilities */; - targetProxy = B2C29E8875D89CC5D8BDE2C68F3CE32E /* PBXContainerItemProxy */; + name = FirebaseInstallations; + target = 87803597EB3F20FC46472B85392EC4FD /* FirebaseInstallations */; + targetProxy = 9EAC1EF71AF43778463B1343C855256D /* PBXContainerItemProxy */; }; - DE5DF4BA21E21D6C5ACFF964EF8E364C /* PBXTargetDependency */ = { + CC2BF516BF672A850116A70CB8BFA8FC /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = PromisesObjC; target = 2BBF7206D7FAC92C82A042A99C4A98F8 /* PromisesObjC */; - targetProxy = EF251258910026107F311DC58F9E09D4 /* PBXContainerItemProxy */; + targetProxy = E33E7D1DD8656567DE47B207FF6C0BC3 /* PBXContainerItemProxy */; + }; + DD7EDB2D4CB6B42D16418EA65DE31758 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = GoogleUtilities; + target = 8D7F5D5DD528D21A72DC87ADA5B12E2D /* GoogleUtilities */; + targetProxy = B2C29E8875D89CC5D8BDE2C68F3CE32E /* PBXContainerItemProxy */; }; E2DCFAE78BCAA36E9D8B5649B9D0C62B /* PBXTargetDependency */ = { isa = PBXTargetDependency; @@ -2309,12 +2327,36 @@ target = 8D7F5D5DD528D21A72DC87ADA5B12E2D /* GoogleUtilities */; targetProxy = C230DD0778618FC5FAF689A375C6E712 /* PBXContainerItemProxy */; }; + E85F77C5D7837C7A9AF62048FE7D6EDF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = shared; + target = 8777C9F6889E59EFFD631D80AEE9048B /* shared */; + targetProxy = CC05A166FCD70558DB4AA08092F92B31 /* PBXContainerItemProxy */; + }; + EE39E0CEDFD595A5BB01985767168A73 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = nanopb; + target = D2B5E7DCCBBFB32341D857D01211A1A3 /* nanopb */; + targetProxy = 11B81494225C46E1DCFD4CA6193C328C /* PBXContainerItemProxy */; + }; + EE7DF03127408EDF953C79B687007A76 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = FirebaseCore; + target = 4402AFF83DBDC4DD07E198685FDC2DF2 /* FirebaseCore */; + targetProxy = A554B405FD46BE64C95886F04C91FE15 /* PBXContainerItemProxy */; + }; EF9DC529F551A4C4E432EBB487123829 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = GoogleUtilities; target = 8D7F5D5DD528D21A72DC87ADA5B12E2D /* GoogleUtilities */; targetProxy = 2917D7777B0E3ACF4D02303DF668A03E /* PBXContainerItemProxy */; }; + F1A789E242781A840B4D50606B4F32A3 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = FirebaseCoreInternal; + target = 25E9E9A17BC3F670357D7385C521E48E /* FirebaseCoreInternal */; + targetProxy = C9EC65EBD32F6C5F302F5B7DFF889384 /* PBXContainerItemProxy */; + }; FCF26D3CFA3455940AC5DFF4A6D42B67 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = GoogleAppMeasurement; @@ -2324,6 +2366,25 @@ /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ + 06872E91C3062D7B031414A0253BC20E /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D830325D4FB0D56847E40BBD616A7635 /* shared.release.xcconfig */; + buildSettings = { + "ARCHS[sdk=iphonesimulator*]" = x86_64; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ENABLE_OBJC_WEAK = NO; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; 08CBDB37EA2D4C221225ED87ADDF8F92 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 588ACDBC08ED6EE102BE0AF967952E96 /* nanopb.release.xcconfig */; @@ -2448,24 +2509,6 @@ }; name = Debug; }; - 2C613FACD9CE2E338A7C85145A833A83 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 95B09EA82E7AF9ACCCCAF3E55C859116 /* shared.debug.xcconfig */; - buildSettings = { - "ARCHS[sdk=iphonesimulator*]" = x86_64; - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CLANG_ENABLE_OBJC_WEAK = NO; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; 32FDFC0AF6003B12FCFA99FB63C56828 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 05E8E0C3662B0AC4859DAF12D00175F6 /* FirebaseInstallations.debug.xcconfig */; @@ -2555,6 +2598,24 @@ }; name = Release; }; + 6099AC790CDCCAB9E52DC6054AC2531A /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = DC694ABF344CE2D9D645124A32F45D91 /* shared.debug.xcconfig */; + buildSettings = { + "ARCHS[sdk=iphonesimulator*]" = x86_64; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ENABLE_OBJC_WEAK = NO; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; 65476D2F91F7968D5FB641C89D24BA0F /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 179BE3E7E71E0605336E1598EDC031CD /* GoogleUtilities.debug.xcconfig */; @@ -2589,7 +2650,7 @@ }; name = Debug; }; - 740EBBA651B25058C7DE6FBEA7B9F6AB /* Release */ = { + 6716B4A3BEE6C9DE0E1B3DAA6F21AA81 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 9FECCB5AFF87D121F15BA01E683C7932 /* Pods-iosApp.release.xcconfig */; buildSettings = { @@ -2918,7 +2979,7 @@ }; name = Release; }; - B8C47CB01DCAFD87F27605A1D29E13B2 /* Debug */ = { + B96E36F99489CCF22CEAB3CA1E5EABDE /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 3F64BADB16C311E9D09DAD0116F684F4 /* Pods-iosApp.debug.xcconfig */; buildSettings = { @@ -2956,25 +3017,6 @@ }; name = Debug; }; - BE6B1260832E35F0699CB383AFE412DD /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 35548E3BD8DA30925E8FE97E67B84868 /* shared.release.xcconfig */; - buildSettings = { - "ARCHS[sdk=iphonesimulator*]" = x86_64; - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CLANG_ENABLE_OBJC_WEAK = NO; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; BFB862BCC7493B0AEEEBFA7E64CAB4D1 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 19ABA9BF6F0E4DDA991DE4D3793BC610 /* GoogleUtilities.release.xcconfig */; @@ -3155,15 +3197,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 3442D90E7F5067221336A7DB9B972C30 /* Build configuration list for PBXAggregateTarget "shared" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 2C613FACD9CE2E338A7C85145A833A83 /* Debug */, - BE6B1260832E35F0699CB383AFE412DD /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -3218,11 +3251,20 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - C1E531B18FF042262D4D2AAE781640F2 /* Build configuration list for PBXNativeTarget "Pods-iosApp" */ = { + D32B7ABA4A4D006DB97DE2087CBEB65D /* Build configuration list for PBXAggregateTarget "shared" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6099AC790CDCCAB9E52DC6054AC2531A /* Debug */, + 06872E91C3062D7B031414A0253BC20E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D85BF9F6FF41C893F67914EFCFFD9A4C /* Build configuration list for PBXNativeTarget "Pods-iosApp" */ = { isa = XCConfigurationList; buildConfigurations = ( - B8C47CB01DCAFD87F27605A1D29E13B2 /* Debug */, - 740EBBA651B25058C7DE6FBEA7B9F6AB /* Release */, + B96E36F99489CCF22CEAB3CA1E5EABDE /* Debug */, + 6716B4A3BEE6C9DE0E1B3DAA6F21AA81 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; diff --git a/iosApp/Pods/Target Support Files/Pods-iosApp/Pods-iosApp-Info.plist b/iosApp/Pods/Target Support Files/Pods-iosApp/Pods-iosApp-Info.plist index 19cf209d..2243fe6e 100644 --- a/iosApp/Pods/Target Support Files/Pods-iosApp/Pods-iosApp-Info.plist +++ b/iosApp/Pods/Target Support Files/Pods-iosApp/Pods-iosApp-Info.plist @@ -3,7 +3,7 @@ CFBundleDevelopmentRegion - ${PODS_DEVELOPMENT_LANGUAGE} + en CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier diff --git a/iosApp/Pods/Target Support Files/Pods-iosApp/Pods-iosApp.debug.xcconfig b/iosApp/Pods/Target Support Files/Pods-iosApp/Pods-iosApp.debug.xcconfig index 3e004202..0b8b5eb7 100644 --- a/iosApp/Pods/Target Support Files/Pods-iosApp/Pods-iosApp.debug.xcconfig +++ b/iosApp/Pods/Target Support Files/Pods-iosApp/Pods-iosApp.debug.xcconfig @@ -1,10 +1,9 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO -FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreInternal" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstallations" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities" "${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb" "${PODS_ROOT}/../../shared/build/cocoapods/framework" "${PODS_ROOT}/FirebaseAnalytics/Frameworks" "${PODS_ROOT}/Google-Mobile-Ads-SDK/Frameworks/GoogleMobileAdsFramework" "${PODS_ROOT}/GoogleAppMeasurement/Frameworks" "${PODS_ROOT}/GoogleUserMessagingPlatform/Frameworks/Release" "${PODS_XCFRAMEWORKS_BUILD_DIR}/FirebaseAnalytics/AdIdSupport" "${PODS_XCFRAMEWORKS_BUILD_DIR}/Google-Mobile-Ads-SDK" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleAppMeasurement/AdIdSupport" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleAppMeasurement/WithoutAdIdSupport" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleUserMessagingPlatform" +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreInternal" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstallations" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities" "${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb" "${PODS_ROOT}/../../shared/build/cocoapods/framework" "${PODS_ROOT}/FirebaseAnalytics/Frameworks" "${PODS_ROOT}/Google-Mobile-Ads-SDK/Frameworks/GoogleMobileAdsFramework" "${PODS_ROOT}/GoogleAppMeasurement/Frameworks" "${PODS_ROOT}/GoogleUserMessagingPlatform/Frameworks/Release" "${PODS_XCFRAMEWORKS_BUILD_DIR}/FirebaseAnalytics" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleAppMeasurement" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleAppMeasurementIdentitySupport" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleMobileAds" "${PODS_XCFRAMEWORKS_BUILD_DIR}/UserMessagingPlatform" GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 $(inherited) PB_FIELD_32BIT=1 PB_NO_PACKED_STRUCTS=1 PB_ENABLE_MALLOC=1 HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore/FirebaseCore.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreInternal/FirebaseCoreInternal.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstallations/FirebaseInstallations.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities/GoogleUtilities.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC/FBLPromises.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb/nanopb.framework/Headers" -LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks' -LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift +LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' OTHER_LDFLAGS = $(inherited) -ObjC -l"c++" -l"sqlite3" -l"z" -framework "AVFoundation" -framework "AudioToolbox" -framework "CFNetwork" -framework "CoreGraphics" -framework "CoreMedia" -framework "CoreTelephony" -framework "CoreVideo" -framework "FBLPromises" -framework "FirebaseAnalytics" -framework "FirebaseCore" -framework "FirebaseCoreInternal" -framework "FirebaseInstallations" -framework "Foundation" -framework "GoogleAppMeasurement" -framework "GoogleAppMeasurementIdentitySupport" -framework "GoogleMobileAds" -framework "GoogleUtilities" -framework "MediaPlayer" -framework "MessageUI" -framework "MobileCoreServices" -framework "QuartzCore" -framework "Security" -framework "StoreKit" -framework "SystemConfiguration" -framework "UIKit" -framework "UserMessagingPlatform" -framework "WebKit" -framework "nanopb" -framework "shared" -weak_framework "AdSupport" -weak_framework "JavaScriptCore" -weak_framework "SafariServices" -weak_framework "WebKit" OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS PODS_BUILD_DIR = ${BUILD_DIR} diff --git a/iosApp/Pods/Target Support Files/Pods-iosApp/Pods-iosApp.release.xcconfig b/iosApp/Pods/Target Support Files/Pods-iosApp/Pods-iosApp.release.xcconfig index 3e004202..0b8b5eb7 100644 --- a/iosApp/Pods/Target Support Files/Pods-iosApp/Pods-iosApp.release.xcconfig +++ b/iosApp/Pods/Target Support Files/Pods-iosApp/Pods-iosApp.release.xcconfig @@ -1,10 +1,9 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO -FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreInternal" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstallations" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities" "${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb" "${PODS_ROOT}/../../shared/build/cocoapods/framework" "${PODS_ROOT}/FirebaseAnalytics/Frameworks" "${PODS_ROOT}/Google-Mobile-Ads-SDK/Frameworks/GoogleMobileAdsFramework" "${PODS_ROOT}/GoogleAppMeasurement/Frameworks" "${PODS_ROOT}/GoogleUserMessagingPlatform/Frameworks/Release" "${PODS_XCFRAMEWORKS_BUILD_DIR}/FirebaseAnalytics/AdIdSupport" "${PODS_XCFRAMEWORKS_BUILD_DIR}/Google-Mobile-Ads-SDK" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleAppMeasurement/AdIdSupport" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleAppMeasurement/WithoutAdIdSupport" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleUserMessagingPlatform" +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreInternal" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstallations" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities" "${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb" "${PODS_ROOT}/../../shared/build/cocoapods/framework" "${PODS_ROOT}/FirebaseAnalytics/Frameworks" "${PODS_ROOT}/Google-Mobile-Ads-SDK/Frameworks/GoogleMobileAdsFramework" "${PODS_ROOT}/GoogleAppMeasurement/Frameworks" "${PODS_ROOT}/GoogleUserMessagingPlatform/Frameworks/Release" "${PODS_XCFRAMEWORKS_BUILD_DIR}/FirebaseAnalytics" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleAppMeasurement" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleAppMeasurementIdentitySupport" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleMobileAds" "${PODS_XCFRAMEWORKS_BUILD_DIR}/UserMessagingPlatform" GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 $(inherited) PB_FIELD_32BIT=1 PB_NO_PACKED_STRUCTS=1 PB_ENABLE_MALLOC=1 HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore/FirebaseCore.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreInternal/FirebaseCoreInternal.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstallations/FirebaseInstallations.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities/GoogleUtilities.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC/FBLPromises.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb/nanopb.framework/Headers" -LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks' -LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift +LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' OTHER_LDFLAGS = $(inherited) -ObjC -l"c++" -l"sqlite3" -l"z" -framework "AVFoundation" -framework "AudioToolbox" -framework "CFNetwork" -framework "CoreGraphics" -framework "CoreMedia" -framework "CoreTelephony" -framework "CoreVideo" -framework "FBLPromises" -framework "FirebaseAnalytics" -framework "FirebaseCore" -framework "FirebaseCoreInternal" -framework "FirebaseInstallations" -framework "Foundation" -framework "GoogleAppMeasurement" -framework "GoogleAppMeasurementIdentitySupport" -framework "GoogleMobileAds" -framework "GoogleUtilities" -framework "MediaPlayer" -framework "MessageUI" -framework "MobileCoreServices" -framework "QuartzCore" -framework "Security" -framework "StoreKit" -framework "SystemConfiguration" -framework "UIKit" -framework "UserMessagingPlatform" -framework "WebKit" -framework "nanopb" -framework "shared" -weak_framework "AdSupport" -weak_framework "JavaScriptCore" -weak_framework "SafariServices" -weak_framework "WebKit" OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS PODS_BUILD_DIR = ${BUILD_DIR} diff --git a/iosApp/Pods/Target Support Files/shared/shared.debug.xcconfig b/iosApp/Pods/Target Support Files/shared/shared.debug.xcconfig index 0799ead1..bcaa85ba 100644 --- a/iosApp/Pods/Target Support Files/shared/shared.debug.xcconfig +++ b/iosApp/Pods/Target Support Files/shared/shared.debug.xcconfig @@ -1,12 +1,11 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/shared -FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities" "${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb" "${PODS_ROOT}/../../shared/build/cocoapods/framework" "${PODS_ROOT}/Google-Mobile-Ads-SDK/Frameworks/GoogleMobileAdsFramework" "${PODS_ROOT}/GoogleAppMeasurement/Frameworks" "${PODS_ROOT}/GoogleUserMessagingPlatform/Frameworks/Release" "${PODS_XCFRAMEWORKS_BUILD_DIR}/Google-Mobile-Ads-SDK" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleAppMeasurement/AdIdSupport" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleAppMeasurement/WithoutAdIdSupport" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleUserMessagingPlatform" +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities" "${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb" "${PODS_ROOT}/../../shared/build/cocoapods/framework" "${PODS_ROOT}/Google-Mobile-Ads-SDK/Frameworks/GoogleMobileAdsFramework" "${PODS_ROOT}/GoogleAppMeasurement/Frameworks" "${PODS_ROOT}/GoogleUserMessagingPlatform/Frameworks/Release" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleAppMeasurement" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleAppMeasurementIdentitySupport" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleMobileAds" "${PODS_XCFRAMEWORKS_BUILD_DIR}/UserMessagingPlatform" GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 KOTLIN_PROJECT_PATH = :shared OTHER_LDFLAGS = $(inherited) -l"c++" -framework "AVFoundation" -framework "AudioToolbox" -framework "CFNetwork" -framework "CoreGraphics" -framework "CoreMedia" -framework "CoreTelephony" -framework "CoreVideo" -framework "MediaPlayer" -framework "MessageUI" -framework "MobileCoreServices" -framework "QuartzCore" -framework "Security" -framework "StoreKit" -framework "SystemConfiguration" -framework "WebKit" -weak_framework "AdSupport" -weak_framework "JavaScriptCore" -weak_framework "SafariServices" -weak_framework "WebKit" PODS_BUILD_DIR = ${BUILD_DIR} PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} PODS_ROOT = ${SRCROOT} PODS_TARGET_SRCROOT = ${PODS_ROOT}/../../shared PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates diff --git a/iosApp/Pods/Target Support Files/shared/shared.release.xcconfig b/iosApp/Pods/Target Support Files/shared/shared.release.xcconfig index 0799ead1..bcaa85ba 100644 --- a/iosApp/Pods/Target Support Files/shared/shared.release.xcconfig +++ b/iosApp/Pods/Target Support Files/shared/shared.release.xcconfig @@ -1,12 +1,11 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/shared -FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities" "${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb" "${PODS_ROOT}/../../shared/build/cocoapods/framework" "${PODS_ROOT}/Google-Mobile-Ads-SDK/Frameworks/GoogleMobileAdsFramework" "${PODS_ROOT}/GoogleAppMeasurement/Frameworks" "${PODS_ROOT}/GoogleUserMessagingPlatform/Frameworks/Release" "${PODS_XCFRAMEWORKS_BUILD_DIR}/Google-Mobile-Ads-SDK" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleAppMeasurement/AdIdSupport" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleAppMeasurement/WithoutAdIdSupport" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleUserMessagingPlatform" +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities" "${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb" "${PODS_ROOT}/../../shared/build/cocoapods/framework" "${PODS_ROOT}/Google-Mobile-Ads-SDK/Frameworks/GoogleMobileAdsFramework" "${PODS_ROOT}/GoogleAppMeasurement/Frameworks" "${PODS_ROOT}/GoogleUserMessagingPlatform/Frameworks/Release" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleAppMeasurement" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleAppMeasurementIdentitySupport" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleMobileAds" "${PODS_XCFRAMEWORKS_BUILD_DIR}/UserMessagingPlatform" GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 KOTLIN_PROJECT_PATH = :shared OTHER_LDFLAGS = $(inherited) -l"c++" -framework "AVFoundation" -framework "AudioToolbox" -framework "CFNetwork" -framework "CoreGraphics" -framework "CoreMedia" -framework "CoreTelephony" -framework "CoreVideo" -framework "MediaPlayer" -framework "MessageUI" -framework "MobileCoreServices" -framework "QuartzCore" -framework "Security" -framework "StoreKit" -framework "SystemConfiguration" -framework "WebKit" -weak_framework "AdSupport" -weak_framework "JavaScriptCore" -weak_framework "SafariServices" -weak_framework "WebKit" PODS_BUILD_DIR = ${BUILD_DIR} PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE} PODS_ROOT = ${SRCROOT} PODS_TARGET_SRCROOT = ${PODS_ROOT}/../../shared PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates diff --git a/iosApp/fastlane/Fastfile b/iosApp/fastlane/Fastfile index 5dc0fb42..a58727d4 100644 --- a/iosApp/fastlane/Fastfile +++ b/iosApp/fastlane/Fastfile @@ -32,4 +32,11 @@ platform :ios do build_app(workspace: "iosApp.xcworkspace", scheme: "iosApp") upload_to_app_store end + + desc "Build IPA" + lane :build do + match(type: "appstore") + cocoapods + build_app(workspace: "iosApp.xcworkspace", scheme: "iosApp") + end end diff --git a/iosApp/fastlane/README.md b/iosApp/fastlane/README.md index 87ffeb24..6b65d840 100644 --- a/iosApp/fastlane/README.md +++ b/iosApp/fastlane/README.md @@ -23,6 +23,14 @@ For _fastlane_ installation instructions, see [Installing _fastlane_](https://do Push a new release build to the App Store +### ios build + +```sh +[bundle exec] fastlane ios build +``` + +Build IPA + ---- This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run. diff --git a/iosApp/fastlane/report.xml b/iosApp/fastlane/report.xml index 2fffaaec..2696987b 100644 --- a/iosApp/fastlane/report.xml +++ b/iosApp/fastlane/report.xml @@ -5,29 +5,24 @@ - + - + - + - + - - - - - - + From 2f7e8c50b5460ef9f6f45ab7385caaf162a8cfa5 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Wed, 10 May 2023 18:10:51 +0200 Subject: [PATCH 54/82] fixes --- iosApp/fastlane/Fastfile | 4 ++++ iosApp/fastlane/report.xml | 13 ++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/iosApp/fastlane/Fastfile b/iosApp/fastlane/Fastfile index a58727d4..eed8be5f 100644 --- a/iosApp/fastlane/Fastfile +++ b/iosApp/fastlane/Fastfile @@ -28,6 +28,7 @@ platform :ios do match(type: "appstore") cocoapods + sh("sed -i '' 's/source=\"$(readlink \"${source}\")\"/source=\"$(readlink -f \"${source}\")\"/g' \"Pods/Target Support Files/Pods-iosApp/Pods-iosApp-frameworks.sh\"") increment_build_number(xcodeproj: "iosApp.xcodeproj") build_app(workspace: "iosApp.xcworkspace", scheme: "iosApp") upload_to_app_store @@ -37,6 +38,9 @@ platform :ios do lane :build do match(type: "appstore") cocoapods + sh("sed -i '' 's/source=\"$(readlink \"${source}\")\"/source=\"$(readlink -f \"${source}\")\"/g' \"../Pods/Target Support Files/Pods-iosApp/Pods-iosApp-frameworks.sh\"") + build_app(workspace: "iosApp.xcworkspace", scheme: "iosApp") end end + diff --git a/iosApp/fastlane/report.xml b/iosApp/fastlane/report.xml index 2696987b..297ab038 100644 --- a/iosApp/fastlane/report.xml +++ b/iosApp/fastlane/report.xml @@ -5,24 +5,27 @@ - + - + - + - + - + + + + From fab6d2b24eeb07058b2bd2cf35bbe9615693ff5c Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Wed, 10 May 2023 18:11:40 +0200 Subject: [PATCH 55/82] oops --- iosApp/fastlane/Fastfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iosApp/fastlane/Fastfile b/iosApp/fastlane/Fastfile index eed8be5f..291aa9cb 100644 --- a/iosApp/fastlane/Fastfile +++ b/iosApp/fastlane/Fastfile @@ -28,7 +28,7 @@ platform :ios do match(type: "appstore") cocoapods - sh("sed -i '' 's/source=\"$(readlink \"${source}\")\"/source=\"$(readlink -f \"${source}\")\"/g' \"Pods/Target Support Files/Pods-iosApp/Pods-iosApp-frameworks.sh\"") + sh("sed -i '' 's/source=\"$(readlink \"${source}\")\"/source=\"$(readlink -f \"${source}\")\"/g' \"../Pods/Target Support Files/Pods-iosApp/Pods-iosApp-frameworks.sh\"") increment_build_number(xcodeproj: "iosApp.xcodeproj") build_app(workspace: "iosApp.xcworkspace", scheme: "iosApp") upload_to_app_store From f7d2eac9bf343afcb6cb585b2b319a6e55bc1b5b Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Wed, 10 May 2023 19:40:07 +0200 Subject: [PATCH 56/82] Create test.yml --- .github/workflows/test.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..3fec44ae --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,24 @@ +name: iOS test + +on: + workflow_dispatch: + +jobs: + build: + name: Test + runs-on: macos-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: set up JDK 11 + uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'temurin' + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - run: ./gradlew tasks From cc01c20b65976ae8ecb4f954ba33dadb7101496f Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Wed, 10 May 2023 19:47:21 +0200 Subject: [PATCH 57/82] Update ios.yml --- .github/workflows/ios.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml index ce900814..7cb28ce2 100644 --- a/.github/workflows/ios.yml +++ b/.github/workflows/ios.yml @@ -20,6 +20,9 @@ jobs: - name: Grant execute permission for gradlew run: chmod +x gradlew + + - name: Build + run: ./gradlew linkPodReleaseFrameworkIosArm64 - name: Setup Ruby uses: ruby/setup-ruby@v1 From 2d5a0060acd0c0f36a4a26f193c17402cbc3bf19 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Thu, 11 May 2023 15:14:06 +0200 Subject: [PATCH 58/82] fix rounding bugs on ios --- .../iosMain/kotlin/nl/tiebe/otarium/ui/login/LoginScreen.kt | 4 ++-- .../iosMain/kotlin/nl/tiebe/otarium/utils/ui/ResourcesIOS.kt | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/shared/src/iosMain/kotlin/nl/tiebe/otarium/ui/login/LoginScreen.kt b/shared/src/iosMain/kotlin/nl/tiebe/otarium/ui/login/LoginScreen.kt index 40939cdf..8a2effdb 100644 --- a/shared/src/iosMain/kotlin/nl/tiebe/otarium/ui/login/LoginScreen.kt +++ b/shared/src/iosMain/kotlin/nl/tiebe/otarium/ui/login/LoginScreen.kt @@ -1,3 +1,5 @@ +@file:Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE", "CONFLICTING_OVERLOADS") + package nl.tiebe.otarium.ui.login import androidx.compose.foundation.layout.fillMaxSize @@ -24,7 +26,6 @@ internal actual fun LoginScreen(component: LoginComponent) { preferences.javaScriptCanOpenWindowsAutomatically = true config.setURLSchemeHandler(object : NSObject(), WKURLSchemeHandlerProtocol { - @Suppress("CONFLICTING_OVERLOADS") override fun webView( webView: WKWebView, startURLSchemeTask: WKURLSchemeTaskProtocol @@ -34,7 +35,6 @@ internal actual fun LoginScreen(component: LoginComponent) { } } - @Suppress("CONFLICTING_OVERLOADS") override fun webView( webView: WKWebView, stopURLSchemeTask: WKURLSchemeTaskProtocol diff --git a/shared/src/iosMain/kotlin/nl/tiebe/otarium/utils/ui/ResourcesIOS.kt b/shared/src/iosMain/kotlin/nl/tiebe/otarium/utils/ui/ResourcesIOS.kt index f10dc845..40bbff52 100644 --- a/shared/src/iosMain/kotlin/nl/tiebe/otarium/utils/ui/ResourcesIOS.kt +++ b/shared/src/iosMain/kotlin/nl/tiebe/otarium/utils/ui/ResourcesIOS.kt @@ -7,6 +7,7 @@ import dev.icerock.moko.resources.desc.StringDesc import platform.Foundation.NSNumber import platform.Foundation.NSNumberFormatter import platform.Foundation.NSNumberFormatterDecimalStyle +import platform.Foundation.NSNumberFormatterRoundHalfUp import platform.UIKit.UIViewController object IOS { @@ -21,6 +22,8 @@ actual fun Float.format(decimals: Int): String { val formatter = NSNumberFormatter() formatter.minimumFractionDigits = decimals.toULong() formatter.maximumFractionDigits = decimals.toULong() + + formatter.roundingMode = NSNumberFormatterRoundHalfUp formatter.numberStyle = NSNumberFormatterDecimalStyle return formatter.stringFromNumber(NSNumber(this))!! } From 1efd9be6eaa837a00969d7223b9a87b555cee20c Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Fri, 12 May 2023 17:18:45 +0200 Subject: [PATCH 59/82] fixes --- buildSrc/src/main/kotlin/Dependencies.kt | 2 + icons/content_save.svg | 1 + icons/palette.svg | 1 + shared/build.gradle.kts | 2 +- .../kotlin/nl/tiebe/otarium/Main.kt | 9 +++ .../nl/tiebe/otarium/ui/home/HomeComponent.kt | 30 ++++----- .../otarium/ui/home/debug/DebugScreen.kt | 24 +++++--- .../assignments/assignment/VersionCards.kt | 10 +-- .../otarium/ui/home/grades/GradesComponent.kt | 2 + .../otarium/ui/home/messages/MessageItem.kt | 26 ++++---- .../ui/home/messages/message/MessageHeader.kt | 10 +-- .../home/settings/items/ads/AdsChildScreen.kt | 6 +- .../settings/items/main/MainChildScreen.kt | 20 +++--- .../home/settings/items/ui/UIChildScreen.kt | 10 +-- .../items/ui/colors/ColorChildScreen.kt | 40 +++++++----- .../home/settings/utils/SettingColorPicker.kt | 4 +- .../home/settings/utils/SettingRowButton.kt | 8 ++- .../home/settings/utils/SettingRowToggle.kt | 9 ++- .../ui/home/settings/utils/SettingSlider.kt | 1 - .../{icons/__Icons.kt => __OtariumIcons.kt} | 9 +-- .../tiebe/otarium/utils/icons/__Bottombar.kt | 22 ------- .../nl/tiebe/otarium/utils/icons/__Email.kt | 21 ------- .../{icons => otariumicons}/Advertisements.kt | 5 +- .../AdvertisementsOff.kt | 6 +- .../{icons => otariumicons}/BugOutline.kt | 6 +- .../otarium/utils/otariumicons/ContentSave.kt | 49 +++++++++++++++ .../utils/{icons => otariumicons}/Folder.kt | 5 +- .../otarium/utils/otariumicons/Palette.kt | 61 +++++++++++++++++++ .../otarium/utils/otariumicons/__Bottombar.kt | 32 ++++++++++ .../otarium/utils/otariumicons/__Email.kt | 26 ++++++++ .../bottombar/BookOpenFilled.kt | 4 +- .../bottombar/BookOpenOutline.kt | 4 +- .../bottombar/Box10Filled.kt | 4 +- .../bottombar/Box10Outline.kt | 4 +- .../bottombar/CalendarTodayFilled.kt | 4 +- .../bottombar/CalendarTodayOutline.kt | 4 +- .../bottombar/CogFilled.kt | 4 +- .../bottombar/CogOutline.kt | 4 +- .../bottombar/EmailFilled.kt | 4 +- .../bottombar/EmailOutline.kt | 4 +- .../email/Attachment.kt | 4 +- .../email/AttachmentOff.kt | 4 +- .../email/EmailAlert.kt | 4 +- .../email/EmailAlertOpen.kt | 4 +- .../email/EmailOpen.kt | 4 +- .../commonMain/resources/MR/base/strings.xml | 9 +++ 46 files changed, 353 insertions(+), 173 deletions(-) create mode 100644 icons/content_save.svg create mode 100644 icons/palette.svg rename shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/{icons/__Icons.kt => __OtariumIcons.kt} (62%) delete mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/__Bottombar.kt delete mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/__Email.kt rename shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/{icons => otariumicons}/Advertisements.kt (95%) rename shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/{icons => otariumicons}/AdvertisementsOff.kt (95%) rename shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/{icons => otariumicons}/BugOutline.kt (96%) create mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/ContentSave.kt rename shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/{icons => otariumicons}/Folder.kt (92%) create mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/Palette.kt create mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/__Bottombar.kt create mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/__Email.kt rename shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/{icons => otariumicons}/bottombar/BookOpenFilled.kt (95%) rename shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/{icons => otariumicons}/bottombar/BookOpenOutline.kt (95%) rename shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/{icons => otariumicons}/bottombar/Box10Filled.kt (95%) rename shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/{icons => otariumicons}/bottombar/Box10Outline.kt (95%) rename shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/{icons => otariumicons}/bottombar/CalendarTodayFilled.kt (95%) rename shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/{icons => otariumicons}/bottombar/CalendarTodayOutline.kt (95%) rename shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/{icons => otariumicons}/bottombar/CogFilled.kt (96%) rename shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/{icons => otariumicons}/bottombar/CogOutline.kt (97%) rename shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/{icons => otariumicons}/bottombar/EmailFilled.kt (94%) rename shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/{icons => otariumicons}/bottombar/EmailOutline.kt (94%) rename shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/{icons => otariumicons}/email/Attachment.kt (95%) rename shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/{icons => otariumicons}/email/AttachmentOff.kt (96%) rename shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/{icons => otariumicons}/email/EmailAlert.kt (95%) rename shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/{icons => otariumicons}/email/EmailAlertOpen.kt (95%) rename shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/{icons => otariumicons}/email/EmailOpen.kt (94%) diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt index 05460c13..6aadfc36 100755 --- a/buildSrc/src/main/kotlin/Dependencies.kt +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -24,6 +24,7 @@ object Version { const val guava_coroutines = "1.7.0-RC" const val complete_kotlin = "1.1.0" + const val colormath = "3.2.0" } object Compose { @@ -98,6 +99,7 @@ const val admob = "com.google.android.gms:play-services-ads:${Version.admob}" const val magisterAPI = "dev.tiebe:magisterapi:${Version.magister}" const val completeKotlin = "com.louiscad.complete-kotlin" +const val colorMath = "com.github.ajalt.colormath:colormath:${Version.colormath}" object Guava { const val core = "com.google.guava:guava:${Version.guava}" diff --git a/icons/content_save.svg b/icons/content_save.svg new file mode 100644 index 00000000..1b7d6513 --- /dev/null +++ b/icons/content_save.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/palette.svg b/icons/palette.svg new file mode 100644 index 00000000..40e9c0ad --- /dev/null +++ b/icons/palette.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts index 4449470f..5e9be125 100644 --- a/shared/build.gradle.kts +++ b/shared/build.gradle.kts @@ -79,7 +79,7 @@ kotlin { implementation(Decompose.compose) implementation(magisterAPI) - implementation("com.github.ajalt.colormath:colormath:3.2.0") + implementation(colorMath) } } val androidMain by getting { diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/Main.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/Main.kt index ab121b6c..932e20be 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/Main.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/Main.kt @@ -24,6 +24,15 @@ val settings: Settings = Settings() lateinit var darkModeState: MutableState val safeAreaState = mutableStateOf(PaddingValues()) +//todo: back gestures ios +//todo: fix colorrssss +//todo: handmatig cijfers invoeren +//todo: soepelheid swipen overal +//todo: add settings item to show lesuitval +//todo: make important emails red +//todo: add statistics tab in grades +//todo: voldoendegrens instellen in instellingen + fun setup() { val oldVersion = settings.getInt("version", 1000) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/HomeComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/HomeComponent.kt index 5b9f1d4b..0377c703 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/HomeComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/HomeComponent.kt @@ -20,9 +20,9 @@ import nl.tiebe.otarium.ui.home.messages.DefaultMessagesComponent import nl.tiebe.otarium.ui.home.settings.DefaultSettingsComponent import nl.tiebe.otarium.ui.home.timetable.DefaultTimetableComponent import nl.tiebe.otarium.ui.root.RootComponent -import nl.tiebe.otarium.utils.icons.Bottombar -import nl.tiebe.otarium.utils.icons.Icons -import nl.tiebe.otarium.utils.icons.bottombar.* +import nl.tiebe.otarium.utils.OtariumIcons +import nl.tiebe.otarium.utils.otariumicons.Bottombar +import nl.tiebe.otarium.utils.otariumicons.bottombar.* interface HomeComponent { val dialog: Value> @@ -33,38 +33,38 @@ interface HomeComponent { sealed class MenuItem(val resourceId: StringResource, val icon: @Composable () -> Unit, val iconSelected: @Composable () -> Unit): Parcelable { object Timetable: MenuItem( MR.strings.agendaItem, - { Icon(Icons.Bottombar.CalendarTodayOutline, "Timetable", tint = MaterialTheme.colorScheme.onPrimary) }, - { Icon(Icons.Bottombar.CalendarTodayFilled, "Timetable", tint = MaterialTheme.colorScheme.onSecondaryContainer) }, + { Icon(OtariumIcons.Bottombar.CalendarTodayOutline, "Timetable", tint = MaterialTheme.colorScheme.onPrimary) }, + { Icon(OtariumIcons.Bottombar.CalendarTodayFilled, "Timetable", tint = MaterialTheme.colorScheme.onSecondaryContainer) }, ) object Grades: MenuItem( MR.strings.gradesItem, - { Icon(Icons.Bottombar.Box10Outline, "Grades", tint = MaterialTheme.colorScheme.onPrimary) }, - { Icon(Icons.Bottombar.Box10Filled, "Grades", tint = MaterialTheme.colorScheme.onSecondaryContainer) }, + { Icon(OtariumIcons.Bottombar.Box10Outline, "Grades", tint = MaterialTheme.colorScheme.onPrimary) }, + { Icon(OtariumIcons.Bottombar.Box10Filled, "Grades", tint = MaterialTheme.colorScheme.onSecondaryContainer) }, ) object Messages: MenuItem( MR.strings.messagesItem, - { Icon(Icons.Bottombar.EmailOutline, "Messages", tint = MaterialTheme.colorScheme.onPrimary) }, - { Icon(Icons.Bottombar.EmailFilled, "Messages", tint = MaterialTheme.colorScheme.onSecondaryContainer) }, + { Icon(OtariumIcons.Bottombar.EmailOutline, "Messages", tint = MaterialTheme.colorScheme.onPrimary) }, + { Icon(OtariumIcons.Bottombar.EmailFilled, "Messages", tint = MaterialTheme.colorScheme.onSecondaryContainer) }, ) object ELO: MenuItem( MR.strings.eloItem, - { Icon(Icons.Bottombar.BookOpenOutline, "ELO", tint = MaterialTheme.colorScheme.onPrimary) }, - { Icon(Icons.Bottombar.BookOpenFilled, "ELO", tint = MaterialTheme.colorScheme.onSecondaryContainer) }, + { Icon(OtariumIcons.Bottombar.BookOpenOutline, "ELO", tint = MaterialTheme.colorScheme.onPrimary) }, + { Icon(OtariumIcons.Bottombar.BookOpenFilled, "ELO", tint = MaterialTheme.colorScheme.onSecondaryContainer) }, ) object Settings: MenuItem( MR.strings.settingsItem, - { Icon(Icons.Bottombar.CogOutline, "Settings", tint = MaterialTheme.colorScheme.onPrimary) }, - { Icon(Icons.Bottombar.CogFilled, "Settings", tint = MaterialTheme.colorScheme.onSecondaryContainer) }, + { Icon(OtariumIcons.Bottombar.CogOutline, "Settings", tint = MaterialTheme.colorScheme.onPrimary) }, + { Icon(OtariumIcons.Bottombar.CogFilled, "Settings", tint = MaterialTheme.colorScheme.onSecondaryContainer) }, ) object Debug: MenuItem( MR.strings.settingsItem, - { Icon(Icons.Bottombar.Box10Outline, "Debug", tint = MaterialTheme.colorScheme.onPrimary) }, - { Icon(Icons.Bottombar.Box10Filled, "Debug", tint = MaterialTheme.colorScheme.onSecondaryContainer) }, + { Icon(OtariumIcons.Bottombar.Box10Outline, "Debug", tint = MaterialTheme.colorScheme.onPrimary) }, + { Icon(OtariumIcons.Bottombar.Box10Filled, "Debug", tint = MaterialTheme.colorScheme.onSecondaryContainer) }, ) } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/debug/DebugScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/debug/DebugScreen.kt index 4cd1339e..33c14dc1 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/debug/DebugScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/debug/DebugScreen.kt @@ -23,21 +23,24 @@ internal fun DebugScreen(component: DebugComponent) { Column(modifier = Modifier.padding(horizontal = 8.dp)) { SettingRowIconButton( leftText = AnnotatedString("Ask notification permission"), - icon = Icons.Default.Notifications + icon = Icons.Default.Notifications, + rowClickable = true, ) { setupNotifications() } SettingRowIconButton( leftText = AnnotatedString("Send test notification"), - icon = Icons.Default.Notifications + icon = Icons.Default.Notifications, + rowClickable = true, ) { sendNotification("Test notification", "This is a test notification") } SettingRowIconButton( leftText = AnnotatedString("Remove random grade"), - icon = Icons.Default.Delete + icon = Icons.Default.Delete, + rowClickable = true, ) { val account = Data.selectedAccount val savedGrades = account.fullGradeList.toMutableList() @@ -48,7 +51,8 @@ internal fun DebugScreen(component: DebugComponent) { SettingRowIconButton( leftText = AnnotatedString("Check grades"), - icon = Icons.Default.Refresh + icon = Icons.Default.Refresh, + rowClickable = true, ) { component.scope.launch { Data.selectedAccount.refreshGrades() @@ -57,7 +61,8 @@ internal fun DebugScreen(component: DebugComponent) { SettingRowIconButton( leftText = AnnotatedString("Check grades background"), - icon = Icons.Default.Refresh + icon = Icons.Default.Refresh, + rowClickable = true, ) { component.scope.launch { refreshGradesBackground() @@ -66,7 +71,8 @@ internal fun DebugScreen(component: DebugComponent) { SettingRowIconButton( leftText = AnnotatedString("Export accounts"), - icon = Icons.Default.Share + icon = Icons.Default.Share, + rowClickable = true, ) { component.exportAccounts() } @@ -74,14 +80,16 @@ internal fun DebugScreen(component: DebugComponent) { SettingRowIconButton( leftText = AnnotatedString("Import accounts"), - icon = Icons.Default.Add + icon = Icons.Default.Add, + rowClickable = true, ) { component.importAccounts(getClipboardText()) } SettingRowIconButton( leftText = AnnotatedString("Change language"), - icon = Icons.Default.KeyboardArrowRight + icon = Icons.Default.KeyboardArrowRight, + rowClickable = true, ) { component.changeLanguage() } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionCards.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionCards.kt index a8f0440a..81c91bad 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionCards.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionCards.kt @@ -15,9 +15,9 @@ import kotlinx.datetime.toLocalDateTime import nl.tiebe.otarium.MR import nl.tiebe.otarium.ui.utils.DownloadIndicator import nl.tiebe.otarium.ui.utils.parseHtml -import nl.tiebe.otarium.utils.icons.Email -import nl.tiebe.otarium.utils.icons.Icons -import nl.tiebe.otarium.utils.icons.email.Attachment +import nl.tiebe.otarium.utils.otariumicons.Email +import nl.tiebe.otarium.utils.OtariumIcons +import nl.tiebe.otarium.utils.otariumicons.email.Attachment import nl.tiebe.otarium.utils.toFormattedString import nl.tiebe.otarium.utils.ui.getLocalizedString @@ -93,7 +93,7 @@ internal fun TeacherFeedbackCard(component: AssignmentScreenComponent, assignmen verticalAlignment = Alignment.CenterVertically ) { Icon( - Icons.Email.Attachment, + OtariumIcons.Email.Attachment, contentDescription = "Attachment" ) Text(text = attachment.naam, modifier = Modifier.padding(start = 10.dp)) @@ -147,7 +147,7 @@ internal fun StudentVersionCard(component: AssignmentScreenComponent, assignment verticalAlignment = Alignment.CenterVertically ) { Icon( - Icons.Email.Attachment, + OtariumIcons.Email.Attachment, contentDescription = "Attachment" ) Text(text = attachment.naam, modifier = Modifier.padding(start = 10.dp)) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/GradesComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/GradesComponent.kt index 5514a3bb..6cbe71e2 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/GradesComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/GradesComponent.kt @@ -17,6 +17,8 @@ interface GradesComponent : MenuItemComponent { object RecentGrades : GradesChild(0) object Calculation : GradesChild(1) + //todo: overzicht van alle cijfers + } val recentGradeComponent: RecentGradesChildComponent diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/MessageItem.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/MessageItem.kt index e981aaf8..96542438 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/MessageItem.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/MessageItem.kt @@ -9,22 +9,22 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import dev.tiebe.magisterapi.response.messages.Message import dev.tiebe.magisterapi.response.messages.MessageFolder -import nl.tiebe.otarium.utils.icons.Bottombar -import nl.tiebe.otarium.utils.icons.Email -import nl.tiebe.otarium.utils.icons.Folder -import nl.tiebe.otarium.utils.icons.Icons -import nl.tiebe.otarium.utils.icons.bottombar.EmailFilled -import nl.tiebe.otarium.utils.icons.email.Attachment -import nl.tiebe.otarium.utils.icons.email.EmailAlert -import nl.tiebe.otarium.utils.icons.email.EmailAlertOpen -import nl.tiebe.otarium.utils.icons.email.EmailOpen +import nl.tiebe.otarium.utils.otariumicons.Bottombar +import nl.tiebe.otarium.utils.otariumicons.Email +import nl.tiebe.otarium.utils.otariumicons.Folder +import nl.tiebe.otarium.utils.OtariumIcons +import nl.tiebe.otarium.utils.otariumicons.bottombar.EmailFilled +import nl.tiebe.otarium.utils.otariumicons.email.Attachment +import nl.tiebe.otarium.utils.otariumicons.email.EmailAlert +import nl.tiebe.otarium.utils.otariumicons.email.EmailAlertOpen +import nl.tiebe.otarium.utils.otariumicons.email.EmailOpen @OptIn(ExperimentalMaterial3Api::class) @Composable internal fun MessageFolderItem(navigateToFolder: (MessageFolder) -> Unit, folder: MessageFolder) { ListItem( headlineText = { Text(folder.name) }, - leadingContent = { Icon(Icons.Folder, contentDescription = null) }, + leadingContent = { Icon(OtariumIcons.Folder, contentDescription = null) }, trailingContent = { Text(folder.unreadCount.toString()) }, modifier = Modifier.clickable { navigateToFolder(folder) @@ -36,14 +36,14 @@ internal fun MessageFolderItem(navigateToFolder: (MessageFolder) -> Unit, folder @Composable internal fun MessageItem(navigateToMessage: (Message) -> Unit, message: Message) { val icon = if (message.hasBeenRead) - if (message.hasPriority) Icons.Email.EmailAlertOpen else Icons.Email.EmailOpen - else if (message.hasPriority) Icons.Email.EmailAlert else Icons.Bottombar.EmailFilled + if (message.hasPriority) OtariumIcons.Email.EmailAlertOpen else OtariumIcons.Email.EmailOpen + else if (message.hasPriority) OtariumIcons.Email.EmailAlert else OtariumIcons.Bottombar.EmailFilled ListItem( headlineText = { Text(message.subject) }, supportingText = { Text(message.sender?.name ?: message.receivers?.joinToString { it.name } ?: "") }, leadingContent = { Icon(icon, contentDescription = null) }, - trailingContent = { if (message.hasAttachments) Icon(Icons.Email.Attachment, contentDescription = null) }, + trailingContent = { if (message.hasAttachments) Icon(OtariumIcons.Email.Attachment, contentDescription = null) }, modifier = Modifier.clickable { navigateToMessage(message) } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/message/MessageHeader.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/message/MessageHeader.kt index 23e5f9fa..3050a7bf 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/message/MessageHeader.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/message/MessageHeader.kt @@ -16,10 +16,10 @@ import nl.tiebe.otarium.MR import nl.tiebe.otarium.ui.home.messages.MessagesComponent import nl.tiebe.otarium.ui.home.messages.message.receiver.ReceiverInfoComponent import nl.tiebe.otarium.ui.utils.DownloadIndicator -import nl.tiebe.otarium.utils.icons.Email -import nl.tiebe.otarium.utils.icons.Icons -import nl.tiebe.otarium.utils.icons.email.Attachment -import nl.tiebe.otarium.utils.icons.email.AttachmentOff +import nl.tiebe.otarium.utils.otariumicons.Email +import nl.tiebe.otarium.utils.OtariumIcons +import nl.tiebe.otarium.utils.otariumicons.email.Attachment +import nl.tiebe.otarium.utils.otariumicons.email.AttachmentOff import nl.tiebe.otarium.utils.toFormattedString @OptIn(ExperimentalMaterial3Api::class) @@ -93,7 +93,7 @@ internal fun MessageHeader(component: MessageComponent) { verticalAlignment = Alignment.CenterVertically ) { Icon( - if (attachment.status == "available") Icons.Email.Attachment else Icons.Email.AttachmentOff, + if (attachment.status == "available") OtariumIcons.Email.Attachment else OtariumIcons.Email.AttachmentOff, contentDescription = "Attachment" ) Text(text = attachment.name, modifier = Modifier.padding(start = 10.dp)) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ads/AdsChildScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ads/AdsChildScreen.kt index e61371af..04cb232c 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ads/AdsChildScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ads/AdsChildScreen.kt @@ -25,7 +25,8 @@ internal fun AdsChildScreen(component: AdsChildComponent) { Divider() SettingsRowToggle( leftText = AnnotatedString(getLocalizedString(MR.strings.show_ads_checkbox)), - checked = checkedStateAds.value + checked = checkedStateAds.value, + rowClickable = true, ) { checkedStateAds.value = it component.changeAdsState(it) @@ -33,7 +34,8 @@ internal fun AdsChildScreen(component: AdsChildComponent) { SettingsRowToggle( leftText = AnnotatedString(getLocalizedString(MR.strings.age_checkbox)), - checked = checkedStateAge.value + checked = checkedStateAge.value, + rowClickable = true, ) { checkedStateAge.value = it component.changeAgeOfConsent(it) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/main/MainChildScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/main/MainChildScreen.kt index 38c2862d..e3af0214 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/main/MainChildScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/main/MainChildScreen.kt @@ -13,10 +13,10 @@ import androidx.compose.ui.text.AnnotatedString import nl.tiebe.otarium.Data import nl.tiebe.otarium.ui.home.settings.SettingsComponent import nl.tiebe.otarium.ui.home.settings.utils.SettingRowIconButton -import nl.tiebe.otarium.utils.icons.Advertisements -import nl.tiebe.otarium.utils.icons.AdvertisementsOff -import nl.tiebe.otarium.utils.icons.BugOutline -import nl.tiebe.otarium.utils.icons.Icons +import nl.tiebe.otarium.utils.OtariumIcons +import nl.tiebe.otarium.utils.otariumicons.Advertisements +import nl.tiebe.otarium.utils.otariumicons.AdvertisementsOff +import nl.tiebe.otarium.utils.otariumicons.BugOutline import androidx.compose.material.icons.Icons as MaterialIcons @Composable @@ -30,7 +30,8 @@ internal fun MainChildScreen(component: MainChildComponent) { //users SettingRowIconButton( leftText = AnnotatedString(SettingsComponent.Config.Users.localizedString), - icon = MaterialIcons.Default.AccountCircle + icon = MaterialIcons.Default.AccountCircle, + rowClickable = true, ) { component.navigate(SettingsComponent.Config.Users) } @@ -38,7 +39,8 @@ internal fun MainChildScreen(component: MainChildComponent) { //ads SettingRowIconButton( leftText = AnnotatedString(SettingsComponent.Config.Ads.localizedString), - icon = if (Data.showAds) Icons.Advertisements else Icons.AdvertisementsOff + icon = if (Data.showAds) OtariumIcons.Advertisements else OtariumIcons.AdvertisementsOff, + rowClickable = true, ) { component.navigate(SettingsComponent.Config.Ads) } @@ -46,7 +48,8 @@ internal fun MainChildScreen(component: MainChildComponent) { //bug report SettingRowIconButton( leftText = AnnotatedString(SettingsComponent.Config.Bugs.localizedString), - icon = Icons.BugOutline + icon = OtariumIcons.BugOutline, + rowClickable = true, ) { component.navigate(SettingsComponent.Config.Bugs) } @@ -54,7 +57,8 @@ internal fun MainChildScreen(component: MainChildComponent) { //ui SettingRowIconButton( leftText = AnnotatedString(SettingsComponent.Config.UI.localizedString), - icon = MaterialIcons.Default.Menu + icon = MaterialIcons.Default.Menu, + rowClickable = true, ) { component.navigate(SettingsComponent.Config.UI) } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/UIChildScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/UIChildScreen.kt index 9d465302..dd196db7 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/UIChildScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/UIChildScreen.kt @@ -10,9 +10,8 @@ import nl.tiebe.otarium.MR import nl.tiebe.otarium.ui.home.settings.SettingsComponent import nl.tiebe.otarium.ui.home.settings.utils.SettingRowIconButton import nl.tiebe.otarium.ui.home.settings.utils.SettingSlider -import nl.tiebe.otarium.utils.icons.Email -import nl.tiebe.otarium.utils.icons.Icons -import nl.tiebe.otarium.utils.icons.email.Attachment +import nl.tiebe.otarium.utils.OtariumIcons +import nl.tiebe.otarium.utils.otariumicons.Palette import nl.tiebe.otarium.utils.ui.getLocalizedString import kotlin.math.roundToInt @@ -25,8 +24,9 @@ internal fun UIChildScreen(component: UIChildComponent) { var sliderValue by remember { mutableStateOf(Data.decimals.toFloat()) } SettingRowIconButton( - leftText = AnnotatedString("Colorrrrssss"), - icon = Icons.Email.Attachment, + leftText = AnnotatedString(getLocalizedString(MR.strings.color_settings)), + icon = OtariumIcons.Palette, + rowClickable = true, onClick = { component.navigate(SettingsComponent.Config.Colors) } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/colors/ColorChildScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/colors/ColorChildScreen.kt index e8eaa566..c4417cae 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/colors/ColorChildScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/colors/ColorChildScreen.kt @@ -3,12 +3,13 @@ package nl.tiebe.otarium.ui.home.settings.items.ui.colors import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.expandVertically import androidx.compose.animation.shrinkVertically +import androidx.compose.foundation.clickable 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.filled.Create -import androidx.compose.material.icons.filled.Delete +import androidx.compose.material.icons.filled.Refresh import androidx.compose.material3.Button import androidx.compose.material3.Icon import androidx.compose.material3.Text @@ -20,10 +21,14 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.unit.dp import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState +import nl.tiebe.otarium.MR import nl.tiebe.otarium.ui.home.settings.utils.SettingRowIconButton import nl.tiebe.otarium.ui.home.settings.utils.SettingsColorPicker import nl.tiebe.otarium.ui.home.settings.utils.SettingsRowToggle +import nl.tiebe.otarium.utils.OtariumIcons import nl.tiebe.otarium.utils.dynamicColorsPossible +import nl.tiebe.otarium.utils.otariumicons.ContentSave +import nl.tiebe.otarium.utils.ui.getLocalizedString @Composable internal fun ColorChildScreen(component: ColorChildComponent) { @@ -38,41 +43,44 @@ internal fun ColorChildScreen(component: ColorChildComponent) { if (dynamicColorsPossible()) { SettingsRowToggle( - leftText = AnnotatedString("Dynamic color scheme"), + leftText = AnnotatedString(getLocalizedString(MR.strings.color_dynamic)), checked = dynamicColorScheme.value, + rowClickable = true ) { component.dynamicColorState.value = it } } SettingsRowToggle( - leftText = AnnotatedString("Custom color scheme"), + leftText = AnnotatedString(getLocalizedString(MR.strings.color_custom)), checked = customColorScheme.value, + rowClickable = true ) { component.customColorScheme.value = it } if (customColorScheme.value) { SettingRowIconButton( - leftText = AnnotatedString("Reset to default"), - icon = Icons.Default.Delete, + leftText = AnnotatedString(getLocalizedString(MR.strings.color_reset)), + icon = Icons.Default.Refresh, + rowClickable = false, onClick = { component.resetColorScheme() } ) SettingRowIconButton( - leftText = AnnotatedString("Save color scheme"), - icon = Icons.Default.Create, + leftText = AnnotatedString(getLocalizedString(MR.strings.color_save)), + icon = OtariumIcons.ContentSave, + rowClickable = false, onClick = { - println(component.primaryLightColor.value.toColor()) component.saveColorScheme() } ) val showLightColors = remember { mutableStateOf(false) } - Row(modifier = Modifier.fillMaxWidth(0.95f).height(70.dp), + Row(modifier = Modifier.fillMaxWidth(0.95f).height(70.dp).clickable { showLightColors.value = !showLightColors.value }, horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically) { Text(text = "Light") @@ -85,21 +93,21 @@ internal fun ColorChildScreen(component: ColorChildComponent) { AnimatedVisibility(modifier = Modifier.padding(start = 16.dp), visible = showLightColors.value, enter = expandVertically(), exit = shrinkVertically()) { Column { SettingsColorPicker( - leftText = AnnotatedString("Primary color"), + leftText = AnnotatedString(getLocalizedString(MR.strings.color_primary)), color = component.primaryLightColor.subscribeAsState().value ) { component.primaryLightColor.value = it } SettingsColorPicker( - leftText = AnnotatedString("Secondary color"), + leftText = AnnotatedString(getLocalizedString(MR.strings.color_secondary)), color = component.secondaryLightColor.subscribeAsState().value ) { component.secondaryLightColor.value = it } SettingsColorPicker( - leftText = AnnotatedString("Tertiary color"), + leftText = AnnotatedString(getLocalizedString(MR.strings.color_tertiary)), color = component.tertiaryLightColor.subscribeAsState().value ) { component.tertiaryLightColor.value = it @@ -109,7 +117,7 @@ internal fun ColorChildScreen(component: ColorChildComponent) { val showDarkColors = remember { mutableStateOf(false) } - Row(modifier = Modifier.fillMaxWidth(0.95f).height(70.dp), + Row(modifier = Modifier.fillMaxWidth(0.95f).height(70.dp).clickable { showDarkColors.value = !showDarkColors.value }, horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically) { Text(text = "Dark") @@ -122,21 +130,21 @@ internal fun ColorChildScreen(component: ColorChildComponent) { AnimatedVisibility(modifier = Modifier.padding(start = 16.dp), visible = showDarkColors.value, enter = expandVertically(), exit = shrinkVertically()) { Column { SettingsColorPicker( - leftText = AnnotatedString("Primary color"), + leftText = AnnotatedString(getLocalizedString(MR.strings.color_primary)), color = component.primaryDarkColor.subscribeAsState().value ) { component.primaryDarkColor.value = it } SettingsColorPicker( - leftText = AnnotatedString("Secondary color"), + leftText = AnnotatedString(getLocalizedString(MR.strings.color_secondary)), color = component.secondaryDarkColor.subscribeAsState().value ) { component.secondaryDarkColor.value = it } SettingsColorPicker( - leftText = AnnotatedString("Tertiary color"), + leftText = AnnotatedString(getLocalizedString(MR.strings.color_tertiary)), color = component.tertiaryDarkColor.subscribeAsState().value ) { component.tertiaryDarkColor.value = it diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/utils/SettingColorPicker.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/utils/SettingColorPicker.kt index 3e37385d..a0900f37 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/utils/SettingColorPicker.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/utils/SettingColorPicker.kt @@ -3,6 +3,7 @@ package nl.tiebe.otarium.ui.home.settings.utils import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.expandVertically import androidx.compose.animation.shrinkVertically +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.material3.* import androidx.compose.runtime.Composable @@ -22,9 +23,8 @@ internal fun SettingsColorPicker(modifier: Modifier = Modifier, leftText: Annota val showPicker = remember { mutableStateOf(false) } Column { - Row( - modifier = Modifier.fillMaxWidth(0.95f).height(70.dp).then(modifier), + modifier = Modifier.fillMaxWidth(0.95f).height(70.dp).clickable { showPicker.value = !showPicker.value }.then(modifier), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/utils/SettingRowButton.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/utils/SettingRowButton.kt index c9ff6977..d6b57ec2 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/utils/SettingRowButton.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/utils/SettingRowButton.kt @@ -1,5 +1,6 @@ package nl.tiebe.otarium.ui.home.settings.utils +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.width @@ -16,7 +17,7 @@ import androidx.compose.ui.text.TextStyle import androidx.compose.ui.unit.dp @Composable -internal fun SettingRowIconButton(modifier: Modifier = Modifier, leftText: AnnotatedString, textStyle: TextStyle = TextStyle(), icon: Painter, description: String = leftText.text, onClick: () -> Unit) { +internal fun SettingRowIconButton(modifier: Modifier = Modifier, leftText: AnnotatedString, textStyle: TextStyle = TextStyle(), icon: Painter, description: String = leftText.text, rowClickable: Boolean, onClick: () -> Unit) { SettingRow(modifier = modifier, text = leftText, textStyle = textStyle) { Button(modifier = Modifier.width(50.dp), onClick = onClick, contentPadding = PaddingValues(0.dp)) { Icon(painter = icon, contentDescription = description, modifier = Modifier.fillMaxWidth()) @@ -25,13 +26,14 @@ internal fun SettingRowIconButton(modifier: Modifier = Modifier, leftText: Annot } @Composable -internal fun SettingRowIconButton(modifier: Modifier = Modifier, leftText: AnnotatedString, textStyle: TextStyle = TextStyle(), icon: ImageVector, description: String = leftText.text, onClick: () -> Unit) { +internal fun SettingRowIconButton(modifier: Modifier = Modifier, leftText: AnnotatedString, textStyle: TextStyle = TextStyle(), icon: ImageVector, description: String = leftText.text, rowClickable: Boolean, onClick: () -> Unit) { SettingRowIconButton( - modifier = modifier, + modifier = modifier.clickable(enabled = rowClickable, onClick = onClick), leftText = leftText, textStyle = textStyle, icon = rememberVectorPainter(icon), description = description, + rowClickable = rowClickable, onClick = onClick ) } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/utils/SettingRowToggle.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/utils/SettingRowToggle.kt index d3687c06..3ffbc0a3 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/utils/SettingRowToggle.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/utils/SettingRowToggle.kt @@ -1,5 +1,6 @@ package nl.tiebe.otarium.ui.home.settings.utils +import androidx.compose.foundation.clickable import androidx.compose.material3.Switch import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -7,8 +8,12 @@ import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.TextStyle @Composable -internal fun SettingsRowToggle(modifier: Modifier = Modifier, leftText: AnnotatedString, textStyle: TextStyle = TextStyle(), checked: Boolean, onClick: ((Boolean) -> Unit)?) { - SettingRow(modifier = modifier, text = leftText, textStyle = textStyle) { +internal fun SettingsRowToggle(modifier: Modifier = Modifier, leftText: AnnotatedString, textStyle: TextStyle = TextStyle(), checked: Boolean, rowClickable: Boolean, onClick: ((Boolean) -> Unit)?) { + SettingRow(modifier = modifier.clickable(enabled = rowClickable, onClick = { + if (onClick != null) { + onClick(!checked) + } + }), text = leftText, textStyle = textStyle) { Switch(checked = checked, onCheckedChange = onClick) } } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/utils/SettingSlider.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/utils/SettingSlider.kt index a1bf6fc8..c8d77c80 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/utils/SettingSlider.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/utils/SettingSlider.kt @@ -12,7 +12,6 @@ import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.style.TextAlign -@OptIn(ExperimentalMaterial3Api::class) @Composable internal fun SettingSlider( modifier: Modifier = Modifier, diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/__Icons.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/__OtariumIcons.kt similarity index 62% rename from shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/__Icons.kt rename to shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/__OtariumIcons.kt index 7c629b49..0d087446 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/__Icons.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/__OtariumIcons.kt @@ -1,18 +1,19 @@ -package nl.tiebe.otarium.utils.icons +package nl.tiebe.otarium.utils import androidx.compose.ui.graphics.vector.ImageVector +import nl.tiebe.otarium.utils.otariumicons.* import kotlin.collections.List as ____KtList -public object Icons +public object OtariumIcons private var __AllIcons: ____KtList? = null -public val Icons.AllIcons: ____KtList +public val OtariumIcons.AllIcons: ____KtList get() { if (__AllIcons != null) { return __AllIcons!! } __AllIcons= Bottombar.AllIcons + Email.AllIcons + listOf(Advertisements, AdvertisementsOff, - BugOutline, Folder) + BugOutline, Folder, ContentSave, Palette) return __AllIcons!! } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/__Bottombar.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/__Bottombar.kt deleted file mode 100644 index 4150ac15..00000000 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/__Bottombar.kt +++ /dev/null @@ -1,22 +0,0 @@ -package nl.tiebe.otarium.utils.icons - -import androidx.compose.ui.graphics.vector.ImageVector -import nl.tiebe.otarium.utils.icons.bottombar.* -import kotlin.collections.List as ____KtList - -public object BottombarGroup - -public val Icons.Bottombar: BottombarGroup - get() = BottombarGroup - -private var __AllIcons: ____KtList? = null - -public val BottombarGroup.AllIcons: ____KtList - get() { - if (__AllIcons != null) { - return __AllIcons!! - } - __AllIcons = listOf(Box10Filled, Box10Outline, CalendarTodayFilled, CalendarTodayOutline, - CogFilled, CogOutline, EmailFilled, EmailOutline, BookOpenOutline, BookOpenFilled) - return __AllIcons!! - } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/__Email.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/__Email.kt deleted file mode 100644 index ac06137d..00000000 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/__Email.kt +++ /dev/null @@ -1,21 +0,0 @@ -package nl.tiebe.otarium.utils.icons - -import androidx.compose.ui.graphics.vector.ImageVector -import nl.tiebe.otarium.utils.icons.email.* -import kotlin.collections.List as ____KtList - -public object EmailGroup - -public val Icons.Email: EmailGroup - get() = EmailGroup - -private var __AllIcons: ____KtList? = null - -public val EmailGroup.AllIcons: ____KtList - get() { - if (__AllIcons != null) { - return __AllIcons!! - } - __AllIcons = listOf(Attachment, AttachmentOff, EmailAlert, EmailAlertOpen, EmailOpen) - return __AllIcons!! - } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/Advertisements.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/Advertisements.kt similarity index 95% rename from shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/Advertisements.kt rename to shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/Advertisements.kt index 5a0bb310..2e276e2f 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/Advertisements.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/Advertisements.kt @@ -1,4 +1,4 @@ -package nl.tiebe.otarium.utils.icons +package nl.tiebe.otarium.utils.otariumicons import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.PathFillType.Companion.NonZero @@ -9,8 +9,9 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector.Builder import androidx.compose.ui.graphics.vector.path import androidx.compose.ui.unit.dp +import nl.tiebe.otarium.utils.OtariumIcons -public val Icons.Advertisements: ImageVector +public val OtariumIcons.Advertisements: ImageVector get() { if (_advertisements != null) { return _advertisements!! diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/AdvertisementsOff.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/AdvertisementsOff.kt similarity index 95% rename from shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/AdvertisementsOff.kt rename to shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/AdvertisementsOff.kt index 59eeb824..d6266f22 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/AdvertisementsOff.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/AdvertisementsOff.kt @@ -1,4 +1,4 @@ -package nl.tiebe.otarium.utils.icons +package nl.tiebe.otarium.utils.otariumicons import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.PathFillType.Companion.NonZero @@ -9,9 +9,9 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector.Builder import androidx.compose.ui.graphics.vector.path import androidx.compose.ui.unit.dp -import nl.tiebe.otarium.utils.icons.Icons +import nl.tiebe.otarium.utils.OtariumIcons -public val Icons.AdvertisementsOff: ImageVector +public val OtariumIcons.AdvertisementsOff: ImageVector get() { if (_advertisementsOff != null) { return _advertisementsOff!! diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/BugOutline.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/BugOutline.kt similarity index 96% rename from shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/BugOutline.kt rename to shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/BugOutline.kt index 4bbfaad3..86709a5a 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/BugOutline.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/BugOutline.kt @@ -1,4 +1,4 @@ -package nl.tiebe.otarium.utils.icons +package nl.tiebe.otarium.utils.otariumicons import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.PathFillType.Companion.NonZero @@ -9,9 +9,9 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector.Builder import androidx.compose.ui.graphics.vector.path import androidx.compose.ui.unit.dp -import nl.tiebe.otarium.utils.icons.Icons +import nl.tiebe.otarium.utils.OtariumIcons -public val Icons.BugOutline: ImageVector +public val OtariumIcons.BugOutline: ImageVector get() { if (_bugOutline != null) { return _bugOutline!! diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/ContentSave.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/ContentSave.kt new file mode 100644 index 00000000..f6858b4a --- /dev/null +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/ContentSave.kt @@ -0,0 +1,49 @@ +package nl.tiebe.otarium.utils.otariumicons + +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.PathFillType.Companion.NonZero +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.graphics.StrokeCap.Companion.Butt +import androidx.compose.ui.graphics.StrokeJoin.Companion.Miter +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.vector.ImageVector.Builder +import androidx.compose.ui.graphics.vector.path +import androidx.compose.ui.unit.dp +import nl.tiebe.otarium.utils.OtariumIcons + +public val OtariumIcons.ContentSave: ImageVector + get() { + if (_contentSave != null) { + return _contentSave!! + } + _contentSave = Builder(name = "Content-save", defaultWidth = 24.0.dp, defaultHeight = + 24.0.dp, viewportWidth = 24.0f, viewportHeight = 24.0f).apply { + path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f, + strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f, + pathFillType = NonZero) { + moveTo(15.0f, 9.0f) + horizontalLineTo(5.0f) + verticalLineTo(5.0f) + horizontalLineTo(15.0f) + moveTo(12.0f, 19.0f) + arcTo(3.0f, 3.0f, 0.0f, false, true, 9.0f, 16.0f) + arcTo(3.0f, 3.0f, 0.0f, false, true, 12.0f, 13.0f) + arcTo(3.0f, 3.0f, 0.0f, false, true, 15.0f, 16.0f) + arcTo(3.0f, 3.0f, 0.0f, false, true, 12.0f, 19.0f) + moveTo(17.0f, 3.0f) + horizontalLineTo(5.0f) + curveTo(3.89f, 3.0f, 3.0f, 3.9f, 3.0f, 5.0f) + verticalLineTo(19.0f) + arcTo(2.0f, 2.0f, 0.0f, false, false, 5.0f, 21.0f) + horizontalLineTo(19.0f) + arcTo(2.0f, 2.0f, 0.0f, false, false, 21.0f, 19.0f) + verticalLineTo(7.0f) + lineTo(17.0f, 3.0f) + close() + } + } + .build() + return _contentSave!! + } + +private var _contentSave: ImageVector? = null diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/Folder.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/Folder.kt similarity index 92% rename from shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/Folder.kt rename to shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/Folder.kt index 30803d8a..139da970 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/Folder.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/Folder.kt @@ -1,4 +1,4 @@ -package nl.tiebe.otarium.utils.icons +package nl.tiebe.otarium.utils.otariumicons import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.PathFillType.Companion.NonZero @@ -9,8 +9,9 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector.Builder import androidx.compose.ui.graphics.vector.path import androidx.compose.ui.unit.dp +import nl.tiebe.otarium.utils.OtariumIcons -public val Icons.Folder: ImageVector +public val OtariumIcons.Folder: ImageVector get() { if (_folder != null) { return _folder!! diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/Palette.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/Palette.kt new file mode 100644 index 00000000..84f411fc --- /dev/null +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/Palette.kt @@ -0,0 +1,61 @@ +package nl.tiebe.otarium.utils.otariumicons + +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.PathFillType.Companion.NonZero +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.graphics.StrokeCap.Companion.Butt +import androidx.compose.ui.graphics.StrokeJoin.Companion.Miter +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.vector.ImageVector.Builder +import androidx.compose.ui.graphics.vector.path +import androidx.compose.ui.unit.dp +import nl.tiebe.otarium.utils.OtariumIcons + +public val OtariumIcons.Palette: ImageVector + get() { + if (_palette != null) { + return _palette!! + } + _palette = Builder(name = "Palette", defaultWidth = 24.0.dp, defaultHeight = 24.0.dp, + viewportWidth = 24.0f, viewportHeight = 24.0f).apply { + path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f, + strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f, + pathFillType = NonZero) { + moveTo(17.5f, 12.0f) + arcTo(1.5f, 1.5f, 0.0f, false, true, 16.0f, 10.5f) + arcTo(1.5f, 1.5f, 0.0f, false, true, 17.5f, 9.0f) + arcTo(1.5f, 1.5f, 0.0f, false, true, 19.0f, 10.5f) + arcTo(1.5f, 1.5f, 0.0f, false, true, 17.5f, 12.0f) + moveTo(14.5f, 8.0f) + arcTo(1.5f, 1.5f, 0.0f, false, true, 13.0f, 6.5f) + arcTo(1.5f, 1.5f, 0.0f, false, true, 14.5f, 5.0f) + arcTo(1.5f, 1.5f, 0.0f, false, true, 16.0f, 6.5f) + arcTo(1.5f, 1.5f, 0.0f, false, true, 14.5f, 8.0f) + moveTo(9.5f, 8.0f) + arcTo(1.5f, 1.5f, 0.0f, false, true, 8.0f, 6.5f) + arcTo(1.5f, 1.5f, 0.0f, false, true, 9.5f, 5.0f) + arcTo(1.5f, 1.5f, 0.0f, false, true, 11.0f, 6.5f) + arcTo(1.5f, 1.5f, 0.0f, false, true, 9.5f, 8.0f) + moveTo(6.5f, 12.0f) + arcTo(1.5f, 1.5f, 0.0f, false, true, 5.0f, 10.5f) + arcTo(1.5f, 1.5f, 0.0f, false, true, 6.5f, 9.0f) + arcTo(1.5f, 1.5f, 0.0f, false, true, 8.0f, 10.5f) + arcTo(1.5f, 1.5f, 0.0f, false, true, 6.5f, 12.0f) + moveTo(12.0f, 3.0f) + arcTo(9.0f, 9.0f, 0.0f, false, false, 3.0f, 12.0f) + arcTo(9.0f, 9.0f, 0.0f, false, false, 12.0f, 21.0f) + arcTo(1.5f, 1.5f, 0.0f, false, false, 13.5f, 19.5f) + curveTo(13.5f, 19.11f, 13.35f, 18.76f, 13.11f, 18.5f) + curveTo(12.88f, 18.23f, 12.73f, 17.88f, 12.73f, 17.5f) + arcTo(1.5f, 1.5f, 0.0f, false, true, 14.23f, 16.0f) + horizontalLineTo(16.0f) + arcTo(5.0f, 5.0f, 0.0f, false, false, 21.0f, 11.0f) + curveTo(21.0f, 6.58f, 16.97f, 3.0f, 12.0f, 3.0f) + close() + } + } + .build() + return _palette!! + } + +private var _palette: ImageVector? = null diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/__Bottombar.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/__Bottombar.kt new file mode 100644 index 00000000..21f6fbee --- /dev/null +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/__Bottombar.kt @@ -0,0 +1,32 @@ +package nl.tiebe.otarium.utils.otariumicons + +import androidx.compose.ui.graphics.vector.ImageVector +import nl.tiebe.otarium.utils.OtariumIcons +import nl.tiebe.otarium.utils.otariumicons.bottombar.BookOpenFilled +import nl.tiebe.otarium.utils.otariumicons.bottombar.BookOpenOutline +import nl.tiebe.otarium.utils.otariumicons.bottombar.Box10Filled +import nl.tiebe.otarium.utils.otariumicons.bottombar.Box10Outline +import nl.tiebe.otarium.utils.otariumicons.bottombar.CalendarTodayFilled +import nl.tiebe.otarium.utils.otariumicons.bottombar.CalendarTodayOutline +import nl.tiebe.otarium.utils.otariumicons.bottombar.CogFilled +import nl.tiebe.otarium.utils.otariumicons.bottombar.CogOutline +import nl.tiebe.otarium.utils.otariumicons.bottombar.EmailFilled +import nl.tiebe.otarium.utils.otariumicons.bottombar.EmailOutline +import kotlin.collections.List as ____KtList + +public object BottombarGroup + +public val OtariumIcons.Bottombar: BottombarGroup + get() = BottombarGroup + +private var __AllIcons: ____KtList? = null + +public val BottombarGroup.AllIcons: ____KtList + get() { + if (__AllIcons != null) { + return __AllIcons!! + } + __AllIcons= listOf(BookOpenFilled, BookOpenOutline, Box10Filled, Box10Outline, + CalendarTodayFilled, CalendarTodayOutline, CogFilled, CogOutline, EmailFilled, EmailOutline) + return __AllIcons!! + } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/__Email.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/__Email.kt new file mode 100644 index 00000000..4dffa16b --- /dev/null +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/__Email.kt @@ -0,0 +1,26 @@ +package nl.tiebe.otarium.utils.otariumicons + +import androidx.compose.ui.graphics.vector.ImageVector +import nl.tiebe.otarium.utils.OtariumIcons +import nl.tiebe.otarium.utils.otariumicons.email.Attachment +import nl.tiebe.otarium.utils.otariumicons.email.AttachmentOff +import nl.tiebe.otarium.utils.otariumicons.email.EmailAlert +import nl.tiebe.otarium.utils.otariumicons.email.EmailAlertOpen +import nl.tiebe.otarium.utils.otariumicons.email.EmailOpen +import kotlin.collections.List as ____KtList + +public object EmailGroup + +public val OtariumIcons.Email: EmailGroup + get() = EmailGroup + +private var __AllIcons: ____KtList? = null + +public val EmailGroup.AllIcons: ____KtList + get() { + if (__AllIcons != null) { + return __AllIcons!! + } + __AllIcons= listOf(Attachment, AttachmentOff, EmailAlert, EmailAlertOpen, EmailOpen) + return __AllIcons!! + } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/bottombar/BookOpenFilled.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/bottombar/BookOpenFilled.kt similarity index 95% rename from shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/bottombar/BookOpenFilled.kt rename to shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/bottombar/BookOpenFilled.kt index ca8b25a6..e8a99ea6 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/bottombar/BookOpenFilled.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/bottombar/BookOpenFilled.kt @@ -1,4 +1,4 @@ -package nl.tiebe.otarium.utils.icons.bottombar +package nl.tiebe.otarium.utils.otariumicons.bottombar import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.PathFillType.Companion.NonZero @@ -9,7 +9,7 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector.Builder import androidx.compose.ui.graphics.vector.path import androidx.compose.ui.unit.dp -import nl.tiebe.otarium.utils.icons.BottombarGroup +import nl.tiebe.otarium.utils.otariumicons.BottombarGroup public val BottombarGroup.BookOpenFilled: ImageVector get() { diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/bottombar/BookOpenOutline.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/bottombar/BookOpenOutline.kt similarity index 95% rename from shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/bottombar/BookOpenOutline.kt rename to shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/bottombar/BookOpenOutline.kt index 96c0e2f8..8d278903 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/bottombar/BookOpenOutline.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/bottombar/BookOpenOutline.kt @@ -1,4 +1,4 @@ -package nl.tiebe.otarium.utils.icons.bottombar +package nl.tiebe.otarium.utils.otariumicons.bottombar import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.PathFillType.Companion.NonZero @@ -9,7 +9,7 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector.Builder import androidx.compose.ui.graphics.vector.path import androidx.compose.ui.unit.dp -import nl.tiebe.otarium.utils.icons.BottombarGroup +import nl.tiebe.otarium.utils.otariumicons.BottombarGroup public val BottombarGroup.BookOpenOutline: ImageVector get() { diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/bottombar/Box10Filled.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/bottombar/Box10Filled.kt similarity index 95% rename from shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/bottombar/Box10Filled.kt rename to shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/bottombar/Box10Filled.kt index 3ca3b6ec..d655f5e1 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/bottombar/Box10Filled.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/bottombar/Box10Filled.kt @@ -1,4 +1,4 @@ -package nl.tiebe.otarium.utils.icons.bottombar +package nl.tiebe.otarium.utils.otariumicons.bottombar import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.PathFillType.Companion.NonZero @@ -9,7 +9,7 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector.Builder import androidx.compose.ui.graphics.vector.path import androidx.compose.ui.unit.dp -import nl.tiebe.otarium.utils.icons.BottombarGroup +import nl.tiebe.otarium.utils.otariumicons.BottombarGroup public val BottombarGroup.Box10Filled: ImageVector get() { diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/bottombar/Box10Outline.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/bottombar/Box10Outline.kt similarity index 95% rename from shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/bottombar/Box10Outline.kt rename to shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/bottombar/Box10Outline.kt index 02b107c3..56215281 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/bottombar/Box10Outline.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/bottombar/Box10Outline.kt @@ -1,4 +1,4 @@ -package nl.tiebe.otarium.utils.icons.bottombar +package nl.tiebe.otarium.utils.otariumicons.bottombar import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.PathFillType.Companion.NonZero @@ -9,7 +9,7 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector.Builder import androidx.compose.ui.graphics.vector.path import androidx.compose.ui.unit.dp -import nl.tiebe.otarium.utils.icons.BottombarGroup +import nl.tiebe.otarium.utils.otariumicons.BottombarGroup public val BottombarGroup.Box10Outline: ImageVector get() { diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/bottombar/CalendarTodayFilled.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/bottombar/CalendarTodayFilled.kt similarity index 95% rename from shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/bottombar/CalendarTodayFilled.kt rename to shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/bottombar/CalendarTodayFilled.kt index d56a16f1..b1c7617a 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/bottombar/CalendarTodayFilled.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/bottombar/CalendarTodayFilled.kt @@ -1,4 +1,4 @@ -package nl.tiebe.otarium.utils.icons.bottombar +package nl.tiebe.otarium.utils.otariumicons.bottombar import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.PathFillType.Companion.NonZero @@ -9,7 +9,7 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector.Builder import androidx.compose.ui.graphics.vector.path import androidx.compose.ui.unit.dp -import nl.tiebe.otarium.utils.icons.BottombarGroup +import nl.tiebe.otarium.utils.otariumicons.BottombarGroup public val BottombarGroup.CalendarTodayFilled: ImageVector get() { diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/bottombar/CalendarTodayOutline.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/bottombar/CalendarTodayOutline.kt similarity index 95% rename from shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/bottombar/CalendarTodayOutline.kt rename to shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/bottombar/CalendarTodayOutline.kt index 4549ee4f..324b41cd 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/bottombar/CalendarTodayOutline.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/bottombar/CalendarTodayOutline.kt @@ -1,4 +1,4 @@ -package nl.tiebe.otarium.utils.icons.bottombar +package nl.tiebe.otarium.utils.otariumicons.bottombar import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.PathFillType.Companion.NonZero @@ -9,7 +9,7 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector.Builder import androidx.compose.ui.graphics.vector.path import androidx.compose.ui.unit.dp -import nl.tiebe.otarium.utils.icons.BottombarGroup +import nl.tiebe.otarium.utils.otariumicons.BottombarGroup public val BottombarGroup.CalendarTodayOutline: ImageVector get() { diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/bottombar/CogFilled.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/bottombar/CogFilled.kt similarity index 96% rename from shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/bottombar/CogFilled.kt rename to shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/bottombar/CogFilled.kt index 0e808851..359f0c0c 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/bottombar/CogFilled.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/bottombar/CogFilled.kt @@ -1,4 +1,4 @@ -package nl.tiebe.otarium.utils.icons.bottombar +package nl.tiebe.otarium.utils.otariumicons.bottombar import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.PathFillType.Companion.NonZero @@ -9,7 +9,7 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector.Builder import androidx.compose.ui.graphics.vector.path import androidx.compose.ui.unit.dp -import nl.tiebe.otarium.utils.icons.BottombarGroup +import nl.tiebe.otarium.utils.otariumicons.BottombarGroup public val BottombarGroup.CogFilled: ImageVector get() { diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/bottombar/CogOutline.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/bottombar/CogOutline.kt similarity index 97% rename from shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/bottombar/CogOutline.kt rename to shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/bottombar/CogOutline.kt index a41a90dc..aa2ba187 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/bottombar/CogOutline.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/bottombar/CogOutline.kt @@ -1,4 +1,4 @@ -package nl.tiebe.otarium.utils.icons.bottombar +package nl.tiebe.otarium.utils.otariumicons.bottombar import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.PathFillType.Companion.NonZero @@ -9,7 +9,7 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector.Builder import androidx.compose.ui.graphics.vector.path import androidx.compose.ui.unit.dp -import nl.tiebe.otarium.utils.icons.BottombarGroup +import nl.tiebe.otarium.utils.otariumicons.BottombarGroup public val BottombarGroup.CogOutline: ImageVector get() { diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/bottombar/EmailFilled.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/bottombar/EmailFilled.kt similarity index 94% rename from shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/bottombar/EmailFilled.kt rename to shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/bottombar/EmailFilled.kt index 32328f1c..03c3c688 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/bottombar/EmailFilled.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/bottombar/EmailFilled.kt @@ -1,4 +1,4 @@ -package nl.tiebe.otarium.utils.icons.bottombar +package nl.tiebe.otarium.utils.otariumicons.bottombar import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.PathFillType.Companion.NonZero @@ -9,7 +9,7 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector.Builder import androidx.compose.ui.graphics.vector.path import androidx.compose.ui.unit.dp -import nl.tiebe.otarium.utils.icons.BottombarGroup +import nl.tiebe.otarium.utils.otariumicons.BottombarGroup public val BottombarGroup.EmailFilled: ImageVector get() { diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/bottombar/EmailOutline.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/bottombar/EmailOutline.kt similarity index 94% rename from shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/bottombar/EmailOutline.kt rename to shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/bottombar/EmailOutline.kt index dd36e205..4dfa2888 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/bottombar/EmailOutline.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/bottombar/EmailOutline.kt @@ -1,4 +1,4 @@ -package nl.tiebe.otarium.utils.icons.bottombar +package nl.tiebe.otarium.utils.otariumicons.bottombar import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.PathFillType.Companion.NonZero @@ -9,7 +9,7 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector.Builder import androidx.compose.ui.graphics.vector.path import androidx.compose.ui.unit.dp -import nl.tiebe.otarium.utils.icons.BottombarGroup +import nl.tiebe.otarium.utils.otariumicons.BottombarGroup public val BottombarGroup.EmailOutline: ImageVector get() { diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/email/Attachment.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/email/Attachment.kt similarity index 95% rename from shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/email/Attachment.kt rename to shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/email/Attachment.kt index a67f9b77..e19105ff 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/email/Attachment.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/email/Attachment.kt @@ -1,4 +1,4 @@ -package nl.tiebe.otarium.utils.icons.email +package nl.tiebe.otarium.utils.otariumicons.email import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.PathFillType.Companion.NonZero @@ -9,7 +9,7 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector.Builder import androidx.compose.ui.graphics.vector.path import androidx.compose.ui.unit.dp -import nl.tiebe.otarium.utils.icons.EmailGroup +import nl.tiebe.otarium.utils.otariumicons.EmailGroup public val EmailGroup.Attachment: ImageVector get() { diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/email/AttachmentOff.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/email/AttachmentOff.kt similarity index 96% rename from shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/email/AttachmentOff.kt rename to shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/email/AttachmentOff.kt index b6d703d0..5a1edf56 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/email/AttachmentOff.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/email/AttachmentOff.kt @@ -1,4 +1,4 @@ -package nl.tiebe.otarium.utils.icons.email +package nl.tiebe.otarium.utils.otariumicons.email import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.PathFillType.Companion.NonZero @@ -9,7 +9,7 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector.Builder import androidx.compose.ui.graphics.vector.path import androidx.compose.ui.unit.dp -import nl.tiebe.otarium.utils.icons.EmailGroup +import nl.tiebe.otarium.utils.otariumicons.EmailGroup public val EmailGroup.AttachmentOff: ImageVector get() { diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/email/EmailAlert.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/email/EmailAlert.kt similarity index 95% rename from shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/email/EmailAlert.kt rename to shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/email/EmailAlert.kt index a5534c1a..b2ea4a46 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/email/EmailAlert.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/email/EmailAlert.kt @@ -1,4 +1,4 @@ -package nl.tiebe.otarium.utils.icons.email +package nl.tiebe.otarium.utils.otariumicons.email import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.PathFillType.Companion.NonZero @@ -9,7 +9,7 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector.Builder import androidx.compose.ui.graphics.vector.path import androidx.compose.ui.unit.dp -import nl.tiebe.otarium.utils.icons.EmailGroup +import nl.tiebe.otarium.utils.otariumicons.EmailGroup public val EmailGroup.EmailAlert: ImageVector get() { diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/email/EmailAlertOpen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/email/EmailAlertOpen.kt similarity index 95% rename from shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/email/EmailAlertOpen.kt rename to shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/email/EmailAlertOpen.kt index d5281232..736bbdc4 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/email/EmailAlertOpen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/email/EmailAlertOpen.kt @@ -1,4 +1,4 @@ -package nl.tiebe.otarium.utils.icons.email +package nl.tiebe.otarium.utils.otariumicons.email import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.PathFillType.Companion.NonZero @@ -9,7 +9,7 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector.Builder import androidx.compose.ui.graphics.vector.path import androidx.compose.ui.unit.dp -import nl.tiebe.otarium.utils.icons.EmailGroup +import nl.tiebe.otarium.utils.otariumicons.EmailGroup public val EmailGroup.EmailAlertOpen: ImageVector get() { diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/email/EmailOpen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/email/EmailOpen.kt similarity index 94% rename from shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/email/EmailOpen.kt rename to shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/email/EmailOpen.kt index cd9e7a18..2dc1fa7d 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/icons/email/EmailOpen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/otariumicons/email/EmailOpen.kt @@ -1,4 +1,4 @@ -package nl.tiebe.otarium.utils.icons.email +package nl.tiebe.otarium.utils.otariumicons.email import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.PathFillType.Companion.NonZero @@ -9,7 +9,7 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector.Builder import androidx.compose.ui.graphics.vector.path import androidx.compose.ui.unit.dp -import nl.tiebe.otarium.utils.icons.EmailGroup +import nl.tiebe.otarium.utils.otariumicons.EmailGroup public val EmailGroup.EmailOpen: ImageVector get() { diff --git a/shared/src/commonMain/resources/MR/base/strings.xml b/shared/src/commonMain/resources/MR/base/strings.xml index a6349fb0..b55e860f 100644 --- a/shared/src/commonMain/resources/MR/base/strings.xml +++ b/shared/src/commonMain/resources/MR/base/strings.xml @@ -88,4 +88,13 @@ Grade Submitted on Feedback + + Dynamic color scheme + Custom color scheme + Reset color scheme + Save color scheme + + Primary color + Secondary color + Tertiary color \ No newline at end of file From 3507fb3859dc46aa44623a99224a67c7cab42631 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Fri, 12 May 2023 17:21:19 +0200 Subject: [PATCH 60/82] strings --- shared/src/commonMain/kotlin/nl/tiebe/otarium/Main.kt | 3 +-- shared/src/commonMain/resources/MR/nl/strings.xml | 9 +++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/Main.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/Main.kt index 932e20be..9bf55b5b 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/Main.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/Main.kt @@ -25,16 +25,15 @@ lateinit var darkModeState: MutableState val safeAreaState = mutableStateOf(PaddingValues()) //todo: back gestures ios -//todo: fix colorrssss //todo: handmatig cijfers invoeren //todo: soepelheid swipen overal //todo: add settings item to show lesuitval +//todo: use tertiary color //todo: make important emails red //todo: add statistics tab in grades //todo: voldoendegrens instellen in instellingen fun setup() { - val oldVersion = settings.getInt("version", 1000) runVersionCheck(oldVersion) diff --git a/shared/src/commonMain/resources/MR/nl/strings.xml b/shared/src/commonMain/resources/MR/nl/strings.xml index 3653d35f..b44b0ba0 100644 --- a/shared/src/commonMain/resources/MR/nl/strings.xml +++ b/shared/src/commonMain/resources/MR/nl/strings.xml @@ -81,4 +81,13 @@ Kleuren + Dynamisch kleurenschema + Aangepast kleurenschema + Reset kleurenschema + Kleurenschema opslaan + + Primaire kleur + Secundaire kleur + Tertiaire kleur + \ No newline at end of file From 2a8ed39788f3ec57dd3b48399bd188ec3812712b Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Tue, 16 May 2023 13:11:16 +0200 Subject: [PATCH 61/82] quick commit --- buildSrc/src/main/kotlin/Dependencies.kt | 2 +- .../kotlin/nl/tiebe/otarium/Data.kt | 5 ++ .../component/home/StoreHomeComponent.kt | 6 +- .../home/children/StoreTimetableComponent.kt | 20 ++--- .../nl/tiebe/otarium/ui/home/BottomBar.kt | 6 +- .../nl/tiebe/otarium/ui/home/HomeComponent.kt | 5 +- .../assignments/assignment/VersionCards.kt | 6 +- .../assignment/VersionInfoScreen.kt | 4 +- .../otarium/ui/home/grades/GradeScreen.kt | 8 +- .../settings/items/ui/UIChildComponent.kt | 13 +++ .../home/settings/items/ui/UIChildScreen.kt | 14 ++++ .../home/settings/utils/SettingRowButton.kt | 4 +- .../home/timetable/TimetableRootComponent.kt | 59 +++++++++++++ .../ui/home/timetable/TimetableRootScreen.kt | 82 +++++++++++++++++++ .../ui/home/timetable/item/TimetableItem.kt | 4 +- .../home/timetable/item/TimetableItemPopup.kt | 10 ++- .../ui/home/timetable/main/DaySelector.kt | 4 +- .../ui/home/timetable/main/Timetable.kt | 1 - .../{ => main}/TimetableComponent.kt | 38 ++++----- .../ui/home/timetable/main/TimetableScreen.kt | 19 ----- .../utils/colorpicker/HarmonyColorPicker.kt | 49 +++-------- .../commonMain/resources/MR/base/strings.xml | 2 + .../commonMain/resources/MR/nl/strings.xml | 1 + 23 files changed, 243 insertions(+), 119 deletions(-) create mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/TimetableRootComponent.kt create mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/TimetableRootScreen.kt rename shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/{ => main}/TimetableComponent.kt (85%) diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt index 6aadfc36..7a5713d6 100755 --- a/buildSrc/src/main/kotlin/Dependencies.kt +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -3,7 +3,7 @@ object Version { const val appVersion = "3.3.0-alpha01" const val appVersionCode = 32 - const val magister = "1.1.8" + const val magister = "1.1.9" const val kotlin = "1.8.0" const val gradle = "7.4.1" diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/Data.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/Data.kt index d4afdf65..d3f899cd 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/Data.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/Data.kt @@ -56,4 +56,9 @@ object Data { get() = settings.getBoolean("dynamic_theme", false) set(value) = settings.putBoolean("dynamic_theme", value) + var showCancelledLessons: Boolean + get() = settings.getBoolean("show_cancelled_lessons", false) + set(value) = settings.putBoolean("show_cancelled_lessons", value) + + } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/store/component/home/StoreHomeComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/store/component/home/StoreHomeComponent.kt index 37bcc4ff..ef7efb51 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/store/component/home/StoreHomeComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/store/component/home/StoreHomeComponent.kt @@ -6,7 +6,6 @@ import com.arkivanov.decompose.router.slot.SlotNavigation import com.arkivanov.decompose.router.slot.activate import com.arkivanov.decompose.router.slot.childSlot import com.arkivanov.decompose.value.Value -import nl.tiebe.otarium.store.component.home.children.StoreTimetableComponent import nl.tiebe.otarium.store.component.home.children.grades.StoreGradeComponent import nl.tiebe.otarium.store.component.home.children.settings.StoreSettingsComponent import nl.tiebe.otarium.ui.home.HomeComponent @@ -14,6 +13,7 @@ import nl.tiebe.otarium.ui.home.MenuItemComponent import nl.tiebe.otarium.ui.home.debug.DefaultDebugComponent import nl.tiebe.otarium.ui.home.elo.DefaultELOComponent import nl.tiebe.otarium.ui.home.messages.DefaultMessagesComponent +import nl.tiebe.otarium.ui.home.timetable.DefaultTimetableRootComponent import nl.tiebe.otarium.ui.root.RootComponent class StoreHomeComponent(componentContext: ComponentContext, override val navigateRootComponent: (RootComponent.ChildScreen) -> Unit @@ -43,11 +43,11 @@ class StoreHomeComponent(componentContext: ComponentContext, override val naviga } private fun timetableComponent(componentContext: ComponentContext) = - StoreTimetableComponent( + DefaultTimetableRootComponent( componentContext = componentContext, - navigate = ::navigate ) + private fun gradesComponent(componentContext: ComponentContext) = StoreGradeComponent( componentContext = componentContext diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/store/component/home/children/StoreTimetableComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/store/component/home/children/StoreTimetableComponent.kt index 7772890c..63d8dc2e 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/store/component/home/children/StoreTimetableComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/store/component/home/children/StoreTimetableComponent.kt @@ -2,7 +2,6 @@ package nl.tiebe.otarium.store.component.home.children import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.value.MutableValue -import com.arkivanov.essenty.backhandler.BackCallback import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.datetime.* @@ -10,23 +9,20 @@ import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json import nl.tiebe.otarium.MR import nl.tiebe.otarium.magister.AgendaItemWithAbsence -import nl.tiebe.otarium.ui.home.HomeComponent -import nl.tiebe.otarium.ui.home.timetable.TimetableComponent -import nl.tiebe.otarium.ui.home.timetable.days +import nl.tiebe.otarium.ui.home.timetable.main.TimetableComponent +import nl.tiebe.otarium.ui.home.timetable.main.days import nl.tiebe.otarium.ui.root.componentCoroutineScope import nl.tiebe.otarium.utils.ui.getText import kotlin.math.floor class StoreTimetableComponent( - componentContext: ComponentContext, - navigate: (menuItem: HomeComponent.MenuItem) -> Unit + componentContext: ComponentContext ): TimetableComponent, ComponentContext by componentContext { override val now: MutableValue = MutableValue(Clock.System.now().toLocalDateTime(TimeZone.of("Europe/Amsterdam"))) override val currentPage = MutableValue(500 + now.value.date.dayOfWeek.ordinal) override val timetable: MutableValue> = MutableValue(emptyList()) - override val openedTimetableItem: MutableValue> = MutableValue(false to null) override val selectedWeek = MutableValue(floor((currentPage.value - (amountOfDays / 2).toFloat()) / days.size).toInt()) @@ -43,13 +39,15 @@ class StoreTimetableComponent( } } - override val backCallbackOpenItem: BackCallback = BackCallback(false) { - closeItemPopup() + override fun openTimeTableItem(item: AgendaItemWithAbsence) { + TODO("Not yet implemented") } - init { - backHandler.register(backCallbackOpenItem) + override fun closeItemPopup() { + TODO("Not yet implemented") + } + init { selectedWeek.subscribe { refreshSelectedWeek() } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/BottomBar.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/BottomBar.kt index 1532958e..a655b2f3 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/BottomBar.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/BottomBar.kt @@ -18,8 +18,8 @@ import nl.tiebe.otarium.ui.home.messages.MessagesComponent import nl.tiebe.otarium.ui.home.messages.MessagesScreen import nl.tiebe.otarium.ui.home.settings.SettingsComponent import nl.tiebe.otarium.ui.home.settings.SettingsScreen -import nl.tiebe.otarium.ui.home.timetable.TimetableComponent -import nl.tiebe.otarium.ui.home.timetable.main.TimetableScreen +import nl.tiebe.otarium.ui.home.timetable.TimetableRootComponent +import nl.tiebe.otarium.ui.home.timetable.TimetableRootScreen import nl.tiebe.otarium.utils.ui.getLocalizedString @OptIn(ExperimentalMaterial3Api::class) @@ -52,7 +52,7 @@ internal fun BottomBar( ) { innerPadding -> Box(Modifier.fillMaxSize().padding(innerPadding)) { when (val dialogComponent = overlay.instance) { - is TimetableComponent -> TimetableScreen(dialogComponent) + is TimetableRootComponent -> TimetableRootScreen(dialogComponent) is GradesComponent -> GradesScreen(dialogComponent) is MessagesComponent -> MessagesScreen(dialogComponent) is ELOComponent -> ELOScreen(dialogComponent) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/HomeComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/HomeComponent.kt index 0377c703..2fcf09d0 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/HomeComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/HomeComponent.kt @@ -18,7 +18,7 @@ import nl.tiebe.otarium.ui.home.elo.DefaultELOComponent import nl.tiebe.otarium.ui.home.grades.DefaultGradesComponent import nl.tiebe.otarium.ui.home.messages.DefaultMessagesComponent import nl.tiebe.otarium.ui.home.settings.DefaultSettingsComponent -import nl.tiebe.otarium.ui.home.timetable.DefaultTimetableComponent +import nl.tiebe.otarium.ui.home.timetable.DefaultTimetableRootComponent import nl.tiebe.otarium.ui.root.RootComponent import nl.tiebe.otarium.utils.OtariumIcons import nl.tiebe.otarium.utils.otariumicons.Bottombar @@ -93,9 +93,8 @@ class DefaultHomeComponent(componentContext: ComponentContext, override val navi } private fun timetableComponent(componentContext: ComponentContext) = - DefaultTimetableComponent( + DefaultTimetableRootComponent( componentContext = componentContext, - navigate = ::navigate ) private fun gradesComponent(componentContext: ComponentContext) = diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionCards.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionCards.kt index 81c91bad..344e10ff 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionCards.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionCards.kt @@ -15,8 +15,8 @@ import kotlinx.datetime.toLocalDateTime import nl.tiebe.otarium.MR import nl.tiebe.otarium.ui.utils.DownloadIndicator import nl.tiebe.otarium.ui.utils.parseHtml -import nl.tiebe.otarium.utils.otariumicons.Email import nl.tiebe.otarium.utils.OtariumIcons +import nl.tiebe.otarium.utils.otariumicons.Email import nl.tiebe.otarium.utils.otariumicons.email.Attachment import nl.tiebe.otarium.utils.toFormattedString import nl.tiebe.otarium.utils.ui.getLocalizedString @@ -53,7 +53,7 @@ internal fun MainInfoCard(assignment: Assignment, version: AssignmentVersion) { @OptIn(ExperimentalMaterial3Api::class) @Composable -internal fun TeacherFeedbackCard(component: AssignmentScreenComponent, assignment: Assignment, version: AssignmentVersion) { +internal fun TeacherFeedbackCard(component: AssignmentScreenComponent, version: AssignmentVersion) { ElevatedCard { Column { if (version.gradedOn != null) { @@ -114,7 +114,7 @@ internal fun TeacherFeedbackCard(component: AssignmentScreenComponent, assignmen @OptIn(ExperimentalMaterial3Api::class) @Composable -internal fun StudentVersionCard(component: AssignmentScreenComponent, assignment: Assignment, version: AssignmentVersion) { +internal fun StudentVersionCard(component: AssignmentScreenComponent, version: AssignmentVersion) { ElevatedCard { Column { if (version.submittedOn != null) { diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionInfoScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionInfoScreen.kt index 1466ec4e..2ee5e71c 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionInfoScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/assignment/VersionInfoScreen.kt @@ -20,13 +20,13 @@ internal fun VersionInfoScreen(component: AssignmentScreenComponent, assignment: if (version.gradedOn != null || version.grade != null || version.teacherNote != null || version.feedbackAttachments.isNotEmpty()) { Spacer(modifier = Modifier.height(10.dp)) - TeacherFeedbackCard(component, assignment, version) + TeacherFeedbackCard(component, version) } if (version.submittedOn != null || version.studentNote != null || version.studentAttachments.isNotEmpty()) { Spacer(modifier = Modifier.height(10.dp)) - StudentVersionCard(component, assignment, version) + StudentVersionCard(component, version) } } } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/GradeScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/GradeScreen.kt index b69e5118..bd60649a 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/GradeScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/GradeScreen.kt @@ -79,7 +79,13 @@ internal fun GradesScreen(component: GradesComponent) { } } - HorizontalPager(pageCount = 2, state = pagerState, beyondBoundsPageCount = 1, modifier = Modifier.fillMaxSize()) { page -> + + HorizontalPager( + pageCount = 2, + state = pagerState, + beyondBoundsPageCount = 1, + modifier = Modifier.fillMaxSize() + ) { page -> when (page) { 0 -> RecentGradesChild(component.recentGradeComponent) 1 -> GradeCalculationChild(component.calculationChildComponent) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/UIChildComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/UIChildComponent.kt index f0d3fd54..355acbab 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/UIChildComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/UIChildComponent.kt @@ -1,11 +1,18 @@ package nl.tiebe.otarium.ui.home.settings.items.ui import com.arkivanov.decompose.ComponentContext +import com.arkivanov.decompose.value.MutableValue +import com.arkivanov.decompose.value.Value +import nl.tiebe.otarium.Data import nl.tiebe.otarium.ui.home.settings.SettingsComponent interface UIChildComponent { fun navigate(child: SettingsComponent.Config) + val showCancelledLessons: Value + + fun showCancelledLessons(value: Boolean) + } class DefaultUIChildComponent( @@ -16,4 +23,10 @@ class DefaultUIChildComponent( _navigate(child) } + override val showCancelledLessons: MutableValue = MutableValue(Data.showCancelledLessons) + override fun showCancelledLessons(value: Boolean) { + Data.showCancelledLessons = value + showCancelledLessons.value = value + } + } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/UIChildScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/UIChildScreen.kt index dd196db7..4049a89d 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/UIChildScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/UIChildScreen.kt @@ -5,11 +5,13 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.text.AnnotatedString +import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState import nl.tiebe.otarium.Data import nl.tiebe.otarium.MR import nl.tiebe.otarium.ui.home.settings.SettingsComponent import nl.tiebe.otarium.ui.home.settings.utils.SettingRowIconButton import nl.tiebe.otarium.ui.home.settings.utils.SettingSlider +import nl.tiebe.otarium.ui.home.settings.utils.SettingsRowToggle import nl.tiebe.otarium.utils.OtariumIcons import nl.tiebe.otarium.utils.otariumicons.Palette import nl.tiebe.otarium.utils.ui.getLocalizedString @@ -32,6 +34,18 @@ internal fun UIChildScreen(component: UIChildComponent) { } ) + val checkedStateCancelledLessons = component.showCancelledLessons.subscribeAsState() + + //todo: change url to remove status parameter + SettingsRowToggle( + leftText = AnnotatedString(getLocalizedString(MR.strings.show_cancelled_lessons)), + checked = checkedStateCancelledLessons.value, + rowClickable = true, + onClick = { + component.showCancelledLessons(it) + } + ) + SettingSlider( text = AnnotatedString(getLocalizedString(MR.strings.decimals_slider)), value = sliderValue, diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/utils/SettingRowButton.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/utils/SettingRowButton.kt index d6b57ec2..f166dff5 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/utils/SettingRowButton.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/utils/SettingRowButton.kt @@ -18,7 +18,7 @@ import androidx.compose.ui.unit.dp @Composable internal fun SettingRowIconButton(modifier: Modifier = Modifier, leftText: AnnotatedString, textStyle: TextStyle = TextStyle(), icon: Painter, description: String = leftText.text, rowClickable: Boolean, onClick: () -> Unit) { - SettingRow(modifier = modifier, text = leftText, textStyle = textStyle) { + SettingRow(modifier = modifier.clickable(enabled = rowClickable, onClick = onClick), text = leftText, textStyle = textStyle) { Button(modifier = Modifier.width(50.dp), onClick = onClick, contentPadding = PaddingValues(0.dp)) { Icon(painter = icon, contentDescription = description, modifier = Modifier.fillMaxWidth()) } @@ -28,7 +28,7 @@ internal fun SettingRowIconButton(modifier: Modifier = Modifier, leftText: Annot @Composable internal fun SettingRowIconButton(modifier: Modifier = Modifier, leftText: AnnotatedString, textStyle: TextStyle = TextStyle(), icon: ImageVector, description: String = leftText.text, rowClickable: Boolean, onClick: () -> Unit) { SettingRowIconButton( - modifier = modifier.clickable(enabled = rowClickable, onClick = onClick), + modifier = modifier, leftText = leftText, textStyle = textStyle, icon = rememberVectorPainter(icon), diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/TimetableRootComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/TimetableRootComponent.kt new file mode 100644 index 00000000..f7ea0d7d --- /dev/null +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/TimetableRootComponent.kt @@ -0,0 +1,59 @@ +package nl.tiebe.otarium.ui.home.timetable + +import com.arkivanov.decompose.ComponentContext +import com.arkivanov.decompose.router.stack.* +import com.arkivanov.decompose.value.Value +import com.arkivanov.essenty.parcelable.Parcelable +import com.arkivanov.essenty.parcelable.Parcelize +import nl.tiebe.otarium.ui.home.MenuItemComponent +import nl.tiebe.otarium.ui.home.timetable.main.DefaultTimetableComponent +import nl.tiebe.otarium.ui.home.timetable.main.TimetableComponent + +interface TimetableRootComponent : MenuItemComponent { + val navigation: StackNavigation + val childStack: Value> + + fun navigate(child: Config) { + navigation.push(child) + } + + fun back() { + navigation.pop() + } + + sealed class Child { + class TimetableChild(val component: TimetableComponent) : Child() + class TimetablePopupChild(val component: TimetableComponent, val id: Int) : Child() + } + + sealed class Config : Parcelable { + @Parcelize + object Main : Config() + + @Parcelize + data class TimetablePopup(val id: Int) : Config() + + } + + val timetableComponent: TimetableComponent +} + +class DefaultTimetableRootComponent(componentContext: ComponentContext): TimetableRootComponent, ComponentContext by componentContext { + override val navigation = StackNavigation() + + override val timetableComponent = DefaultTimetableComponent(componentContext, ::navigate, ::back) + + override val childStack: Value> = + childStack( + source = navigation, + initialConfiguration = TimetableRootComponent.Config.Main, + handleBackButton = true, // Pop the back stack on back button press + childFactory = ::createChild, + ) + + private fun createChild(config: TimetableRootComponent.Config, @Suppress("UNUSED_PARAMETER") componentContext: ComponentContext): TimetableRootComponent.Child = + when (config) { + is TimetableRootComponent.Config.Main -> TimetableRootComponent.Child.TimetableChild(timetableComponent) + is TimetableRootComponent.Config.TimetablePopup -> TimetableRootComponent.Child.TimetablePopupChild(timetableComponent, config.id) + } +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/TimetableRootScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/TimetableRootScreen.kt new file mode 100644 index 00000000..34a2e8dc --- /dev/null +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/TimetableRootScreen.kt @@ -0,0 +1,82 @@ +package nl.tiebe.otarium.ui.home.timetable + +import androidx.compose.animation.core.FiniteAnimationSpec +import androidx.compose.animation.core.tween +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.layout.layout +import androidx.compose.ui.layout.onSizeChanged +import androidx.compose.ui.unit.toSize +import com.arkivanov.decompose.extensions.compose.jetbrains.stack.Children +import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.StackAnimator +import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.fade +import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.stackAnimation +import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.stackAnimator +import nl.tiebe.otarium.ui.home.timetable.item.TimetableItemPopup +import nl.tiebe.otarium.ui.home.timetable.main.TimetableScreen + +@Composable +internal fun TimetableRootScreen(component: TimetableRootComponent) { + TimetableScreen(component.timetableComponent) + + var size by remember { mutableStateOf(Size.Zero) } + + val offsetX = remember { mutableStateOf(0f) } + val offsetY = remember { mutableStateOf(0f) } + + + var factor by remember { mutableStateOf(0f) } + + Children( + stack = component.childStack, + animation = stackAnimation(animator = fade(), disableInputDuringAnimation = false), + modifier = Modifier.fillMaxSize() + .onSizeChanged { size = it.toSize() } + ) { + when (val child = it.instance) { + is TimetableRootComponent.Child.TimetableChild -> {} + is TimetableRootComponent.Child.TimetablePopupChild -> TimetableItemPopup(child.component, child.id, Modifier)/*.pointerInput(Unit) { + detectDragGestures(onDragStart = { position -> + offsetX.value = position.x + offsetY.value = position.y + }) { change, dragAmount -> + change.consume() + + val original = Offset(offsetX.value, offsetY.value) + val summed = original + dragAmount + val newValue = Offset( + x = summed.x.coerceIn(0f, size.width - 50.dp.toPx()), + y = summed.y.coerceIn(0f, size.height - 50.dp.toPx()) + ) + offsetX.value = newValue.x + offsetY.value = newValue.y + + factor = newValue.x / (size.width) + } + }.size(size.width.dp, size.height.dp).offset(x = offsetX.value.dp, y = offsetY.value.dp))*/ + } + } + +} + +@Composable +internal fun slide(animationSpec: FiniteAnimationSpec = tween(), factor: Float): StackAnimator { + //println(factor) + + val animator = stackAnimator(animationSpec = animationSpec) { animationFactor, _, content -> + content(Modifier.offsetXFactor(factor = maxOf(animationFactor, factor))) + } + + return animator +} + +private fun Modifier.offsetXFactor(factor: Float): Modifier = + layout { measurable, constraints -> + val placeable = measurable.measure(constraints) + + layout(placeable.width, placeable.height) { + placeable.placeRelative(x = (placeable.width.toFloat() * factor).toInt(), y = 0) + } + } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/item/TimetableItem.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/item/TimetableItem.kt index 3738c335..7a061e36 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/item/TimetableItem.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/item/TimetableItem.kt @@ -18,8 +18,8 @@ import androidx.compose.ui.unit.dp import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState import kotlinx.datetime.* import nl.tiebe.otarium.magister.getAgendaForDay -import nl.tiebe.otarium.ui.home.timetable.TimetableComponent -import nl.tiebe.otarium.ui.home.timetable.days +import nl.tiebe.otarium.ui.home.timetable.main.TimetableComponent +import nl.tiebe.otarium.ui.home.timetable.main.days import nl.tiebe.otarium.ui.theme.defaultLightTheme import nl.tiebe.otarium.ui.utils.parseHtml import nl.tiebe.otarium.ui.utils.topBottomRectBorder diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/item/TimetableItemPopup.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/item/TimetableItemPopup.kt index 43d8d6d4..6b064bc1 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/item/TimetableItemPopup.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/item/TimetableItemPopup.kt @@ -12,20 +12,22 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.rotate import androidx.compose.ui.unit.dp +import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState import kotlinx.datetime.TimeZone import kotlinx.datetime.toInstant import kotlinx.datetime.toLocalDateTime import nl.tiebe.otarium.MR -import nl.tiebe.otarium.magister.AgendaItemWithAbsence -import nl.tiebe.otarium.ui.home.timetable.TimetableComponent +import nl.tiebe.otarium.ui.home.timetable.main.TimetableComponent import nl.tiebe.otarium.ui.utils.BackButton import nl.tiebe.otarium.ui.utils.ClickableText import nl.tiebe.otarium.ui.utils.parseHtml import nl.tiebe.otarium.utils.ui.getLocalizedString @Composable -internal fun TimetableItemPopup(component: TimetableComponent, agendaItemWithAbsence: AgendaItemWithAbsence) { - Surface(Modifier.fillMaxSize()) { +internal fun TimetableItemPopup(component: TimetableComponent, agendaItemId: Int, modifier: Modifier) { + val agendaItemWithAbsence = component.timetable.subscribeAsState().value.first { it.agendaItem.id == agendaItemId } + + Surface(Modifier.fillMaxSize().then(modifier)) { Box(Modifier.fillMaxSize().padding(top = 8.dp, start = 16.dp, end = 16.dp)) { Column( modifier = Modifier diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/DaySelector.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/DaySelector.kt index 41ed0796..7b05bf9a 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/DaySelector.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/DaySelector.kt @@ -20,8 +20,6 @@ import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState import kotlinx.coroutines.launch import kotlinx.datetime.DateTimeUnit import kotlinx.datetime.plus -import nl.tiebe.otarium.ui.home.timetable.TimetableComponent -import nl.tiebe.otarium.ui.home.timetable.days import nl.tiebe.otarium.ui.utils.tabIndicatorOffset @OptIn(ExperimentalFoundationApi::class) @@ -53,7 +51,7 @@ internal fun DaySelector( selected = (dayPagerState.currentPage - (dayPageCount / 2)) % 7 == index && week == 100 + component.selectedWeek.subscribeAsState().value, onClick = { scope.launch { - dayPagerState.animateScrollToPage((week-100)*days.size + index + (component.amountOfDays / 2)) + dayPagerState.animateScrollToPage((week-100)* days.size + index + (component.amountOfDays / 2)) } }, text = { diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/Timetable.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/Timetable.kt index 350813c8..3dfc9459 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/Timetable.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/Timetable.kt @@ -20,7 +20,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.times import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState -import nl.tiebe.otarium.ui.home.timetable.TimetableComponent import nl.tiebe.otarium.ui.home.timetable.item.TimetableItem val timesShown = 8..17 diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/TimetableComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/TimetableComponent.kt similarity index 85% rename from shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/TimetableComponent.kt rename to shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/TimetableComponent.kt index e150c311..942c0ff6 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/TimetableComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/TimetableComponent.kt @@ -1,11 +1,10 @@ -package nl.tiebe.otarium.ui.home.timetable +package nl.tiebe.otarium.ui.home.timetable.main import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.pager.PagerState import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.value.MutableValue import com.arkivanov.decompose.value.Value -import com.arkivanov.essenty.backhandler.BackCallback import dev.tiebe.magisterapi.utils.MagisterException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay @@ -17,8 +16,7 @@ import nl.tiebe.otarium.magister.AgendaItemWithAbsence import nl.tiebe.otarium.magister.MagisterAccount import nl.tiebe.otarium.magister.getAbsences import nl.tiebe.otarium.magister.getMagisterAgenda -import nl.tiebe.otarium.ui.home.HomeComponent -import nl.tiebe.otarium.ui.home.MenuItemComponent +import nl.tiebe.otarium.ui.home.timetable.TimetableRootComponent import nl.tiebe.otarium.ui.root.componentCoroutineScope import nl.tiebe.otarium.utils.ui.getLocalizedString import kotlin.math.floor @@ -33,7 +31,7 @@ val days = listOf( getLocalizedString(MR.strings.sunday) ) -interface TimetableComponent : MenuItemComponent { +interface TimetableComponent { val now: Value val firstDayOfWeek get() = now.value.date.minus(now.value.date.dayOfWeek.ordinal, DateTimeUnit.DAY) val amountOfDays get() = 1000 @@ -42,8 +40,6 @@ interface TimetableComponent : MenuItemComponent { val timetable: Value> - val openedTimetableItem: Value> - val selectedWeek: Value val isRefreshingTimetable: Value @@ -82,18 +78,9 @@ interface TimetableComponent : MenuItemComponent { } } - val backCallbackOpenItem: BackCallback - - fun openTimeTableItem(item: AgendaItemWithAbsence) { - backCallbackOpenItem.isEnabled = true + fun openTimeTableItem(item: AgendaItemWithAbsence) - (openedTimetableItem as MutableValue).value = true to item - } - - fun closeItemPopup() { - (openedTimetableItem as MutableValue).value = false to openedTimetableItem.value.second - backCallbackOpenItem.isEnabled = false - } + fun closeItemPopup() @OptIn(ExperimentalFoundationApi::class) fun scrollToPage(coroutineScope: CoroutineScope, page: Int, pagerState: PagerState) { @@ -106,13 +93,13 @@ interface TimetableComponent : MenuItemComponent { class DefaultTimetableComponent( componentContext: ComponentContext, - navigate: (menuItem: HomeComponent.MenuItem) -> Unit + val navigate: (TimetableRootComponent.Config) -> Unit, + val back: () -> Unit, ): TimetableComponent, ComponentContext by componentContext { override val now: MutableValue = MutableValue(Clock.System.now().toLocalDateTime(TimeZone.of("Europe/Amsterdam"))) override val currentPage = MutableValue(500 + now.value.date.dayOfWeek.ordinal) override val timetable: MutableValue> = MutableValue(emptyList()) - override val openedTimetableItem: MutableValue> = MutableValue(false to null) override val selectedWeek = MutableValue(floor((currentPage.value - (amountOfDays / 2).toFloat()) / days.size).toInt()) @@ -167,10 +154,15 @@ class DefaultTimetableComponent( } } - override val backCallbackOpenItem: BackCallback = BackCallback(false) { - closeItemPopup() + override fun openTimeTableItem(item: AgendaItemWithAbsence) { + navigate(TimetableRootComponent.Config.TimetablePopup(item.agendaItem.id)) } + override fun closeItemPopup() { + back() + } + + @OptIn(ExperimentalFoundationApi::class) override fun scrollToPage(coroutineScope: CoroutineScope, page: Int, pagerState: PagerState) { coroutineScope.launch { @@ -181,8 +173,6 @@ class DefaultTimetableComponent( init { - backHandler.register(backCallbackOpenItem) - selectedWeek.subscribe { refreshSelectedWeek() } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/TimetableScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/TimetableScreen.kt index 7381ec77..f3a4039a 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/TimetableScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/TimetableScreen.kt @@ -1,8 +1,5 @@ package nl.tiebe.otarium.ui.home.timetable.main -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.* import androidx.compose.foundation.pager.rememberPagerState @@ -20,10 +17,7 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState import kotlinx.coroutines.launch -import nl.tiebe.otarium.ui.home.timetable.TimetableComponent -import nl.tiebe.otarium.ui.home.timetable.item.TimetableItemPopup @OptIn(ExperimentalFoundationApi::class) @@ -71,19 +65,6 @@ internal fun TimetableScreen(component: TimetableComponent) { } } - Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { - val popupItem = component.openedTimetableItem.subscribeAsState() - - AnimatedVisibility( - visible = popupItem.value.first, - enter = fadeIn(), - exit = fadeOut(), - modifier = Modifier.fillMaxSize() - ) { - TimetableItemPopup(component, popupItem.value.second!!) - } - } - var currentPage = remember { dayPagerState.currentPage } LaunchedEffect(dayPagerState.currentPage) { diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/colorpicker/HarmonyColorPicker.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/colorpicker/HarmonyColorPicker.kt index 88a443b4..78a9b091 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/colorpicker/HarmonyColorPicker.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/colorpicker/HarmonyColorPicker.kt @@ -6,49 +6,26 @@ import androidx.compose.animation.core.VectorConverter import androidx.compose.animation.core.animateDpAsState import androidx.compose.animation.core.spring import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.gestures.awaitEachGesture import androidx.compose.foundation.gestures.awaitFirstDown import androidx.compose.foundation.gestures.drag -import androidx.compose.foundation.gestures.forEachGesture -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.BoxWithConstraints -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.aspectRatio -import androidx.compose.foundation.layout.defaultMinSize -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.offset -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.wrapContentSize +import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Slider import androidx.compose.material3.SliderDefaults import androidx.compose.material3.Surface -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberUpdatedState -import androidx.compose.runtime.setValue +import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.SolidColor -import androidx.compose.ui.input.pointer.consumePositionChange import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.dp -import kotlin.math.PI -import kotlin.math.atan2 -import kotlin.math.cos -import kotlin.math.hypot -import kotlin.math.min -import kotlin.math.sin +import kotlin.math.* @Deprecated( @@ -180,17 +157,15 @@ internal fun HarmonyColorPickerWithMagnifiers( } val inputModifier = Modifier.pointerInput(diameterPx) { - forEachGesture { - awaitPointerEventScope { - val down = awaitFirstDown(false) - currentlyChangingInput = true - updateColorWheel(down.position, animate = true) - drag(down.id) { change -> - updateColorWheel(change.position, animate = false) - change.consumePositionChange() - } - currentlyChangingInput = false + awaitEachGesture { + val down = awaitFirstDown(false) + currentlyChangingInput = true + updateColorWheel(down.position, animate = true) + drag(down.id) { change -> + updateColorWheel(change.position, animate = false) + change.consume() } + currentlyChangingInput = false } } diff --git a/shared/src/commonMain/resources/MR/base/strings.xml b/shared/src/commonMain/resources/MR/base/strings.xml index b55e860f..f2ebbac7 100644 --- a/shared/src/commonMain/resources/MR/base/strings.xml +++ b/shared/src/commonMain/resources/MR/base/strings.xml @@ -97,4 +97,6 @@ Primary color Secondary color Tertiary color + + Show cancelled lessons \ No newline at end of file diff --git a/shared/src/commonMain/resources/MR/nl/strings.xml b/shared/src/commonMain/resources/MR/nl/strings.xml index b44b0ba0..da3650c2 100644 --- a/shared/src/commonMain/resources/MR/nl/strings.xml +++ b/shared/src/commonMain/resources/MR/nl/strings.xml @@ -90,4 +90,5 @@ Secundaire kleur Tertiaire kleur + Laat lesuitval zien \ No newline at end of file From 14a89f3c608cab3df18b2773ecc87f3859dda7c2 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Sat, 20 May 2023 23:33:50 +0200 Subject: [PATCH 62/82] quick commit --- build.gradle.kts | 16 ++++++++-------- buildSrc/src/main/kotlin/Dependencies.kt | 2 +- .../commonMain/kotlin/nl/tiebe/otarium/Main.kt | 1 + .../kotlin/nl/tiebe/otarium/magister/Agenda.kt | 6 ++++-- .../ui/home/timetable/main/TimetableComponent.kt | 3 ++- 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index c285c979..11d5c15d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -23,19 +23,19 @@ buildscript { allprojects { repositories { - google() - mavenCentral() - mavenLocal() - maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") - maven("https://jitpack.io") - -/* maven { + maven { url = uri("https://maven.pkg.github.com/Tiebe/MagisterAPIKt") credentials { username = project.findProperty("gpr.user") as String? ?: System.getenv("USERNAME") password = project.findProperty("gpr.key") as String? ?: System.getenv("TOKEN") } - }*/ + } + + google() + mavenCentral() + mavenLocal() + maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") + maven("https://jitpack.io") } } diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt index 7a5713d6..96b704c1 100755 --- a/buildSrc/src/main/kotlin/Dependencies.kt +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -3,7 +3,7 @@ object Version { const val appVersion = "3.3.0-alpha01" const val appVersionCode = 32 - const val magister = "1.1.9" + const val magister = "1.1.10-alpha01" const val kotlin = "1.8.0" const val gradle = "7.4.1" diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/Main.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/Main.kt index 9bf55b5b..282d7ce1 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/Main.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/Main.kt @@ -32,6 +32,7 @@ val safeAreaState = mutableStateOf(PaddingValues()) //todo: make important emails red //todo: add statistics tab in grades //todo: voldoendegrens instellen in instellingen +//todo: mappen in studiewijzers fun setup() { val oldVersion = settings.getInt("version", 1000) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/magister/Agenda.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/magister/Agenda.kt index 4dd745b6..d4daf5b1 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/magister/Agenda.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/magister/Agenda.kt @@ -10,14 +10,16 @@ suspend fun getMagisterAgenda( tenantUrl: String, accessToken: String, start: LocalDate, - end: LocalDate + end: LocalDate, + status: Int? = null, ): List { return getAgenda( Url(tenantUrl), accessToken, accountId, "${start.year}-${start.monthNumber}-${start.dayOfMonth}", - "${end.year}-${end.monthNumber}-${end.dayOfMonth}" + "${end.year}-${end.monthNumber}-${end.dayOfMonth}", + status = status ) } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/TimetableComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/TimetableComponent.kt index 942c0ff6..cae0a24e 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/TimetableComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/TimetableComponent.kt @@ -124,7 +124,8 @@ class DefaultTimetableComponent( account.tenantUrl, account.tokens.accessToken, from, - to + to, + if (Data.showCancelledLessons) null else 2 ) ) From ff8299ea94c72c4ed1fded3901d36b6c11843df3 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Sun, 21 May 2023 14:57:18 +0200 Subject: [PATCH 63/82] uitval --- buildSrc/src/main/kotlin/Dependencies.kt | 2 +- .../nl/tiebe/otarium/magister/Agenda.kt | 2 +- .../ui/home/timetable/item/TimetableItem.kt | 176 ++++++++++-------- .../home/timetable/main/TimetableComponent.kt | 3 +- 4 files changed, 98 insertions(+), 85 deletions(-) diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt index 96b704c1..bf5ca135 100755 --- a/buildSrc/src/main/kotlin/Dependencies.kt +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -3,7 +3,7 @@ object Version { const val appVersion = "3.3.0-alpha01" const val appVersionCode = 32 - const val magister = "1.1.10-alpha01" + const val magister = "1.1.10-alpha02" const val kotlin = "1.8.0" const val gradle = "7.4.1" diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/magister/Agenda.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/magister/Agenda.kt index d4daf5b1..45ea9ed7 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/magister/Agenda.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/magister/Agenda.kt @@ -11,7 +11,7 @@ suspend fun getMagisterAgenda( accessToken: String, start: LocalDate, end: LocalDate, - status: Int? = null, + status: AgendaItem.Companion.Status = AgendaItem.Companion.Status.NONE ): List { return getAgenda( Url(tenantUrl), diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/item/TimetableItem.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/item/TimetableItem.kt index 7a061e36..2af238a2 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/item/TimetableItem.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/item/TimetableItem.kt @@ -16,6 +16,7 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState +import dev.tiebe.magisterapi.response.general.year.agenda.AgendaItem import kotlinx.datetime.* import nl.tiebe.otarium.magister.getAgendaForDay import nl.tiebe.otarium.ui.home.timetable.main.TimetableComponent @@ -53,98 +54,109 @@ internal fun TimetableItem( DateTimeUnit.DAY ) // add days to get to selected day - val timeTop: Long = startOfWeekDate.atStartOfDayIn(TimeZone.of("Europe/Amsterdam")).toEpochMilliseconds() + (timesShown.first() * 60 * 60 * 1000) + val timeTop: Long = startOfWeekDate.atStartOfDayIn(TimeZone.of("Europe/Amsterdam")) + .toEpochMilliseconds() + (timesShown.first() * 60 * 60 * 1000) val timetable = component.timetable.subscribeAsState() - component.getTimetableForWeek(timetable.value, startOfWeekDate).getAgendaForDay(page - (pageWeek * days.size)).forEach { agendaItemWithAbsence -> - val agendaItem = agendaItemWithAbsence.agendaItem - val absence = agendaItemWithAbsence.absence - val startTime = - agendaItem.start.substring(0, 26).toLocalDateTime().toInstant(TimeZone.UTC) - val endTime = - agendaItem.einde.substring(0, 26).toLocalDateTime().toInstant(TimeZone.UTC) + component.getTimetableForWeek(timetable.value, startOfWeekDate).getAgendaForDay(page - (pageWeek * days.size)) + .forEach { agendaItemWithAbsence -> + val agendaItem = agendaItemWithAbsence.agendaItem + val absence = agendaItemWithAbsence.absence + val startTime = + agendaItem.start.substring(0, 26).toLocalDateTime().toInstant(TimeZone.UTC) + val endTime = + agendaItem.einde.substring(0, 26).toLocalDateTime().toInstant(TimeZone.UTC) - val localStartTime = startTime.toLocalDateTime(TimeZone.of("Europe/Amsterdam")) - val localEndTime = endTime.toLocalDateTime(TimeZone.of("Europe/Amsterdam")) + val localStartTime = startTime.toLocalDateTime(TimeZone.of("Europe/Amsterdam")) + val localEndTime = endTime.toLocalDateTime(TimeZone.of("Europe/Amsterdam")) - val height = - dpPerHour * ((endTime.toEpochMilliseconds() - startTime.toEpochMilliseconds()).toFloat() / 60 / 60 / 1000) - var distanceAfterTop = - (dpPerHour * ((startTime.toEpochMilliseconds() - timeTop).toFloat() / 60 / 60 / 1000)) - if (distanceAfterTop < 0.dp) distanceAfterTop = 0.dp + val height = + dpPerHour * ((endTime.toEpochMilliseconds() - startTime.toEpochMilliseconds()).toFloat() / 60 / 60 / 1000) + var distanceAfterTop = + (dpPerHour * ((startTime.toEpochMilliseconds() - timeTop).toFloat() / 60 / 60 / 1000)) + if (distanceAfterTop < 0.dp) distanceAfterTop = 0.dp - val supportingText = mutableListOf() + val supportingText = mutableListOf() - if (!agendaItem.location.isNullOrEmpty()) supportingText.add(AnnotatedString(agendaItem.location!!)) - supportingText.add(AnnotatedString( - "${ - localStartTime.hour.toString().padStart(2, '0') - }:${localStartTime.minute.toString().padStart(2, '0')} - ${ - localEndTime.hour.toString().padStart(2, '0') - }:${localEndTime.minute.toString().padStart(2, '0')}" - )) + if (!agendaItem.location.isNullOrEmpty()) supportingText.add(AnnotatedString(agendaItem.location!!)) + supportingText.add( + AnnotatedString( + "${ + localStartTime.hour.toString().padStart(2, '0') + }:${localStartTime.minute.toString().padStart(2, '0')} - ${ + localEndTime.hour.toString().padStart(2, '0') + }:${localEndTime.minute.toString().padStart(2, '0')}" + ) + ) - if (!agendaItem.content.isNullOrEmpty()) supportingText.add( - agendaItem.content!!.parseHtml() - ) + if (!agendaItem.content.isNullOrEmpty()) supportingText.add( + agendaItem.content!!.parseHtml() + ) - ListItem( - modifier = Modifier - .padding(start = 40.5.dp, top = distanceAfterTop) - .height(height) - .topBottomRectBorder(brush = SolidColor(MaterialTheme.colorScheme.outline)) - .clickable { component.openTimeTableItem(agendaItemWithAbsence) }, - headlineText = { Text(agendaItem.description ?: "") }, - supportingText = { - Text( - supportingText.joinToString(" • "), - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - }, - leadingContent = { - if (agendaItem.fromPeriod != null) { - Text(agendaItem.fromPeriod!!.toString(), modifier = Modifier.padding(2.dp)) - } else { - Spacer(modifier = Modifier.size(16.dp)) - } - }, - trailingContent = { - if (absence?.justified == true) { - Box( - modifier = Modifier - .clip(RoundedCornerShape(5.dp)) - .size(25.dp) - .background(Color(defaultLightTheme.secondary)), - contentAlignment = Alignment.Center - ) { - Text( - absence.code.uppercase(), - modifier = Modifier.padding(2.dp), - color = MaterialTheme.colorScheme.onSecondary - ) + ListItem( + modifier = Modifier + .padding(start = 40.5.dp, top = distanceAfterTop) + .height(height) + .topBottomRectBorder(brush = SolidColor(MaterialTheme.colorScheme.outline)) + .clickable { component.openTimeTableItem(agendaItemWithAbsence) }, + headlineText = { Text(agendaItem.description ?: "") }, + supportingText = { + Text( + supportingText.joinToString(" • "), + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + }, + leadingContent = { + if (agendaItem.fromPeriod != null) { + Text(agendaItem.fromPeriod!!.toString(), modifier = Modifier.padding(2.dp)) + } else { + Spacer(modifier = Modifier.size(16.dp)) } - } else if (absence?.justified == false) { - Box( - modifier = Modifier - .clip(RoundedCornerShape(5.dp)) - .size(25.dp) - .background(Color(defaultLightTheme.tertiary)), - contentAlignment = Alignment.Center - ) { - Text( - absence.code.uppercase(), - modifier = Modifier.padding(2.dp), - color = MaterialTheme.colorScheme.onTertiary - ) + }, + trailingContent = { + if (absence?.justified == true) { + Box( + modifier = Modifier + .clip(RoundedCornerShape(5.dp)) + .size(25.dp) + .background(Color(defaultLightTheme.secondary)), + contentAlignment = Alignment.Center + ) { + Text( + absence.code.uppercase(), + modifier = Modifier.padding(2.dp), + color = MaterialTheme.colorScheme.onSecondary + ) + } + } else if (absence?.justified == false) { + Box( + modifier = Modifier + .clip(RoundedCornerShape(5.dp)) + .size(25.dp) + .background(Color(defaultLightTheme.tertiary)), + contentAlignment = Alignment.Center + ) { + Text( + absence.code.uppercase(), + modifier = Modifier.padding(2.dp), + color = MaterialTheme.colorScheme.onTertiary + ) + } } - } - }, - colors = ListItemDefaults.colors( - containerColor = MaterialTheme.colorScheme.inverseOnSurface - ), - ) - } + }, + colors = ListItemDefaults.colors( + containerColor = if ( + agendaItem.getStatus() == AgendaItem.Companion.Status.CANCELED_AUTOMATICALLY + || agendaItem.getStatus() == AgendaItem.Companion.Status.CANCELED_MANUALLY + ) { + MaterialTheme.colorScheme.errorContainer + } else { + MaterialTheme.colorScheme.inverseOnSurface + }, + ), + ) + } } } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/TimetableComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/TimetableComponent.kt index cae0a24e..1ba41f18 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/TimetableComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/TimetableComponent.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.pager.PagerState import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.value.MutableValue import com.arkivanov.decompose.value.Value +import dev.tiebe.magisterapi.response.general.year.agenda.AgendaItem import dev.tiebe.magisterapi.utils.MagisterException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay @@ -125,7 +126,7 @@ class DefaultTimetableComponent( account.tokens.accessToken, from, to, - if (Data.showCancelledLessons) null else 2 + if (Data.showCancelledLessons) AgendaItem.Companion.Status.NONE else AgendaItem.Companion.Status.SCHEDULED_MANUALLY ) ) From 12f68ee9e046ada4ac001615f2d7cf02c6b9e481 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Sun, 21 May 2023 16:09:08 +0200 Subject: [PATCH 64/82] more fixes and changes --- shared/src/commonMain/kotlin/nl/tiebe/otarium/Main.kt | 4 ++-- .../nl/tiebe/otarium/ui/home/messages/MessageItem.kt | 10 ++++------ .../otarium/ui/home/timetable/item/TimetableItem.kt | 6 ++---- .../kotlin/nl/tiebe/otarium/ui/theme/CustomTheme.kt | 3 +++ 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/Main.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/Main.kt index 282d7ce1..2bd1832e 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/Main.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/Main.kt @@ -27,12 +27,12 @@ val safeAreaState = mutableStateOf(PaddingValues()) //todo: back gestures ios //todo: handmatig cijfers invoeren //todo: soepelheid swipen overal -//todo: add settings item to show lesuitval //todo: use tertiary color -//todo: make important emails red //todo: add statistics tab in grades //todo: voldoendegrens instellen in instellingen //todo: mappen in studiewijzers +//todo: attachments bij lessen +//todo: fix swiping grades and correctly switching weeks fun setup() { val oldVersion = settings.getInt("version", 1000) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/MessageItem.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/MessageItem.kt index 96542438..bcfae2cb 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/MessageItem.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/MessageItem.kt @@ -1,18 +1,16 @@ package nl.tiebe.otarium.ui.home.messages import androidx.compose.foundation.clickable -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Icon -import androidx.compose.material3.ListItem -import androidx.compose.material3.Text +import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import dev.tiebe.magisterapi.response.messages.Message import dev.tiebe.magisterapi.response.messages.MessageFolder +import nl.tiebe.otarium.ui.theme.red +import nl.tiebe.otarium.utils.OtariumIcons import nl.tiebe.otarium.utils.otariumicons.Bottombar import nl.tiebe.otarium.utils.otariumicons.Email import nl.tiebe.otarium.utils.otariumicons.Folder -import nl.tiebe.otarium.utils.OtariumIcons import nl.tiebe.otarium.utils.otariumicons.bottombar.EmailFilled import nl.tiebe.otarium.utils.otariumicons.email.Attachment import nl.tiebe.otarium.utils.otariumicons.email.EmailAlert @@ -42,7 +40,7 @@ internal fun MessageItem(navigateToMessage: (Message) -> Unit, message: Message) ListItem( headlineText = { Text(message.subject) }, supportingText = { Text(message.sender?.name ?: message.receivers?.joinToString { it.name } ?: "") }, - leadingContent = { Icon(icon, contentDescription = null) }, + leadingContent = { Icon(icon, contentDescription = null, tint = if (message.hasPriority) red else LocalContentColor.current) }, trailingContent = { if (message.hasAttachments) Icon(OtariumIcons.Email.Attachment, contentDescription = null) }, modifier = Modifier.clickable { navigateToMessage(message) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/item/TimetableItem.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/item/TimetableItem.kt index 2af238a2..513360c9 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/item/TimetableItem.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/item/TimetableItem.kt @@ -9,7 +9,6 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.style.TextOverflow @@ -21,7 +20,6 @@ import kotlinx.datetime.* import nl.tiebe.otarium.magister.getAgendaForDay import nl.tiebe.otarium.ui.home.timetable.main.TimetableComponent import nl.tiebe.otarium.ui.home.timetable.main.days -import nl.tiebe.otarium.ui.theme.defaultLightTheme import nl.tiebe.otarium.ui.utils.parseHtml import nl.tiebe.otarium.ui.utils.topBottomRectBorder import kotlin.math.floor @@ -121,7 +119,7 @@ internal fun TimetableItem( modifier = Modifier .clip(RoundedCornerShape(5.dp)) .size(25.dp) - .background(Color(defaultLightTheme.secondary)), + .background(MaterialTheme.colorScheme.secondary), contentAlignment = Alignment.Center ) { Text( @@ -135,7 +133,7 @@ internal fun TimetableItem( modifier = Modifier .clip(RoundedCornerShape(5.dp)) .size(25.dp) - .background(Color(defaultLightTheme.tertiary)), + .background(MaterialTheme.colorScheme.tertiary), contentAlignment = Alignment.Center ) { Text( diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/theme/CustomTheme.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/theme/CustomTheme.kt index 869fbd33..56e6c348 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/theme/CustomTheme.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/theme/CustomTheme.kt @@ -30,6 +30,9 @@ data class CustomTheme( } } +val red = Color(0xFFC40808) +val green = Color(0xFF1FC43B) + val defaultLightTheme = CustomTheme( Color(0xFF6ACBF0).toArgb(), Color(0xFF1FC43B).toArgb(), From 53cc3c30e9097ad22bb866d8f400b7a12d0c3fb3 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Mon, 22 May 2023 11:39:17 +0200 Subject: [PATCH 65/82] ui changes --- .../tiebe/otarium/androidApp/MainActivity.kt | 19 +++++++++----- .../nl/tiebe/otarium/RootViewAndroid.kt | 5 ++-- .../kotlin/nl/tiebe/otarium/Main.kt | 18 ++++++------- .../nl/tiebe/otarium/ui/home/BottomBar.kt | 8 +++--- .../nl/tiebe/otarium/ui/home/HomeComponent.kt | 25 +++++++++---------- .../ui/home/messages/MessagesScreen.kt | 8 +++--- .../ui/home/settings/SettingsScreen.kt | 4 ++- .../home/timetable/main/TimetableComponent.kt | 2 +- 8 files changed, 46 insertions(+), 43 deletions(-) diff --git a/androidApp/src/main/java/nl/tiebe/otarium/androidApp/MainActivity.kt b/androidApp/src/main/java/nl/tiebe/otarium/androidApp/MainActivity.kt index 3c81c27b..6ae230fe 100644 --- a/androidApp/src/main/java/nl/tiebe/otarium/androidApp/MainActivity.kt +++ b/androidApp/src/main/java/nl/tiebe/otarium/androidApp/MainActivity.kt @@ -2,14 +2,18 @@ package nl.tiebe.otarium.androidApp import android.content.Context import android.content.res.Configuration -import android.graphics.Color import android.os.Build import android.os.Bundle +import android.view.WindowManager import androidx.activity.compose.setContent import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.asPaddingValues +import androidx.compose.foundation.layout.systemBars import androidx.compose.material3.dynamicDarkColorScheme import androidx.compose.material3.dynamicLightColorScheme +import androidx.core.view.WindowCompat import com.arkivanov.decompose.defaultComponentContext import nl.tiebe.otarium.RootView import nl.tiebe.otarium.utils.refreshGradesBackground @@ -30,10 +34,6 @@ class MainActivity : AppCompatActivity() { Android.requestPermissionLauncher = requestPermissionLauncher Android.window = window - // has to be set in code or in theme - window.decorView.setBackgroundColor(Color.WHITE) - window.statusBarColor = Color.parseColor("#0F86E4") - reloadTokensBackground() refreshGradesBackground() @@ -53,9 +53,16 @@ class MainActivity : AppCompatActivity() { } else -> null } + WindowCompat.setDecorFitsSystemWindows(window, false) + + + window.setFlags( + WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, + WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS + ) setContent { - RootView(rootComponentContext, colorScheme) + RootView(rootComponentContext, colorScheme, WindowInsets.Companion.systemBars.asPaddingValues()) } } diff --git a/shared/src/androidMain/kotlin/nl/tiebe/otarium/RootViewAndroid.kt b/shared/src/androidMain/kotlin/nl/tiebe/otarium/RootViewAndroid.kt index ae284135..888053ff 100644 --- a/shared/src/androidMain/kotlin/nl/tiebe/otarium/RootViewAndroid.kt +++ b/shared/src/androidMain/kotlin/nl/tiebe/otarium/RootViewAndroid.kt @@ -1,14 +1,15 @@ package nl.tiebe.otarium +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.material3.ColorScheme import androidx.compose.runtime.Composable import com.arkivanov.decompose.DefaultComponentContext @Composable -fun RootView(rootComponentContext: DefaultComponentContext, colorScheme: ColorScheme?) { +fun RootView(rootComponentContext: DefaultComponentContext, colorScheme: ColorScheme?, padding: PaddingValues) { ProvideComponentContext(rootComponentContext) { setup() - Content(componentContext = rootComponentContext, colorScheme = colorScheme) + Content(componentContext = rootComponentContext, colorScheme = colorScheme, padding = padding) } } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/Main.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/Main.kt index 2bd1832e..fc169e09 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/Main.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/Main.kt @@ -2,7 +2,9 @@ package nl.tiebe.otarium import androidx.compose.foundation.background import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.ColorScheme import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface @@ -22,7 +24,6 @@ import nl.tiebe.otarium.utils.versions.runVersionCheck val settings: Settings = Settings() lateinit var darkModeState: MutableState -val safeAreaState = mutableStateOf(PaddingValues()) //todo: back gestures ios //todo: handmatig cijfers invoeren @@ -44,12 +45,12 @@ fun setup() { } @Composable -internal fun Content(componentContext: ComponentContext, colorScheme: ColorScheme? = null) { - Content(component = DefaultRootComponent(componentContext), colorScheme = colorScheme) +internal fun Content(componentContext: ComponentContext, colorScheme: ColorScheme? = null, padding: PaddingValues) { + Content(component = DefaultRootComponent(componentContext), colorScheme = colorScheme, padding = padding) } @Composable -internal fun Content(component: RootComponent, colorScheme: ColorScheme? = null) { +internal fun Content(component: RootComponent, colorScheme: ColorScheme? = null, padding: PaddingValues) { darkModeState = mutableStateOf(isSystemInDarkTheme()) OtariumTheme(colorScheme) { @@ -57,12 +58,7 @@ internal fun Content(component: RootComponent, colorScheme: ColorScheme? = null) modifier = Modifier.fillMaxSize() .background(MaterialTheme.colorScheme.primary) ) {} - Box( - modifier = Modifier.fillMaxHeight(0.5f).fillMaxWidth() - .background(MaterialTheme.colorScheme.background) - ) {} - Box(modifier = Modifier.padding(safeAreaState.value)) { - + Box() { val currentScreen by component.currentScreen.subscribeAsState() Surface( diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/BottomBar.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/BottomBar.kt index a655b2f3..798ec07e 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/BottomBar.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/BottomBar.kt @@ -33,18 +33,18 @@ internal fun BottomBar( Scaffold( bottomBar = { - NavigationBar(modifier = modifier, contentColor = MaterialTheme.colorScheme.onPrimary, containerColor = MaterialTheme.colorScheme.primary) { + NavigationBar(modifier = modifier, /*contentColor = MaterialTheme.colorScheme.onPrimary, containerColor = MaterialTheme.colorScheme.primary*/) { component.visibleItems.forEach { screen -> NavigationBarItem( icon = if (overlay.configuration == screen) screen.iconSelected else screen.icon, - label = { Text(getLocalizedString(screen.resourceId), modifier = Modifier.wrapContentWidth(unbounded = true), color = MaterialTheme.colorScheme.onPrimary) }, + label = { Text(getLocalizedString(screen.resourceId), modifier = Modifier.wrapContentWidth(unbounded = true)/*, color = MaterialTheme.colorScheme.onPrimary*/) }, selected = overlay.configuration == screen, onClick = { component.navigate(screen) }, - colors = NavigationBarItemDefaults.colors( +/* colors = NavigationBarItemDefaults.colors( indicatorColor = MaterialTheme.colorScheme.secondaryContainer, - ) + )*/ ) } } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/HomeComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/HomeComponent.kt index 2fcf09d0..fb38ccb1 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/HomeComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/HomeComponent.kt @@ -1,7 +1,6 @@ package nl.tiebe.otarium.ui.home import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.router.slot.ChildSlot @@ -33,38 +32,38 @@ interface HomeComponent { sealed class MenuItem(val resourceId: StringResource, val icon: @Composable () -> Unit, val iconSelected: @Composable () -> Unit): Parcelable { object Timetable: MenuItem( MR.strings.agendaItem, - { Icon(OtariumIcons.Bottombar.CalendarTodayOutline, "Timetable", tint = MaterialTheme.colorScheme.onPrimary) }, - { Icon(OtariumIcons.Bottombar.CalendarTodayFilled, "Timetable", tint = MaterialTheme.colorScheme.onSecondaryContainer) }, + { Icon(OtariumIcons.Bottombar.CalendarTodayOutline, "Timetable") }, + { Icon(OtariumIcons.Bottombar.CalendarTodayFilled, "Timetable") }, ) object Grades: MenuItem( MR.strings.gradesItem, - { Icon(OtariumIcons.Bottombar.Box10Outline, "Grades", tint = MaterialTheme.colorScheme.onPrimary) }, - { Icon(OtariumIcons.Bottombar.Box10Filled, "Grades", tint = MaterialTheme.colorScheme.onSecondaryContainer) }, + { Icon(OtariumIcons.Bottombar.Box10Outline, "Grades") }, + { Icon(OtariumIcons.Bottombar.Box10Filled, "Grades") }, ) object Messages: MenuItem( MR.strings.messagesItem, - { Icon(OtariumIcons.Bottombar.EmailOutline, "Messages", tint = MaterialTheme.colorScheme.onPrimary) }, - { Icon(OtariumIcons.Bottombar.EmailFilled, "Messages", tint = MaterialTheme.colorScheme.onSecondaryContainer) }, + { Icon(OtariumIcons.Bottombar.EmailOutline, "Messages") }, + { Icon(OtariumIcons.Bottombar.EmailFilled, "Messages") }, ) object ELO: MenuItem( MR.strings.eloItem, - { Icon(OtariumIcons.Bottombar.BookOpenOutline, "ELO", tint = MaterialTheme.colorScheme.onPrimary) }, - { Icon(OtariumIcons.Bottombar.BookOpenFilled, "ELO", tint = MaterialTheme.colorScheme.onSecondaryContainer) }, + { Icon(OtariumIcons.Bottombar.BookOpenOutline, "ELO") }, + { Icon(OtariumIcons.Bottombar.BookOpenFilled, "ELO") }, ) object Settings: MenuItem( MR.strings.settingsItem, - { Icon(OtariumIcons.Bottombar.CogOutline, "Settings", tint = MaterialTheme.colorScheme.onPrimary) }, - { Icon(OtariumIcons.Bottombar.CogFilled, "Settings", tint = MaterialTheme.colorScheme.onSecondaryContainer) }, + { Icon(OtariumIcons.Bottombar.CogOutline, "Settings") }, + { Icon(OtariumIcons.Bottombar.CogFilled, "Settings") }, ) object Debug: MenuItem( MR.strings.settingsItem, - { Icon(OtariumIcons.Bottombar.Box10Outline, "Debug", tint = MaterialTheme.colorScheme.onPrimary) }, - { Icon(OtariumIcons.Bottombar.Box10Filled, "Debug", tint = MaterialTheme.colorScheme.onSecondaryContainer) }, + { Icon(OtariumIcons.Bottombar.Box10Outline, "Debug") }, + { Icon(OtariumIcons.Bottombar.Box10Filled, "Debug") }, ) } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/MessagesScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/MessagesScreen.kt index c89cc7e6..03b2132e 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/MessagesScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/MessagesScreen.kt @@ -1,9 +1,6 @@ package nl.tiebe.otarium.ui.home.messages -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.* import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack @@ -44,7 +41,8 @@ internal fun MessagesScreen(component: MessagesComponent) { Icon(Icons.Default.ArrowBack, contentDescription = "Back") } } - } + }, + windowInsets = WindowInsets(0.dp) ) Box(modifier = Modifier.padding(start = 5.dp, end = 5.dp)) { diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/SettingsScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/SettingsScreen.kt index 34238111..80360778 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/SettingsScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/SettingsScreen.kt @@ -2,6 +2,7 @@ package nl.tiebe.otarium.ui.home.settings import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack @@ -30,7 +31,8 @@ internal fun SettingsScreen(component: SettingsComponent) { IconButton(onClick = { component.back() }) { Icon(Icons.Default.ArrowBack, contentDescription = "Back") } - } + }, + windowInsets = WindowInsets(0.dp) ) } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/TimetableComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/TimetableComponent.kt index 1ba41f18..968c54e6 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/TimetableComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/TimetableComponent.kt @@ -126,7 +126,7 @@ class DefaultTimetableComponent( account.tokens.accessToken, from, to, - if (Data.showCancelledLessons) AgendaItem.Companion.Status.NONE else AgendaItem.Companion.Status.SCHEDULED_MANUALLY + if (Data.showCancelledLessons) AgendaItem.Companion.Status.NONE else AgendaItem.Companion.Status.SCHEDULED_AUTOMATICALLY ) ) From ed3e98177a392ce50d7ae64e65a8c8296bc00d1c Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Mon, 22 May 2023 15:31:49 +0200 Subject: [PATCH 66/82] changes --- .../nl/tiebe/otarium/ui/theme/ThemeAndroid.kt | 8 --- .../kotlin/nl/tiebe/otarium/Main.kt | 8 +-- .../otarium/ui/home/grades/GradeScreen.kt | 2 +- .../grades/calculation/cards/graph/GCGraph.kt | 56 +++++++-------- .../home/timetable/TimetableRootComponent.kt | 22 +++++- .../ui/home/timetable/TimetableRootScreen.kt | 71 +++++++++---------- .../otarium/ui/onboarding/OnboardingScreen.kt | 66 +++++++++-------- .../kotlin/nl/tiebe/otarium/ui/theme/Theme.kt | 10 +-- .../nl/tiebe/otarium/ui/utils/HtmlHandler.kt | 3 +- .../tiebe/otarium/utils/versions/Versions.kt | 4 ++ .../nl/tiebe/otarium/RootViewControllers.kt | 14 +--- .../tiebe/otarium/ui/theme/setWindowTheme.kt | 7 +- 12 files changed, 134 insertions(+), 137 deletions(-) diff --git a/shared/src/androidMain/kotlin/nl/tiebe/otarium/ui/theme/ThemeAndroid.kt b/shared/src/androidMain/kotlin/nl/tiebe/otarium/ui/theme/ThemeAndroid.kt index 30ca5336..ce700f7f 100644 --- a/shared/src/androidMain/kotlin/nl/tiebe/otarium/ui/theme/ThemeAndroid.kt +++ b/shared/src/androidMain/kotlin/nl/tiebe/otarium/ui/theme/ThemeAndroid.kt @@ -1,17 +1,9 @@ package nl.tiebe.otarium.ui.theme -import android.os.Build -import androidx.compose.material3.ColorScheme -import androidx.compose.material3.dynamicDarkColorScheme -import androidx.compose.material3.dynamicLightColorScheme -import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb -import androidx.compose.ui.platform.LocalContext -import nl.tiebe.otarium.darkModeState import nl.tiebe.otarium.utils.ui.Android actual fun setWindowTheme(color: Color) { Android.window.statusBarColor = color.toArgb() - } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/Main.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/Main.kt index fc169e09..dca4aca4 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/Main.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/Main.kt @@ -1,7 +1,6 @@ package nl.tiebe.otarium import androidx.compose.foundation.background -import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize @@ -23,8 +22,6 @@ import nl.tiebe.otarium.utils.versions.runVersionCheck val settings: Settings = Settings() -lateinit var darkModeState: MutableState - //todo: back gestures ios //todo: handmatig cijfers invoeren //todo: soepelheid swipen overal @@ -33,7 +30,8 @@ lateinit var darkModeState: MutableState //todo: voldoendegrens instellen in instellingen //todo: mappen in studiewijzers //todo: attachments bij lessen -//todo: fix swiping grades and correctly switching weeks +//todo: fix swiping days and correctly switching weeks +//todo: laat cijfer info zien bij hover over grafiek fun setup() { val oldVersion = settings.getInt("version", 1000) @@ -51,8 +49,6 @@ internal fun Content(componentContext: ComponentContext, colorScheme: ColorSchem @Composable internal fun Content(component: RootComponent, colorScheme: ColorScheme? = null, padding: PaddingValues) { - darkModeState = mutableStateOf(isSystemInDarkTheme()) - OtariumTheme(colorScheme) { Box( modifier = Modifier.fillMaxSize() diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/GradeScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/GradeScreen.kt index bd60649a..91fe8b8b 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/GradeScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/GradeScreen.kt @@ -84,7 +84,7 @@ internal fun GradesScreen(component: GradesComponent) { pageCount = 2, state = pagerState, beyondBoundsPageCount = 1, - modifier = Modifier.fillMaxSize() + modifier = Modifier.fillMaxSize(), ) { page -> when (page) { 0 -> RecentGradesChild(component.recentGradeComponent) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/cards/graph/GCGraph.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/cards/graph/GCGraph.kt index 7626d247..70832d66 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/cards/graph/GCGraph.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/cards/graph/GCGraph.kt @@ -44,8 +44,8 @@ internal fun GCGraph(grades: List) { textAlign = TextAlign.Center ) val textColor = MaterialTheme.colorScheme.onBackground - val lineColor = MaterialTheme.colorScheme.primary - val averageColor = MaterialTheme.colorScheme.secondary + val gradeLineColor = MaterialTheme.colorScheme.secondary + val averageLineColor = MaterialTheme.colorScheme.tertiary val axisLineColor = MaterialTheme.colorScheme.outline val lineBound = remember { mutableStateOf(1F) } @@ -69,21 +69,41 @@ internal fun GCGraph(grades: List) { ) { lineBound.value = size.width / grades.count() * 0.8f - val gradeBrush = Brush.linearGradient(listOf(lineColor, lineColor)) + val gradeBrush = Brush.linearGradient(listOf(gradeLineColor, gradeLineColor)) val gradePath = Path().apply { moveTo(0f, size.height) } + val averageBrush = Brush.linearGradient(listOf(averageLineColor, averageLineColor)) + val averagePath = Path().apply { moveTo(0f, size.height) } + grades.forEachIndexed { index, gradeInfo -> val grade = gradeInfo.grade.grade?.replace(',', '.')?.toFloatOrNull() ?: 0f + val average = calculateAverage(grades.subList(0, index+1)) + + val gradeOffset = Offset((2*index+1) * lineBound.value * 0.6f, size.height - grade * (size.height / 10)) + val averageOffset = Offset((2*index+1) * lineBound.value * 0.6f, size.height - average * (size.height / 10)) - val offset = Offset((2*index+1) * lineBound.value * 0.6f, size.height - grade * (size.height / 10)) if (grades.size > 1) { when (index) { - 0 -> gradePath.moveTo(offset.x, offset.y) - else -> gradePath.lineTo(offset.x, offset.y) + 0 -> { + gradePath.moveTo(gradeOffset.x, gradeOffset.y) + averagePath.moveTo(averageOffset.x, averageOffset.y) + } + else -> { + gradePath.lineTo(gradeOffset.x, gradeOffset.y) + averagePath.lineTo(averageOffset.x, averageOffset.y) + } + } } + + drawCircle( + center = averageOffset, + radius = size.width / 70, + brush = averageBrush + ) + drawCircle( - center = offset, + center = gradeOffset, radius = size.width / 70, brush = gradeBrush ) @@ -94,29 +114,7 @@ internal fun GCGraph(grades: List) { brush = gradeBrush, style = Stroke(width = size.width / 100), ) - } - - val averageBrush = Brush.linearGradient(listOf(averageColor, averageColor)) - val averagePath = Path().apply { moveTo(0f, size.height) } - grades.forEachIndexed { index, _ -> - val average = calculateAverage(grades.subList(0, index+1)) - - val offset = Offset((2*index+1) * lineBound.value * 0.6f, size.height - average * (size.height / 10)) - if (grades.size > 1) { - when (index) { - 0 -> averagePath.moveTo(offset.x, offset.y) - else -> averagePath.lineTo(offset.x, offset.y) - } - } - drawCircle( - center = offset, - radius = size.width / 70, - brush = averageBrush - ) - } - - if (grades.size > 1) { drawPath( path = averagePath, brush = averageBrush, diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/TimetableRootComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/TimetableRootComponent.kt index f7ea0d7d..6cd09cd8 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/TimetableRootComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/TimetableRootComponent.kt @@ -2,7 +2,9 @@ package nl.tiebe.otarium.ui.home.timetable import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.router.stack.* +import com.arkivanov.decompose.value.MutableValue import com.arkivanov.decompose.value.Value +import com.arkivanov.essenty.backhandler.BackCallback import com.arkivanov.essenty.parcelable.Parcelable import com.arkivanov.essenty.parcelable.Parcelize import nl.tiebe.otarium.ui.home.MenuItemComponent @@ -13,6 +15,11 @@ interface TimetableRootComponent : MenuItemComponent { val navigation: StackNavigation val childStack: Value> + val onBack: MutableValue + + fun registerBackHandler() + fun unregisterBackHandler() + fun navigate(child: Config) { navigation.push(child) } @@ -47,13 +54,26 @@ class DefaultTimetableRootComponent(componentContext: ComponentContext): Timetab childStack( source = navigation, initialConfiguration = TimetableRootComponent.Config.Main, - handleBackButton = true, // Pop the back stack on back button press + handleBackButton = false, // Pop the back stack on back button press childFactory = ::createChild, ) + override val onBack: MutableValue = MutableValue(BackCallback { back() }) + override fun registerBackHandler() { + backHandler.register(onBack.value) + } + + override fun unregisterBackHandler() { + backHandler.unregister(onBack.value) + } + private fun createChild(config: TimetableRootComponent.Config, @Suppress("UNUSED_PARAMETER") componentContext: ComponentContext): TimetableRootComponent.Child = when (config) { is TimetableRootComponent.Config.Main -> TimetableRootComponent.Child.TimetableChild(timetableComponent) is TimetableRootComponent.Config.TimetablePopup -> TimetableRootComponent.Child.TimetablePopupChild(timetableComponent, config.id) } + + init { + backHandler.register(onBack.value) + } } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/TimetableRootScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/TimetableRootScreen.kt index 34a2e8dc..89fd8d8e 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/TimetableRootScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/TimetableRootScreen.kt @@ -2,60 +2,53 @@ package nl.tiebe.otarium.ui.home.timetable import androidx.compose.animation.core.FiniteAnimationSpec import androidx.compose.animation.core.tween -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.runtime.* +import androidx.compose.material.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier -import androidx.compose.ui.geometry.Size import androidx.compose.ui.layout.layout -import androidx.compose.ui.layout.onSizeChanged -import androidx.compose.ui.unit.toSize -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.Children import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.StackAnimator -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.fade -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.stackAnimation import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.stackAnimator +import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState +import com.arkivanov.essenty.backhandler.BackCallback +import kotlinx.coroutines.launch import nl.tiebe.otarium.ui.home.timetable.item.TimetableItemPopup import nl.tiebe.otarium.ui.home.timetable.main.TimetableScreen +@OptIn(ExperimentalMaterialApi::class) @Composable internal fun TimetableRootScreen(component: TimetableRootComponent) { - TimetableScreen(component.timetableComponent) + val child = component.childStack.subscribeAsState().value.active.instance + val state = rememberDismissState(DismissValue.DismissedToEnd) + val scope = rememberCoroutineScope() - var size by remember { mutableStateOf(Size.Zero) } + LaunchedEffect(child) { + if (child is TimetableRootComponent.Child.TimetablePopupChild) { + component.onBack.value = BackCallback { scope.launch { state.animateTo(DismissValue.DismissedToEnd) } } + component.registerBackHandler() - val offsetX = remember { mutableStateOf(0f) } - val offsetY = remember { mutableStateOf(0f) } + state.animateTo(DismissValue.Default, tween()) + } + } + LaunchedEffect(state.currentValue) { + if (state.currentValue != DismissValue.Default) { + component.back() + component.unregisterBackHandler() + } + } - var factor by remember { mutableStateOf(0f) } + TimetableScreen(component.timetableComponent) - Children( - stack = component.childStack, - animation = stackAnimation(animator = fade(), disableInputDuringAnimation = false), - modifier = Modifier.fillMaxSize() - .onSizeChanged { size = it.toSize() } + SwipeToDismiss( + state = state, + dismissThresholds = { FractionalThreshold(0.5f) }, + background = { + } ) { - when (val child = it.instance) { - is TimetableRootComponent.Child.TimetableChild -> {} - is TimetableRootComponent.Child.TimetablePopupChild -> TimetableItemPopup(child.component, child.id, Modifier)/*.pointerInput(Unit) { - detectDragGestures(onDragStart = { position -> - offsetX.value = position.x - offsetY.value = position.y - }) { change, dragAmount -> - change.consume() - - val original = Offset(offsetX.value, offsetY.value) - val summed = original + dragAmount - val newValue = Offset( - x = summed.x.coerceIn(0f, size.width - 50.dp.toPx()), - y = summed.y.coerceIn(0f, size.height - 50.dp.toPx()) - ) - offsetX.value = newValue.x - offsetY.value = newValue.y - - factor = newValue.x / (size.width) - } - }.size(size.width.dp, size.height.dp).offset(x = offsetX.value.dp, y = offsetY.value.dp))*/ + if (child is TimetableRootComponent.Child.TimetablePopupChild) { + TimetableItemPopup(child.component, child.id, Modifier) } } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/onboarding/OnboardingScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/onboarding/OnboardingScreen.kt index 0ec018e6..5edd766b 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/onboarding/OnboardingScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/onboarding/OnboardingScreen.kt @@ -7,6 +7,8 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier @@ -15,45 +17,47 @@ import nl.tiebe.otarium.ui.onboarding.sections.BottomSection import nl.tiebe.otarium.ui.onboarding.sections.TopSection -@OptIn(ExperimentalFoundationApi::class) +@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class) @Composable internal fun OnboardingScreen(component: OnboardingComponent) { val items = OnboardingItems.getData() val scope = rememberCoroutineScope() val pageState = rememberPagerState() - Column(modifier = Modifier.fillMaxSize()) { - TopSection( - onBackClick = { - if (pageState.currentPage + 1 > 1) scope.launch { - pageState.scrollToPage(pageState.currentPage - 1) + Scaffold { + Column(modifier = Modifier.fillMaxSize()) { + TopSection( + onBackClick = { + if (pageState.currentPage + 1 > 1) scope.launch { + pageState.scrollToPage(pageState.currentPage - 1) + } + }, + onSkipClick = { + if (pageState.currentPage + 1 < items.size) scope.launch { + pageState.scrollToPage(items.size - 1) + } } - }, - onSkipClick = { - if (pageState.currentPage + 1 < items.size) scope.launch { - pageState.scrollToPage(items.size - 1) - } - } - ) + ) - HorizontalPager( - pageCount = items.size, - state = pageState, - modifier = Modifier - .fillMaxHeight(0.9f) - .fillMaxWidth() - ) { page -> - OnboardingMenuItem(items = items[page]) - } - BottomSection(size = items.size, index = pageState.currentPage) { - if (pageState.currentPage + 1 < items.size) { - scope.launch { - pageState.animateScrollToPage(pageState.currentPage + 1) - } - } else { - scope.launch { - component.notifications() - component.exitOnboarding() + HorizontalPager( + pageCount = items.size, + state = pageState, + modifier = Modifier + .fillMaxHeight(0.9f) + .fillMaxWidth() + ) { page -> + OnboardingMenuItem(items = items[page]) + } + BottomSection(size = items.size, index = pageState.currentPage) { + if (pageState.currentPage + 1 < items.size) { + scope.launch { + pageState.animateScrollToPage(pageState.currentPage + 1) + } + } else { + scope.launch { + component.notifications() + component.exitOnboarding() + } } } } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/theme/Theme.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/theme/Theme.kt index 1e88c306..4347ba98 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/theme/Theme.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/theme/Theme.kt @@ -7,24 +7,24 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.graphics.Color import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState import nl.tiebe.otarium.Data -import nl.tiebe.otarium.darkModeState import nl.tiebe.otarium.ui.home.settings.items.ui.colors.colorSchemeChanged @Composable internal fun OtariumTheme( colorScheme: ColorScheme? = null, + darkMode: Boolean = false, content: @Composable () -> Unit ) { var selectedColorScheme = if (Data.dynamicTheme && colorScheme != null) { colorScheme } else if (Data.customThemeEnabled) { when { - darkModeState.value -> Data.customDarkTheme.toDarkColorScheme() + darkMode -> Data.customDarkTheme.toDarkColorScheme() else -> Data.customLightTheme.toLightColorScheme() } } else { when { - darkModeState.value -> defaultDarkTheme.toDarkColorScheme() + darkMode -> defaultDarkTheme.toDarkColorScheme() else -> defaultLightTheme.toLightColorScheme() } } @@ -34,12 +34,12 @@ internal fun OtariumTheme( colorScheme } else if (Data.customThemeEnabled) { when { - darkModeState.value -> Data.customDarkTheme.toDarkColorScheme() + darkMode -> Data.customDarkTheme.toDarkColorScheme() else -> Data.customLightTheme.toLightColorScheme() } } else { when { - darkModeState.value -> defaultDarkTheme.toDarkColorScheme() + darkMode -> defaultDarkTheme.toDarkColorScheme() else -> defaultLightTheme.toLightColorScheme() } } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/HtmlHandler.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/HtmlHandler.kt index 6e70b670..81ac9b55 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/HtmlHandler.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/utils/HtmlHandler.kt @@ -12,7 +12,6 @@ import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.text.style.TextIndent import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.times -import nl.tiebe.otarium.darkModeState import kotlin.math.min private val tags = listOf( @@ -40,7 +39,7 @@ private val tags = listOf( Tag("
      ", "
    ", specialOpeningAction = { htmlList = true }, specialClosingAction = { htmlList = null }), Tag("
      ", "
    ", specialOpeningAction = { listIndex = 0; htmlList = false }, specialClosingAction = { htmlList = null }), - Tag("
    ", regex = "", regexAction = { to, value -> to.pushStringAnnotation("URL", value) }, spanStyle = SpanStyle(color = if (!darkModeState.value) Color(0, 0, 238) else Color(83, 155, 245), textDecoration = TextDecoration.Underline)), + Tag("", regex = "", regexAction = { to, value -> to.pushStringAnnotation("URL", value) }, spanStyle = SpanStyle(color = Color(83, 155, 245), textDecoration = TextDecoration.Underline)), Tag("
    ", "", spanStyle = SpanStyle(letterSpacing = 0.sp)), diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/versions/Versions.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/versions/Versions.kt index d88c7a0f..5780e540 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/versions/Versions.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/versions/Versions.kt @@ -20,4 +20,8 @@ fun runVersionCheck(oldVersion: Int) { if (oldVersion <= 21) { migrateFromV21() } + + if (oldVersion <= 31) { + settings.clear() + } } \ No newline at end of file diff --git a/shared/src/iosMain/kotlin/nl/tiebe/otarium/RootViewControllers.kt b/shared/src/iosMain/kotlin/nl/tiebe/otarium/RootViewControllers.kt index f04635de..01da1f9c 100644 --- a/shared/src/iosMain/kotlin/nl/tiebe/otarium/RootViewControllers.kt +++ b/shared/src/iosMain/kotlin/nl/tiebe/otarium/RootViewControllers.kt @@ -10,7 +10,7 @@ import org.jetbrains.skiko.currentSystemTheme import platform.CoreGraphics.CGFloat import platform.UIKit.UIViewController -fun RootViewController(): UIViewController = ComposeUIViewController { +fun RootViewController(startPadding: CGFloat, topPadding: CGFloat, endPadding: CGFloat, bottomPadding: CGFloat): UIViewController = ComposeUIViewController { val componentContext = DefaultComponentContext( lifecycle = LifecycleRegistry(), null, null, null @@ -19,14 +19,6 @@ fun RootViewController(): UIViewController = ComposeUIViewController { ProvideComponentContext(componentContext) { setup() - Content(componentContext = componentContext) + Content(componentContext = componentContext, padding = PaddingValues(startPadding.dp, topPadding.dp, endPadding.dp, bottomPadding.dp)) } -} - -fun setSafeArea(start: CGFloat, top: CGFloat, end: CGFloat, bottom: CGFloat) { - safeAreaState.value = PaddingValues(start.dp, top.dp, end.dp, bottom.dp) -} - -fun setDarkMode() { - darkModeState.value = currentSystemTheme == SystemTheme.DARK -} +} \ No newline at end of file diff --git a/shared/src/iosMain/kotlin/nl/tiebe/otarium/ui/theme/setWindowTheme.kt b/shared/src/iosMain/kotlin/nl/tiebe/otarium/ui/theme/setWindowTheme.kt index 00daa6f1..550e4800 100644 --- a/shared/src/iosMain/kotlin/nl/tiebe/otarium/ui/theme/setWindowTheme.kt +++ b/shared/src/iosMain/kotlin/nl/tiebe/otarium/ui/theme/setWindowTheme.kt @@ -1,14 +1,13 @@ package nl.tiebe.otarium.ui.theme -import androidx.compose.material3.ColorScheme -import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color -import nl.tiebe.otarium.darkModeState +import org.jetbrains.skiko.SystemTheme +import org.jetbrains.skiko.currentSystemTheme import platform.UIKit.UIApplication import platform.UIKit.UIStatusBarStyleDarkContent import platform.UIKit.UIStatusBarStyleLightContent import platform.UIKit.setStatusBarStyle actual fun setWindowTheme(color: Color) { - UIApplication.sharedApplication.setStatusBarStyle(if(darkModeState.value) UIStatusBarStyleLightContent else UIStatusBarStyleDarkContent) + UIApplication.sharedApplication.setStatusBarStyle(if(currentSystemTheme == SystemTheme.DARK) UIStatusBarStyleLightContent else UIStatusBarStyleDarkContent) } From 123053284bbed3b35916813bbd5005095fc98494 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Mon, 22 May 2023 19:29:38 +0200 Subject: [PATCH 67/82] changes --- .../kotlin/nl/tiebe/otarium/Data.kt | 6 + .../kotlin/nl/tiebe/otarium/Main.kt | 11 - .../nl/tiebe/otarium/magister/Grades.kt | 7 + .../StoreGradeCalculationChildComponent.kt | 18 +- .../GradeCalculationChildComponent.kt | 18 ++ .../grades/calculation/cards/graph/GCGraph.kt | 8 +- .../calculation/subject/GCSubjectPopup.kt | 224 ++++++++++++++++-- 7 files changed, 258 insertions(+), 34 deletions(-) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/Data.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/Data.kt index d3f899cd..b391000c 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/Data.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/Data.kt @@ -4,6 +4,7 @@ import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import nl.tiebe.otarium.magister.MagisterAccount +import nl.tiebe.otarium.magister.ManualGrade import nl.tiebe.otarium.ui.theme.CustomTheme import nl.tiebe.otarium.ui.theme.defaultDarkTheme import nl.tiebe.otarium.ui.theme.defaultLightTheme @@ -60,5 +61,10 @@ object Data { get() = settings.getBoolean("show_cancelled_lessons", false) set(value) = settings.putBoolean("show_cancelled_lessons", value) + var manualGrades: List + get() = settings.getString("manual_grades", "[]").let { + Json.decodeFromString(it) } + set(value) = settings.putString("manual_grades", Json.encodeToString(value)) + } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/Main.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/Main.kt index dca4aca4..82d1c171 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/Main.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/Main.kt @@ -22,17 +22,6 @@ import nl.tiebe.otarium.utils.versions.runVersionCheck val settings: Settings = Settings() -//todo: back gestures ios -//todo: handmatig cijfers invoeren -//todo: soepelheid swipen overal -//todo: use tertiary color -//todo: add statistics tab in grades -//todo: voldoendegrens instellen in instellingen -//todo: mappen in studiewijzers -//todo: attachments bij lessen -//todo: fix swiping days and correctly switching weeks -//todo: laat cijfer info zien bij hover over grafiek - fun setup() { val oldVersion = settings.getInt("version", 1000) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/magister/Grades.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/magister/Grades.kt index 942863df..342267bd 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/magister/Grades.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/magister/Grades.kt @@ -78,4 +78,11 @@ suspend fun MagisterAccount.refreshGrades(): List { data class GradeWithGradeInfo( val grade: Grade, val gradeInfo: GradeInfo +) + +@Serializable +data class ManualGrade( + val name: String, + val grade: String, + val weight: Float ) \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/store/component/home/children/grades/children/StoreGradeCalculationChildComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/store/component/home/children/grades/children/StoreGradeCalculationChildComponent.kt index 60f69e98..5bdbde5c 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/store/component/home/children/grades/children/StoreGradeCalculationChildComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/store/component/home/children/grades/children/StoreGradeCalculationChildComponent.kt @@ -7,17 +7,33 @@ import dev.tiebe.magisterapi.response.general.year.grades.Subject import kotlinx.coroutines.launch import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json +import nl.tiebe.otarium.Data import nl.tiebe.otarium.MR +import nl.tiebe.otarium.magister.ManualGrade import nl.tiebe.otarium.ui.home.grades.calculation.GradeCalculationChildComponent import nl.tiebe.otarium.ui.root.componentCoroutineScope import nl.tiebe.otarium.utils.ui.getText -class StoreGradeCalculationChildComponent(componentContext: ComponentContext) : GradeCalculationChildComponent, ComponentContext by componentContext { +class StoreGradeCalculationChildComponent(componentContext: ComponentContext +) : GradeCalculationChildComponent, ComponentContext by componentContext { override val openedSubject: MutableValue> = MutableValue(false to null) override val backCallbackOpenItem = BackCallback(false) { closeSubject() } + override val manualGradesList: MutableValue> = MutableValue(Data.manualGrades.toMutableList()) + override val addManualGradePopupOpen: MutableValue = MutableValue(false) + + override fun addManualGrade(manualGrade: ManualGrade) { + manualGradesList.value.add(manualGrade) + Data.manualGrades = manualGradesList.value + } + + override fun removeManualGrade(manualGrade: ManualGrade) { + manualGradesList.value.remove(manualGrade) + Data.manualGrades = manualGradesList.value + } + override val state: MutableValue = MutableValue(GradeCalculationChildComponent.State.Loading) private val scope = componentCoroutineScope() diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/GradeCalculationChildComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/GradeCalculationChildComponent.kt index 8ee0ff4b..830c85b0 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/GradeCalculationChildComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/GradeCalculationChildComponent.kt @@ -9,6 +9,7 @@ import dev.tiebe.magisterapi.response.general.year.grades.Subject import kotlinx.coroutines.launch import nl.tiebe.otarium.Data import nl.tiebe.otarium.magister.GradeWithGradeInfo +import nl.tiebe.otarium.magister.ManualGrade import nl.tiebe.otarium.magister.refreshGrades import nl.tiebe.otarium.ui.home.grades.GradesChildComponent import nl.tiebe.otarium.ui.root.componentCoroutineScope @@ -18,6 +19,12 @@ interface GradeCalculationChildComponent : GradesChildComponent { val backCallbackOpenItem: BackCallback + val manualGradesList: MutableValue> + val addManualGradePopupOpen: MutableValue + + fun addManualGrade(manualGrade: ManualGrade) + fun removeManualGrade(manualGrade: ManualGrade) + fun openSubject(subject: Subject) { backCallbackOpenItem.isEnabled = true @@ -43,6 +50,17 @@ class DefaultGradeCalculationChildComponent(componentContext: ComponentContext) override val backCallbackOpenItem = BackCallback(false) { closeSubject() } + override val manualGradesList: MutableValue> = MutableValue(Data.manualGrades.toMutableList()) + override val addManualGradePopupOpen: MutableValue = MutableValue(false) + override fun addManualGrade(manualGrade: ManualGrade) { + manualGradesList.value.add(manualGrade) + Data.manualGrades = manualGradesList.value + } + + override fun removeManualGrade(manualGrade: ManualGrade) { + manualGradesList.value.remove(manualGrade) + Data.manualGrades = manualGradesList.value + } override val state: MutableValue = MutableValue(GradeCalculationChildComponent.State.Loading) private val scope = componentCoroutineScope() diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/cards/graph/GCGraph.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/cards/graph/GCGraph.kt index 70832d66..6a9f550b 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/cards/graph/GCGraph.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/cards/graph/GCGraph.kt @@ -97,15 +97,15 @@ internal fun GCGraph(grades: List) { } drawCircle( - center = averageOffset, + center = gradeOffset, radius = size.width / 70, - brush = averageBrush + brush = gradeBrush ) drawCircle( - center = gradeOffset, + center = averageOffset, radius = size.width / 70, - brush = gradeBrush + brush = averageBrush ) } if (grades.size > 1) { diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/subject/GCSubjectPopup.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/subject/GCSubjectPopup.kt index aa2c51b7..0bd2e85a 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/subject/GCSubjectPopup.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/subject/GCSubjectPopup.kt @@ -1,23 +1,37 @@ package nl.tiebe.otarium.ui.home.grades.calculation.subject +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.expandVertically +import androidx.compose.animation.shrinkVertically import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.filled.Delete import androidx.compose.material3.* import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.rotate import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState import dev.tiebe.magisterapi.response.general.year.grades.Subject import kotlinx.datetime.toLocalDateTime import nl.tiebe.otarium.Data import nl.tiebe.otarium.magister.GradeWithGradeInfo +import nl.tiebe.otarium.magister.ManualGrade import nl.tiebe.otarium.ui.home.grades.calculation.GradeCalculationChildComponent import nl.tiebe.otarium.ui.home.grades.calculation.calculateAverage import nl.tiebe.otarium.ui.home.grades.calculation.cards.GCAverageCalculator @@ -61,10 +75,57 @@ internal fun GCSubjectPopup(component: GradeCalculationChildComponent, subject: GCAverageCalculator(grades = gradeList) - GradeList(grades = gradeList) + val manuallyAddedGrades = component.manualGradesList.subscribeAsState() + var addItemPopout = component.addManualGradePopupOpen.subscribeAsState().value + + Row(Modifier.padding(top = 20.dp, start = 20.dp, bottom = 12.dp)) { + Text( + text = "Manually added grades", + style = MaterialTheme.typography.headlineSmall, + ) + + Spacer(modifier = Modifier.weight(1f)) + + val rotation by animateFloatAsState( + targetValue = if (addItemPopout) 45f else 0f + ) + + IconButton(onClick = { + addItemPopout = !addItemPopout + }) { + Icon( + imageVector = Icons.Default.Add, + contentDescription = "Add grade manually", + modifier = Modifier.size(24.dp).rotate(rotation) + ) + } + } + + + AnimatedVisibility(visible = addItemPopout, enter = expandVertically(), exit = shrinkVertically()) { + Column { + Spacer(modifier = Modifier.height(12.dp)) + + AddGradeManually(component) + } + } + + manuallyAddedGrades.value.reversed().forEach { + ManualGradeListItem(it, component) + } + + Text( + text = "Grades", + style = MaterialTheme.typography.headlineSmall, + modifier = Modifier.padding(20.dp) + ) + + gradeList.reversed().forEach { grade -> + GradeListItem(grade) + } } - Box(Modifier.fillMaxSize().padding(top = 12.dp, start = 12.dp)) { + Box(Modifier.fillMaxSize().padding(top = 20.dp, start = 20.dp, bottom = 12.dp)) { BackButton( Modifier.align(Alignment.TopStart), { @@ -80,20 +141,135 @@ internal fun GCSubjectPopup(component: GradeCalculationChildComponent, subject: @OptIn(ExperimentalMaterial3Api::class) @Composable -internal fun GradeList(grades: List) { - grades.reversed().forEach { grade -> - ListItem( - modifier = Modifier - .topBottomRectBorder(brush = SolidColor(MaterialTheme.colorScheme.outline)), - headlineText = { Text(grade.gradeInfo.columnDescription ?: "") }, - supportingText = { Text(grade.grade.dateEntered?.substring(0, 26)?.toLocalDateTime()?.toFormattedString() ?: "") }, - trailingContent = { +internal fun AddGradeManually(component: GradeCalculationChildComponent) { + val name = remember { mutableStateOf("") } + val nameError = remember { mutableStateOf(false) } + + val grade = remember { mutableStateOf("") } + val gradeError = remember { mutableStateOf(false) } + + val weight = remember { mutableStateOf("") } + val weightError = remember { mutableStateOf(false) } + + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 20.dp), + verticalAlignment = Alignment.CenterVertically + ) { + OutlinedTextField( + value = name.value, + onValueChange = { name.value = it }, + label = { Text("Name") }, + modifier = Modifier.weight(1f) + ) + + Spacer(modifier = Modifier.width(15.dp)) + + OutlinedTextField( + value = grade.value, + onValueChange = { value -> + if ((value.replace(",", ".").replace(".", "").all { it.isDigit() } && + value.replace(",", ".").toFloatOrNull() != null) || value.isBlank()) grade.value = value }, + singleLine = true, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal), + label = { Text("Grade") }, + modifier = Modifier.weight(1f) + ) + + Spacer(modifier = Modifier.width(5.dp)) + + OutlinedTextField( + value = weight.value, + onValueChange = { value -> + if ((value.replace(",", ".").replace(".", "").all { it.isDigit() } && + value.replace(",", ".").toFloatOrNull() != null) || value.isBlank()) weight.value = value }, + singleLine = true, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal), + label = { Text("Weight") }, + modifier = Modifier.weight(1f) + ) + + Spacer(modifier = Modifier.width(20.dp)) + + Button(onClick = { + nameError.value = name.value.isBlank() + gradeError.value = grade.value.isBlank() + weightError.value = weight.value.isBlank() + + if (name.value.isNotBlank() && grade.value.isNotBlank() && weight.value.isNotBlank()) { + val manualGrade = ManualGrade( + name = name.value, + grade = grade.value, + weight = weight.value.toFloatOrNull() ?: 0f + ) + + component.addManualGrade(manualGrade) + + name.value = "" + grade.value = "" + weight.value = "" + + component.addManualGradePopupOpen.value = false + } + + }) { + Text("Add") + } + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +internal fun GradeListItem(grade: GradeWithGradeInfo) { + ListItem( + modifier = Modifier + .topBottomRectBorder(brush = SolidColor(MaterialTheme.colorScheme.outline)), + headlineText = { Text(grade.gradeInfo.columnDescription ?: "") }, + supportingText = { Text(grade.grade.dateEntered?.substring(0, 26)?.toLocalDateTime()?.toFormattedString() ?: "") }, + trailingContent = { + Box( + modifier = Modifier + .size(48.dp) + ) { + Text( + text = grade.grade.grade ?: "", + modifier = Modifier + .align(Alignment.Center), + style = MaterialTheme.typography.displaySmall.copy(fontSize = 18.sp), + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + + Text( + text = "${grade.gradeInfo.weight}x", + modifier = Modifier + .align(Alignment.BottomEnd) + ) + } + }, + colors = ListItemDefaults.colors( + containerColor = MaterialTheme.colorScheme.inverseOnSurface + ), + ) +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +internal fun ManualGradeListItem(grade: ManualGrade, component: GradeCalculationChildComponent) { + ListItem( + modifier = Modifier + .topBottomRectBorder(brush = SolidColor(MaterialTheme.colorScheme.outline)), + headlineText = { Text(grade.name) }, + trailingContent = { + Row { + Box( modifier = Modifier .size(48.dp) ) { Text( - text = grade.grade.grade ?: "", + text = grade.grade, modifier = Modifier .align(Alignment.Center), style = MaterialTheme.typography.displaySmall.copy(fontSize = 18.sp), @@ -102,15 +278,27 @@ internal fun GradeList(grades: List) { ) Text( - text = "${grade.gradeInfo.weight}x", + text = "${grade.weight}x", modifier = Modifier .align(Alignment.BottomEnd) ) } - }, - colors = ListItemDefaults.colors( - containerColor = MaterialTheme.colorScheme.inverseOnSurface - ), - ) - } + + Spacer(modifier = Modifier.width(10.dp)) + + IconButton(onClick = { + component.removeManualGrade(grade) + }) { + Icon( + imageVector = Icons.Default.Delete, + contentDescription = "Delete grade", + modifier = Modifier.size(24.dp) + ) + } + } + }, + colors = ListItemDefaults.colors( + containerColor = MaterialTheme.colorScheme.inverseOnSurface + ), + ) } \ No newline at end of file From 4541f2fadd5993a16b040907579376e02d253054 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Mon, 22 May 2023 21:28:54 +0200 Subject: [PATCH 68/82] changes --- .../GradeCalculationChildComponent.kt | 40 ++++++--- .../calculation/cards/GCAverageCalculator.kt | 15 +++- .../grades/calculation/cards/graph/GCGraph.kt | 89 +++++++++++++------ .../calculation/subject/GCSubjectList.kt | 4 +- .../calculation/subject/GCSubjectPopup.kt | 26 +++--- 5 files changed, 118 insertions(+), 56 deletions(-) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/GradeCalculationChildComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/GradeCalculationChildComponent.kt index 830c85b0..ed9bc9e7 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/GradeCalculationChildComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/GradeCalculationChildComponent.kt @@ -19,7 +19,7 @@ interface GradeCalculationChildComponent : GradesChildComponent { val backCallbackOpenItem: BackCallback - val manualGradesList: MutableValue> + val manualGradesList: Value> val addManualGradePopupOpen: MutableValue fun addManualGrade(manualGrade: ManualGrade) @@ -50,15 +50,19 @@ class DefaultGradeCalculationChildComponent(componentContext: ComponentContext) override val backCallbackOpenItem = BackCallback(false) { closeSubject() } - override val manualGradesList: MutableValue> = MutableValue(Data.manualGrades.toMutableList()) + override val manualGradesList: MutableValue> = MutableValue(Data.manualGrades) override val addManualGradePopupOpen: MutableValue = MutableValue(false) override fun addManualGrade(manualGrade: ManualGrade) { - manualGradesList.value.add(manualGrade) + manualGradesList.value = manualGradesList.value.toMutableList().apply { + add(manualGrade) + } Data.manualGrades = manualGradesList.value } override fun removeManualGrade(manualGrade: ManualGrade) { - manualGradesList.value.remove(manualGrade) + manualGradesList.value = manualGradesList.value.toMutableList().apply { + remove(manualGrade) + } Data.manualGrades = manualGradesList.value } @@ -85,12 +89,18 @@ class DefaultGradeCalculationChildComponent(componentContext: ComponentContext) fun calculateAverage(grades: List, addedGrade: Float = 0f, addedGradeWeight: Float = 0f): Float { - var sum = addedGrade * addedGradeWeight - var weight = addedGradeWeight + return calculateAverage(grades.map { + (it.grade.grade?.replace(',', '.')?.toFloatOrNull() ?: 0f) to it.gradeInfo.weight.toFloat() + }, addedGrade, addedGradeWeight) +} - grades.forEach { - sum += (it.grade.grade?.replace(',', '.')?.toFloatOrNull() ?: 0f) * it.gradeInfo.weight.toFloat() - weight += it.gradeInfo.weight.toFloat() +fun calculateAverage(pairs: List>, initialAverage: Float = 0f, initialWeight: Float = 0f): Float { + var sum = initialAverage * initialWeight + var weight = initialWeight + + pairs.forEach { + sum += it.first * it.second + weight += it.second } if (weight == 0f) return 0f @@ -99,12 +109,18 @@ fun calculateAverage(grades: List, addedGrade: Float = 0f, a } fun calculateNewGrade(grades: List, newAverage: Float = 10f, newGradeWeight: Float = 1f): Float { + return calculateNewGrade(grades.map { + (it.grade.grade?.replace(',', '.')?.toFloatOrNull() ?: 0f) to it.gradeInfo.weight.toFloat() + }, newAverage, newGradeWeight) +} + +fun calculateNewGrade(pairs: List>, newAverage: Float = 10f, newGradeWeight: Float = 1f): Float { var sum = 0f var weight = newGradeWeight - grades.forEach { - sum += (it.grade.grade?.replace(',', '.')?.toFloatOrNull() ?: 0f) * it.gradeInfo.weight.toFloat() - weight += it.gradeInfo.weight.toFloat() + pairs.forEach { + sum += it.first * it.second + weight += it.second } return ((newAverage * weight) - sum) / newGradeWeight diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/cards/GCAverageCalculator.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/cards/GCAverageCalculator.kt index 3ec5ebbf..2e00b389 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/cards/GCAverageCalculator.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/cards/GCAverageCalculator.kt @@ -13,6 +13,7 @@ import androidx.compose.ui.unit.dp import nl.tiebe.otarium.Data import nl.tiebe.otarium.MR import nl.tiebe.otarium.magister.GradeWithGradeInfo +import nl.tiebe.otarium.magister.ManualGrade import nl.tiebe.otarium.ui.home.grades.calculation.calculateAverage import nl.tiebe.otarium.ui.home.grades.calculation.calculateNewGrade import nl.tiebe.otarium.utils.ui.format @@ -20,7 +21,7 @@ import nl.tiebe.otarium.utils.ui.getLocalizedString @OptIn(ExperimentalMaterial3Api::class) @Composable -internal fun GCAverageCalculator(grades: List) { +internal fun GCAverageCalculator(grades: List, manualGrades: List) { ElevatedCard( modifier = Modifier .fillMaxWidth() @@ -104,11 +105,19 @@ internal fun GCAverageCalculator(grades: List) { Button(modifier = Modifier.padding(top = 5.dp), onClick = { calculatedAverage = if (type == 1) - calculateAverage(grades, + calculateAverage(grades.map { + (it.grade.grade?.replace(',', '.')?.toFloatOrNull() ?: 0f) to it.gradeInfo.weight.toFloat() + } + manualGrades.map { + (it.grade.toFloatOrNull() ?: 0f) to it.weight + }, enteredGrade.replace(",", ".").toFloatOrNull() ?: 0f, enteredWeight.replace(",", ".").toFloatOrNull() ?: 0f) else { - calculateNewGrade(grades, + calculateNewGrade(grades.map { + (it.grade.grade?.replace(',', '.')?.toFloatOrNull() ?: 0f) to it.gradeInfo.weight.toFloat() + } + manualGrades.map { + (it.grade.toFloatOrNull() ?: 0f) to it.weight + }, enteredGrade.replace(",", ".").toFloatOrNull() ?: 0f, enteredWeight.replace(",", ".").toFloatOrNull() ?: 0f) } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/cards/graph/GCGraph.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/cards/graph/GCGraph.kt index 6a9f550b..f6888c1e 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/cards/graph/GCGraph.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/cards/graph/GCGraph.kt @@ -8,6 +8,7 @@ import androidx.compose.material3.ElevatedCard import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier @@ -15,6 +16,7 @@ import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Path +import androidx.compose.ui.graphics.drawscope.DrawScope import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.graphics.drawscope.drawIntoCanvas import androidx.compose.ui.graphics.nativeCanvas @@ -28,12 +30,13 @@ import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp import nl.tiebe.otarium.MR import nl.tiebe.otarium.magister.GradeWithGradeInfo +import nl.tiebe.otarium.magister.ManualGrade import nl.tiebe.otarium.ui.home.grades.calculation.calculateAverage import nl.tiebe.otarium.utils.ui.getLocalizedString @OptIn(ExperimentalTextApi::class) @Composable -internal fun GCGraph(grades: List) { +internal fun GCGraph(grades: List, manualGrades: List) { ElevatedCard( modifier = Modifier.padding(10.dp), ) { @@ -67,7 +70,7 @@ internal fun GCGraph(grades: List) { .padding(horizontal = 4.dp) ) { - lineBound.value = size.width / grades.count() * 0.8f + lineBound.value = size.width / (grades.size + manualGrades.size) * 0.8f val gradeBrush = Brush.linearGradient(listOf(gradeLineColor, gradeLineColor)) val gradePath = Path().apply { moveTo(0f, size.height) } @@ -79,36 +82,22 @@ internal fun GCGraph(grades: List) { val grade = gradeInfo.grade.grade?.replace(',', '.')?.toFloatOrNull() ?: 0f val average = calculateAverage(grades.subList(0, index+1)) - val gradeOffset = Offset((2*index+1) * lineBound.value * 0.6f, size.height - grade * (size.height / 10)) - val averageOffset = Offset((2*index+1) * lineBound.value * 0.6f, size.height - average * (size.height / 10)) - - if (grades.size > 1) { - when (index) { - 0 -> { - gradePath.moveTo(gradeOffset.x, gradeOffset.y) - averagePath.moveTo(averageOffset.x, averageOffset.y) - } - else -> { - gradePath.lineTo(gradeOffset.x, gradeOffset.y) - averagePath.lineTo(averageOffset.x, averageOffset.y) - } - - } - } + drawGrade(index, lineBound, grade, average, grades.size + manualGrades.size, gradePath, averagePath, gradeBrush, averageBrush) + } - drawCircle( - center = gradeOffset, - radius = size.width / 70, - brush = gradeBrush - ) + manualGrades.forEachIndexed { index, manualGrade -> + val grade = manualGrade.grade.replace(',', '.').toFloatOrNull() ?: 0f + val average = calculateAverage( + grades.map { + (it.grade.grade?.replace(',', '.')?.toFloatOrNull() ?: 0f) to it.gradeInfo.weight.toFloat() + } + manualGrades.subList(0, index+1).map { + (it.grade.toFloatOrNull() ?: 0f) to it.weight + }) - drawCircle( - center = averageOffset, - radius = size.width / 70, - brush = averageBrush - ) + drawGrade(index + grades.size, lineBound, grade, average, grades.size + manualGrades.size, gradePath, averagePath, gradeBrush, averageBrush) } - if (grades.size > 1) { + + if (grades.size + manualGrades.size > 1) { drawPath( path = gradePath, brush = gradeBrush, @@ -153,4 +142,46 @@ internal fun GCGraph(grades: List) { } } } +} + +private fun DrawScope.drawGrade( + index: Int, + lineBound: MutableState, + grade: Float, + average: Float, + listSize: Int, + gradePath: Path, + averagePath: Path, + gradeBrush: Brush, + averageBrush: Brush +) { + val gradeOffset = Offset((2 * index + 1) * lineBound.value * 0.6f, size.height - grade * (size.height / 10)) + val averageOffset = Offset((2 * index + 1) * lineBound.value * 0.6f, size.height - average * (size.height / 10)) + + if (listSize > 1) { + when (index) { + 0 -> { + gradePath.moveTo(gradeOffset.x, gradeOffset.y) + averagePath.moveTo(averageOffset.x, averageOffset.y) + } + + else -> { + gradePath.lineTo(gradeOffset.x, gradeOffset.y) + averagePath.lineTo(averageOffset.x, averageOffset.y) + } + + } + } + + drawCircle( + center = gradeOffset, + radius = size.width / 70, + brush = gradeBrush + ) + + drawCircle( + center = averageOffset, + radius = size.width / 70, + brush = averageBrush + ) } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/subject/GCSubjectList.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/subject/GCSubjectList.kt index d62a5d55..e93693ab 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/subject/GCSubjectList.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/subject/GCSubjectList.kt @@ -17,9 +17,9 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState import nl.tiebe.otarium.magister.GradeWithGradeInfo -import nl.tiebe.otarium.ui.utils.topBottomRectBorder import nl.tiebe.otarium.ui.home.grades.calculation.GradeCalculationChildComponent import nl.tiebe.otarium.ui.home.grades.calculation.calculateAverage +import nl.tiebe.otarium.ui.utils.topBottomRectBorder import nl.tiebe.otarium.utils.ui.format @OptIn(ExperimentalMaterial3Api::class, ExperimentalAnimationApi::class) @@ -37,7 +37,7 @@ internal fun GCSubjectList(component: GradeCalculationChildComponent, grades: Li GCSubjectPopup( component = component, subject = popupItem.value.second!!, - gradeList = grades.filter { it.grade.subject.id == popupItem.value.second?.id } + realGradeList = grades.filter { it.grade.subject.id == popupItem.value.second?.id } ) } else { Column( diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/subject/GCSubjectPopup.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/subject/GCSubjectPopup.kt index 0bd2e85a..5198bbcc 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/subject/GCSubjectPopup.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/subject/GCSubjectPopup.kt @@ -42,7 +42,16 @@ import nl.tiebe.otarium.utils.toFormattedString import nl.tiebe.otarium.utils.ui.format @Composable -internal fun GCSubjectPopup(component: GradeCalculationChildComponent, subject: Subject, gradeList: List) { +internal fun GCSubjectPopup(component: GradeCalculationChildComponent, subject: Subject, realGradeList: List) { + val manualGradeList = component.manualGradesList.subscribeAsState() + val gradeList = remember { + realGradeList.map { + (it.grade.grade?.replace(',', '.')?.toFloatOrNull() ?: 0f) to it.gradeInfo.weight.toFloat() + } + manualGradeList.value.map { + (it.grade.toFloatOrNull() ?: 0f) to it.weight + } + } + Column( modifier = Modifier .fillMaxSize() @@ -71,12 +80,11 @@ internal fun GCSubjectPopup(component: GradeCalculationChildComponent, subject: } } - GCGraph(grades = gradeList) + GCGraph(grades = realGradeList, manualGrades = manualGradeList.value) - GCAverageCalculator(grades = gradeList) + GCAverageCalculator(grades = realGradeList, manualGrades = manualGradeList.value) - val manuallyAddedGrades = component.manualGradesList.subscribeAsState() - var addItemPopout = component.addManualGradePopupOpen.subscribeAsState().value + val addItemPopout = component.addManualGradePopupOpen.subscribeAsState().value Row(Modifier.padding(top = 20.dp, start = 20.dp, bottom = 12.dp)) { Text( @@ -91,7 +99,7 @@ internal fun GCSubjectPopup(component: GradeCalculationChildComponent, subject: ) IconButton(onClick = { - addItemPopout = !addItemPopout + component.addManualGradePopupOpen.value = !addItemPopout }) { Icon( imageVector = Icons.Default.Add, @@ -104,13 +112,11 @@ internal fun GCSubjectPopup(component: GradeCalculationChildComponent, subject: AnimatedVisibility(visible = addItemPopout, enter = expandVertically(), exit = shrinkVertically()) { Column { - Spacer(modifier = Modifier.height(12.dp)) - AddGradeManually(component) } } - manuallyAddedGrades.value.reversed().forEach { + manualGradeList.value.reversed().forEach { ManualGradeListItem(it, component) } @@ -120,7 +126,7 @@ internal fun GCSubjectPopup(component: GradeCalculationChildComponent, subject: modifier = Modifier.padding(20.dp) ) - gradeList.reversed().forEach { grade -> + realGradeList.reversed().forEach { grade -> GradeListItem(grade) } } From ba3948aa645b95633fa897ab243898deeff10ffb Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Tue, 23 May 2023 14:34:40 +0200 Subject: [PATCH 69/82] more changes --- .../kotlin/nl/tiebe/otarium/Data.kt | 7 + .../nl/tiebe/otarium/magister/Grades.kt | 3 +- .../GradeCalculationChildComponent.kt | 6 +- .../calculation/cards/GCAverageCalculator.kt | 8 +- .../grades/calculation/cards/graph/GCGraph.kt | 3 +- .../screen/GradeCalculationChild.kt | 2 +- .../calculation/subject/GCManualGradePopup.kt | 103 +++++++++ .../calculation/subject/GCSubjectLIstItem.kt | 107 +++++++++ .../calculation/subject/GCSubjectList.kt | 24 ++- .../calculation/subject/GCSubjectPopup.kt | 204 ++---------------- .../grades/recentgrades/RecentGradeItem.kt | 2 + .../RecentGradesChildComponent.kt | 6 +- .../settings/items/ui/UIChildComponent.kt | 21 ++ .../home/settings/items/ui/UIChildScreen.kt | 39 ++++ .../ui/home/settings/utils/SettingSlider.kt | 3 +- .../commonMain/resources/MR/base/strings.xml | 10 + .../commonMain/resources/MR/nl/strings.xml | 13 +- 17 files changed, 356 insertions(+), 205 deletions(-) create mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/subject/GCManualGradePopup.kt create mode 100644 shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/subject/GCSubjectLIstItem.kt diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/Data.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/Data.kt index b391000c..fc90de7f 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/Data.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/Data.kt @@ -66,5 +66,12 @@ object Data { Json.decodeFromString(it) } set(value) = settings.putString("manual_grades", Json.encodeToString(value)) + var markGrades: Boolean + get() = settings.getBoolean("mark_grades", false) + set(value) = settings.putBoolean("mark_grades", value) + + var passingGrade: Float + get() = settings.getFloat("passing_grade", 5.5f) + set(value) = settings.putFloat("passing_grade", value) } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/magister/Grades.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/magister/Grades.kt index 342267bd..39934f62 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/magister/Grades.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/magister/Grades.kt @@ -84,5 +84,6 @@ data class GradeWithGradeInfo( data class ManualGrade( val name: String, val grade: String, - val weight: Float + val weight: Float, + val subjectId: Int, ) \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/GradeCalculationChildComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/GradeCalculationChildComponent.kt index ed9bc9e7..4c249c1b 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/GradeCalculationChildComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/GradeCalculationChildComponent.kt @@ -88,7 +88,7 @@ class DefaultGradeCalculationChildComponent(componentContext: ComponentContext) } -fun calculateAverage(grades: List, addedGrade: Float = 0f, addedGradeWeight: Float = 0f): Float { +fun calculateAverageGrade(grades: List, addedGrade: Float = 0f, addedGradeWeight: Float = 0f): Float { return calculateAverage(grades.map { (it.grade.grade?.replace(',', '.')?.toFloatOrNull() ?: 0f) to it.gradeInfo.weight.toFloat() }, addedGrade, addedGradeWeight) @@ -109,12 +109,12 @@ fun calculateAverage(pairs: List>, initialAverage: Float = 0f } fun calculateNewGrade(grades: List, newAverage: Float = 10f, newGradeWeight: Float = 1f): Float { - return calculateNewGrade(grades.map { + return calculateNew(grades.map { (it.grade.grade?.replace(',', '.')?.toFloatOrNull() ?: 0f) to it.gradeInfo.weight.toFloat() }, newAverage, newGradeWeight) } -fun calculateNewGrade(pairs: List>, newAverage: Float = 10f, newGradeWeight: Float = 1f): Float { +fun calculateNew(pairs: List>, newAverage: Float = 10f, newGradeWeight: Float = 1f): Float { var sum = 0f var weight = newGradeWeight diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/cards/GCAverageCalculator.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/cards/GCAverageCalculator.kt index 2e00b389..bc174e08 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/cards/GCAverageCalculator.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/cards/GCAverageCalculator.kt @@ -7,6 +7,7 @@ import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp @@ -15,7 +16,7 @@ import nl.tiebe.otarium.MR import nl.tiebe.otarium.magister.GradeWithGradeInfo import nl.tiebe.otarium.magister.ManualGrade import nl.tiebe.otarium.ui.home.grades.calculation.calculateAverage -import nl.tiebe.otarium.ui.home.grades.calculation.calculateNewGrade +import nl.tiebe.otarium.ui.home.grades.calculation.calculateNew import nl.tiebe.otarium.utils.ui.format import nl.tiebe.otarium.utils.ui.getLocalizedString @@ -100,7 +101,8 @@ internal fun GCAverageCalculator(grades: List, manualGrades: Column(Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center) { Text( text = if (calculatedAverage != null) calculatedAverage?.format(Data.decimals) ?: "" else "", - style = MaterialTheme.typography.displayMedium + style = MaterialTheme.typography.displayMedium, + color = if ((calculatedAverage ?: 10.0f) < Data.passingGrade) MaterialTheme.colorScheme.error else Color.Unspecified, ) Button(modifier = Modifier.padding(top = 5.dp), onClick = { @@ -113,7 +115,7 @@ internal fun GCAverageCalculator(grades: List, manualGrades: enteredGrade.replace(",", ".").toFloatOrNull() ?: 0f, enteredWeight.replace(",", ".").toFloatOrNull() ?: 0f) else { - calculateNewGrade(grades.map { + calculateNew(grades.map { (it.grade.grade?.replace(',', '.')?.toFloatOrNull() ?: 0f) to it.gradeInfo.weight.toFloat() } + manualGrades.map { (it.grade.toFloatOrNull() ?: 0f) to it.weight diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/cards/graph/GCGraph.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/cards/graph/GCGraph.kt index f6888c1e..46633788 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/cards/graph/GCGraph.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/cards/graph/GCGraph.kt @@ -32,6 +32,7 @@ import nl.tiebe.otarium.MR import nl.tiebe.otarium.magister.GradeWithGradeInfo import nl.tiebe.otarium.magister.ManualGrade import nl.tiebe.otarium.ui.home.grades.calculation.calculateAverage +import nl.tiebe.otarium.ui.home.grades.calculation.calculateAverageGrade import nl.tiebe.otarium.utils.ui.getLocalizedString @OptIn(ExperimentalTextApi::class) @@ -80,7 +81,7 @@ internal fun GCGraph(grades: List, manualGrades: List val grade = gradeInfo.grade.grade?.replace(',', '.')?.toFloatOrNull() ?: 0f - val average = calculateAverage(grades.subList(0, index+1)) + val average = calculateAverageGrade(grades.subList(0, index+1)) drawGrade(index, lineBound, grade, average, grades.size + manualGrades.size, gradePath, averagePath, gradeBrush, averageBrush) } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/screen/GradeCalculationChild.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/screen/GradeCalculationChild.kt index c39f21c4..39492524 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/screen/GradeCalculationChild.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/screen/GradeCalculationChild.kt @@ -13,7 +13,7 @@ internal fun GradeCalculationChild(component: GradeCalculationChildComponent) { Text("Loading") } is GradeCalculationChildComponent.State.Data -> { - GCSubjectList(component, state.data.sortedBy { it.grade.dateEntered }) + GCSubjectList(component, state.data.sortedBy { it.grade.dateEntered }, component.manualGradesList.subscribeAsState().value) } is GradeCalculationChildComponent.State.Failed -> { Text("Something went wrong while retrieving your grades") diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/subject/GCManualGradePopup.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/subject/GCManualGradePopup.kt new file mode 100644 index 00000000..cfd37c73 --- /dev/null +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/subject/GCManualGradePopup.kt @@ -0,0 +1,103 @@ +package nl.tiebe.otarium.ui.home.grades.calculation.subject + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.Button +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.unit.dp +import dev.tiebe.magisterapi.response.general.year.grades.Subject +import nl.tiebe.otarium.MR +import nl.tiebe.otarium.magister.ManualGrade +import nl.tiebe.otarium.ui.home.grades.calculation.GradeCalculationChildComponent +import nl.tiebe.otarium.utils.ui.getLocalizedString + + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +internal fun AddGradeManually(component: GradeCalculationChildComponent, subject: Subject) { + val name = remember { mutableStateOf("") } + val nameError = remember { mutableStateOf(false) } + + val grade = remember { mutableStateOf("") } + val gradeError = remember { mutableStateOf(false) } + + val weight = remember { mutableStateOf("") } + val weightError = remember { mutableStateOf(false) } + + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 20.dp), + verticalAlignment = Alignment.CenterVertically + ) { + OutlinedTextField( + value = name.value, + onValueChange = { name.value = it }, + label = { Text(getLocalizedString(MR.strings.manual_name)) }, + modifier = Modifier.weight(1f) + ) + + Spacer(modifier = Modifier.width(15.dp)) + + OutlinedTextField( + value = grade.value, + onValueChange = { value -> + if ((value.replace(",", ".").replace(".", "").all { it.isDigit() } && + value.replace(",", ".").toFloatOrNull() != null) || value.isBlank()) grade.value = value }, + singleLine = true, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal), + label = { Text(getLocalizedString(MR.strings.manual_grade)) }, + modifier = Modifier.weight(1f) + ) + + Spacer(modifier = Modifier.width(5.dp)) + + OutlinedTextField( + value = weight.value, + onValueChange = { value -> + if ((value.replace(",", ".").replace(".", "").all { it.isDigit() } && + value.replace(",", ".").toFloatOrNull() != null) || value.isBlank()) weight.value = value }, + singleLine = true, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal), + label = { Text(getLocalizedString(MR.strings.manual_weight)) }, + modifier = Modifier.weight(1f) + ) + + Spacer(modifier = Modifier.width(20.dp)) + + Button(onClick = { + nameError.value = name.value.isBlank() + gradeError.value = grade.value.isBlank() + weightError.value = weight.value.isBlank() + + if (name.value.isNotBlank() && grade.value.isNotBlank() && weight.value.isNotBlank()) { + val manualGrade = ManualGrade( + name = name.value, + grade = grade.value, + weight = weight.value.toFloatOrNull() ?: 0f, + subjectId = subject.id + ) + + component.addManualGrade(manualGrade) + + name.value = "" + grade.value = "" + weight.value = "" + + component.addManualGradePopupOpen.value = false + } + + }) { + Text("Add") + } + } +} + diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/subject/GCSubjectLIstItem.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/subject/GCSubjectLIstItem.kt new file mode 100644 index 00000000..bbf47771 --- /dev/null +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/subject/GCSubjectLIstItem.kt @@ -0,0 +1,107 @@ +package nl.tiebe.otarium.ui.home.grades.calculation.subject + +import androidx.compose.foundation.layout.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Delete +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import kotlinx.datetime.toLocalDateTime +import nl.tiebe.otarium.Data +import nl.tiebe.otarium.magister.GradeWithGradeInfo +import nl.tiebe.otarium.magister.ManualGrade +import nl.tiebe.otarium.ui.home.grades.calculation.GradeCalculationChildComponent +import nl.tiebe.otarium.ui.utils.topBottomRectBorder +import nl.tiebe.otarium.utils.toFormattedString + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +internal fun GradeListItem(grade: GradeWithGradeInfo) { + ListItem( + modifier = Modifier + .topBottomRectBorder(brush = SolidColor(MaterialTheme.colorScheme.outline)), + headlineText = { Text(grade.gradeInfo.columnDescription ?: "") }, + supportingText = { Text(grade.grade.dateEntered?.substring(0, 26)?.toLocalDateTime()?.toFormattedString() ?: "") }, + trailingContent = { + Box( + modifier = Modifier + .size(48.dp) + ) { + Text( + text = grade.grade.grade ?: "", + modifier = Modifier + .align(Alignment.Center), + style = MaterialTheme.typography.displaySmall.copy(fontSize = 18.sp), + maxLines = 1, + overflow = TextOverflow.Ellipsis, + color = if ((grade.grade.grade?.replace(",", ".")?.toFloatOrNull() ?: 10.0f) < Data.passingGrade) MaterialTheme.colorScheme.error else Color.Unspecified, + ) + + Text( + text = "${grade.gradeInfo.weight}x", + modifier = Modifier + .align(Alignment.BottomEnd) + ) + } + }, + colors = ListItemDefaults.colors( + containerColor = MaterialTheme.colorScheme.inverseOnSurface + ), + ) +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +internal fun ManualGradeListItem(grade: ManualGrade, component: GradeCalculationChildComponent) { + ListItem( + modifier = Modifier + .topBottomRectBorder(brush = SolidColor(MaterialTheme.colorScheme.outline)), + headlineText = { Text(grade.name) }, + trailingContent = { + Row { + + Box( + modifier = Modifier + .size(48.dp) + ) { + Text( + text = grade.grade, + modifier = Modifier + .align(Alignment.Center), + style = MaterialTheme.typography.displaySmall.copy(fontSize = 18.sp), + maxLines = 1, + overflow = TextOverflow.Ellipsis, + color = if ((grade.grade.replace(",", ".").toFloatOrNull() ?: 10.0f) < Data.passingGrade) MaterialTheme.colorScheme.error else Color.Unspecified, + ) + + Text( + text = "${grade.weight}x", + modifier = Modifier + .align(Alignment.BottomEnd) + ) + } + + Spacer(modifier = Modifier.width(10.dp)) + + IconButton(onClick = { + component.removeManualGrade(grade) + }) { + Icon( + imageVector = Icons.Default.Delete, + contentDescription = "Delete grade", + modifier = Modifier.size(24.dp) + ) + } + } + }, + colors = ListItemDefaults.colors( + containerColor = MaterialTheme.colorScheme.inverseOnSurface + ), + ) +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/subject/GCSubjectList.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/subject/GCSubjectList.kt index e93693ab..773aa870 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/subject/GCSubjectList.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/subject/GCSubjectList.kt @@ -10,13 +10,17 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.* import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState +import nl.tiebe.otarium.Data import nl.tiebe.otarium.magister.GradeWithGradeInfo +import nl.tiebe.otarium.magister.ManualGrade import nl.tiebe.otarium.ui.home.grades.calculation.GradeCalculationChildComponent import nl.tiebe.otarium.ui.home.grades.calculation.calculateAverage import nl.tiebe.otarium.ui.utils.topBottomRectBorder @@ -24,7 +28,11 @@ import nl.tiebe.otarium.utils.ui.format @OptIn(ExperimentalMaterial3Api::class, ExperimentalAnimationApi::class) @Composable -internal fun GCSubjectList(component: GradeCalculationChildComponent, grades: List) { +internal fun GCSubjectList( + component: GradeCalculationChildComponent, + grades: List, + manualGrades: List +) { val subjects = grades.map { it.grade.subject }.distinct().sortedBy { it.description.lowercase() } val popupItem = component.openedSubject.subscribeAsState() @@ -46,6 +54,14 @@ internal fun GCSubjectList(component: GradeCalculationChildComponent, grades: Li .verticalScroll(rememberScrollState()), ) { subjects.forEach { subject -> + val gradeList = remember { + grades.filter { it.grade.subject.id == subject.id }.map { + (it.grade.grade?.replace(',', '.')?.toFloatOrNull() ?: 0f) to it.gradeInfo.weight.toFloat() + } + manualGrades.filter { it.subjectId == subject.id }.map { + (it.grade.toFloatOrNull() ?: 0f) to it.weight + } + } + ListItem( modifier = Modifier .topBottomRectBorder(brush = SolidColor(MaterialTheme.colorScheme.outline)) @@ -54,11 +70,13 @@ internal fun GCSubjectList(component: GradeCalculationChildComponent, grades: Li if (it.isLowerCase()) it.titlecase() else it.toString() }) }, trailingContent = { + val average = derivedStateOf { calculateAverage(gradeList) } + Text( - text = calculateAverage(grades.filter { it.grade.subject.id == subject.id }).format(1), + text = average.value.format(1), textAlign = TextAlign.Center, modifier = Modifier.padding(2.dp), - color = MaterialTheme.colorScheme.onSurface, + color = if (average.value < Data.passingGrade) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.onSurface, style = MaterialTheme.typography.displaySmall.copy(fontSize = 18.sp) ) }, diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/subject/GCSubjectPopup.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/subject/GCSubjectPopup.kt index 5198bbcc..bb7c5047 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/subject/GCSubjectPopup.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/subject/GCSubjectPopup.kt @@ -6,48 +6,41 @@ import androidx.compose.animation.expandVertically import androidx.compose.animation.shrinkVertically import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.ArrowBack -import androidx.compose.material.icons.filled.Delete import androidx.compose.material3.* import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.rotate -import androidx.compose.ui.graphics.SolidColor -import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState import dev.tiebe.magisterapi.response.general.year.grades.Subject -import kotlinx.datetime.toLocalDateTime import nl.tiebe.otarium.Data +import nl.tiebe.otarium.MR import nl.tiebe.otarium.magister.GradeWithGradeInfo -import nl.tiebe.otarium.magister.ManualGrade import nl.tiebe.otarium.ui.home.grades.calculation.GradeCalculationChildComponent import nl.tiebe.otarium.ui.home.grades.calculation.calculateAverage import nl.tiebe.otarium.ui.home.grades.calculation.cards.GCAverageCalculator import nl.tiebe.otarium.ui.home.grades.calculation.cards.graph.GCGraph import nl.tiebe.otarium.ui.utils.BackButton -import nl.tiebe.otarium.ui.utils.topBottomRectBorder -import nl.tiebe.otarium.utils.toFormattedString import nl.tiebe.otarium.utils.ui.format +import nl.tiebe.otarium.utils.ui.getLocalizedString @Composable internal fun GCSubjectPopup(component: GradeCalculationChildComponent, subject: Subject, realGradeList: List) { - val manualGradeList = component.manualGradesList.subscribeAsState() - val gradeList = remember { + val manualGradeList = component.manualGradesList.subscribeAsState().value.filter { it.subjectId == subject.id } + val gradeList = derivedStateOf { realGradeList.map { (it.grade.grade?.replace(',', '.')?.toFloatOrNull() ?: 0f) to it.gradeInfo.weight.toFloat() - } + manualGradeList.value.map { + } + manualGradeList.map { (it.grade.toFloatOrNull() ?: 0f) to it.weight } } @@ -71,24 +64,27 @@ internal fun GCSubjectPopup(component: GradeCalculationChildComponent, subject: ElevatedCard(modifier = Modifier.size(50.dp).align(Alignment.CenterEnd)) { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + val average = derivedStateOf { calculateAverage(gradeList.value) } + Text( - text = calculateAverage(gradeList).format(Data.decimals), + text = average.value.format(Data.decimals), style = MaterialTheme.typography.titleMedium, - textAlign = TextAlign.Center + textAlign = TextAlign.Center, + color = if (average.value < Data.passingGrade) MaterialTheme.colorScheme.error else Color.Unspecified, ) } } } - GCGraph(grades = realGradeList, manualGrades = manualGradeList.value) + GCGraph(grades = realGradeList, manualGrades = manualGradeList) - GCAverageCalculator(grades = realGradeList, manualGrades = manualGradeList.value) + GCAverageCalculator(grades = realGradeList, manualGrades = manualGradeList) val addItemPopout = component.addManualGradePopupOpen.subscribeAsState().value Row(Modifier.padding(top = 20.dp, start = 20.dp, bottom = 12.dp)) { Text( - text = "Manually added grades", + text = getLocalizedString(MR.strings.manual_grades), style = MaterialTheme.typography.headlineSmall, ) @@ -112,16 +108,16 @@ internal fun GCSubjectPopup(component: GradeCalculationChildComponent, subject: AnimatedVisibility(visible = addItemPopout, enter = expandVertically(), exit = shrinkVertically()) { Column { - AddGradeManually(component) + AddGradeManually(component, subject) } } - manualGradeList.value.reversed().forEach { + manualGradeList.reversed().forEach { ManualGradeListItem(it, component) } Text( - text = "Grades", + text = getLocalizedString(MR.strings.real_grades), style = MaterialTheme.typography.headlineSmall, modifier = Modifier.padding(20.dp) ) @@ -144,167 +140,3 @@ internal fun GCSubjectPopup(component: GradeCalculationChildComponent, subject: } } } - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -internal fun AddGradeManually(component: GradeCalculationChildComponent) { - val name = remember { mutableStateOf("") } - val nameError = remember { mutableStateOf(false) } - - val grade = remember { mutableStateOf("") } - val gradeError = remember { mutableStateOf(false) } - - val weight = remember { mutableStateOf("") } - val weightError = remember { mutableStateOf(false) } - - Row( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 20.dp), - verticalAlignment = Alignment.CenterVertically - ) { - OutlinedTextField( - value = name.value, - onValueChange = { name.value = it }, - label = { Text("Name") }, - modifier = Modifier.weight(1f) - ) - - Spacer(modifier = Modifier.width(15.dp)) - - OutlinedTextField( - value = grade.value, - onValueChange = { value -> - if ((value.replace(",", ".").replace(".", "").all { it.isDigit() } && - value.replace(",", ".").toFloatOrNull() != null) || value.isBlank()) grade.value = value }, - singleLine = true, - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal), - label = { Text("Grade") }, - modifier = Modifier.weight(1f) - ) - - Spacer(modifier = Modifier.width(5.dp)) - - OutlinedTextField( - value = weight.value, - onValueChange = { value -> - if ((value.replace(",", ".").replace(".", "").all { it.isDigit() } && - value.replace(",", ".").toFloatOrNull() != null) || value.isBlank()) weight.value = value }, - singleLine = true, - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal), - label = { Text("Weight") }, - modifier = Modifier.weight(1f) - ) - - Spacer(modifier = Modifier.width(20.dp)) - - Button(onClick = { - nameError.value = name.value.isBlank() - gradeError.value = grade.value.isBlank() - weightError.value = weight.value.isBlank() - - if (name.value.isNotBlank() && grade.value.isNotBlank() && weight.value.isNotBlank()) { - val manualGrade = ManualGrade( - name = name.value, - grade = grade.value, - weight = weight.value.toFloatOrNull() ?: 0f - ) - - component.addManualGrade(manualGrade) - - name.value = "" - grade.value = "" - weight.value = "" - - component.addManualGradePopupOpen.value = false - } - - }) { - Text("Add") - } - } -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -internal fun GradeListItem(grade: GradeWithGradeInfo) { - ListItem( - modifier = Modifier - .topBottomRectBorder(brush = SolidColor(MaterialTheme.colorScheme.outline)), - headlineText = { Text(grade.gradeInfo.columnDescription ?: "") }, - supportingText = { Text(grade.grade.dateEntered?.substring(0, 26)?.toLocalDateTime()?.toFormattedString() ?: "") }, - trailingContent = { - Box( - modifier = Modifier - .size(48.dp) - ) { - Text( - text = grade.grade.grade ?: "", - modifier = Modifier - .align(Alignment.Center), - style = MaterialTheme.typography.displaySmall.copy(fontSize = 18.sp), - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - - Text( - text = "${grade.gradeInfo.weight}x", - modifier = Modifier - .align(Alignment.BottomEnd) - ) - } - }, - colors = ListItemDefaults.colors( - containerColor = MaterialTheme.colorScheme.inverseOnSurface - ), - ) -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -internal fun ManualGradeListItem(grade: ManualGrade, component: GradeCalculationChildComponent) { - ListItem( - modifier = Modifier - .topBottomRectBorder(brush = SolidColor(MaterialTheme.colorScheme.outline)), - headlineText = { Text(grade.name) }, - trailingContent = { - Row { - - Box( - modifier = Modifier - .size(48.dp) - ) { - Text( - text = grade.grade, - modifier = Modifier - .align(Alignment.Center), - style = MaterialTheme.typography.displaySmall.copy(fontSize = 18.sp), - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - - Text( - text = "${grade.weight}x", - modifier = Modifier - .align(Alignment.BottomEnd) - ) - } - - Spacer(modifier = Modifier.width(10.dp)) - - IconButton(onClick = { - component.removeManualGrade(grade) - }) { - Icon( - imageVector = Icons.Default.Delete, - contentDescription = "Delete grade", - modifier = Modifier.size(24.dp) - ) - } - } - }, - colors = ListItemDefaults.colors( - containerColor = MaterialTheme.colorScheme.inverseOnSurface - ), - ) -} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/recentgrades/RecentGradeItem.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/recentgrades/RecentGradeItem.kt index dce01798..f5d97493 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/recentgrades/RecentGradeItem.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/recentgrades/RecentGradeItem.kt @@ -12,6 +12,7 @@ import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp @@ -50,6 +51,7 @@ internal fun RecentGradeItem(component: RecentGradesChildComponent, grade: Recen .align(Alignment.Center), style = MaterialTheme.typography.displaySmall.copy(fontSize = 18.sp), maxLines = 1, + color = if ((grade.grade.replace(",", ".").toFloatOrNull() ?: 10.0f) < Data.passingGrade) MaterialTheme.colorScheme.error else Color.Unspecified, overflow = TextOverflow.Ellipsis ) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/recentgrades/RecentGradesChildComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/recentgrades/RecentGradesChildComponent.kt index 6cf5d88a..1b4f3a4b 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/recentgrades/RecentGradesChildComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/recentgrades/RecentGradesChildComponent.kt @@ -9,7 +9,7 @@ import kotlinx.coroutines.launch import nl.tiebe.otarium.Data import nl.tiebe.otarium.magister.getRecentGrades import nl.tiebe.otarium.ui.home.grades.GradesChildComponent -import nl.tiebe.otarium.ui.home.grades.calculation.calculateAverage +import nl.tiebe.otarium.ui.home.grades.calculation.calculateAverageGrade import nl.tiebe.otarium.ui.root.componentCoroutineScope interface RecentGradesChildComponent : GradesChildComponent { @@ -63,8 +63,8 @@ class DefaultRecentGradesChildComponent(componentContext: ComponentContext) : Re val index = grades.find { it.grade.gradeColumn.id == grade.gradeColumnId }?.let { grades.indexOf(it) } ?: return 0f to 0f - val before = calculateAverage(grades.subList(0, index)) - val after = calculateAverage(grades.subList(0, index + 1)) + val before = calculateAverageGrade(grades.subList(0, index)) + val after = calculateAverageGrade(grades.subList(0, index + 1)) return before to after } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/UIChildComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/UIChildComponent.kt index 355acbab..cf4ad040 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/UIChildComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/UIChildComponent.kt @@ -13,6 +13,15 @@ interface UIChildComponent { fun showCancelledLessons(value: Boolean) + val markGrades: Value + + fun markGrades(value: Boolean) + + val passingGrade: Value + + fun passingGrade(value: String) + + } class DefaultUIChildComponent( @@ -29,4 +38,16 @@ class DefaultUIChildComponent( showCancelledLessons.value = value } + override val markGrades: MutableValue = MutableValue(Data.markGrades) + override fun markGrades(value: Boolean) { + Data.markGrades = value + markGrades.value = value + } + + override val passingGrade: MutableValue = MutableValue(Data.passingGrade.toString()) + override fun passingGrade(value: String) { + Data.passingGrade = value.toFloatOrNull() ?: 5.5f + passingGrade.value = value + } + } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/UIChildScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/UIChildScreen.kt index 4049a89d..707100d3 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/UIChildScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/items/ui/UIChildScreen.kt @@ -2,13 +2,20 @@ package nl.tiebe.otarium.ui.home.settings.items.ui import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.OutlinedTextField import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.unit.dp import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState import nl.tiebe.otarium.Data import nl.tiebe.otarium.MR import nl.tiebe.otarium.ui.home.settings.SettingsComponent +import nl.tiebe.otarium.ui.home.settings.utils.SettingRow import nl.tiebe.otarium.ui.home.settings.utils.SettingRowIconButton import nl.tiebe.otarium.ui.home.settings.utils.SettingSlider import nl.tiebe.otarium.ui.home.settings.utils.SettingsRowToggle @@ -17,6 +24,7 @@ import nl.tiebe.otarium.utils.otariumicons.Palette import nl.tiebe.otarium.utils.ui.getLocalizedString import kotlin.math.roundToInt +@OptIn(ExperimentalMaterial3Api::class) @Composable internal fun UIChildScreen(component: UIChildComponent) { Column( @@ -57,6 +65,37 @@ internal fun UIChildScreen(component: UIChildComponent) { steps = 2 ) + val checkedStateMarkGrades = component.markGrades.subscribeAsState() + + SettingsRowToggle( + leftText = AnnotatedString(getLocalizedString(MR.strings.mark_grades_red)), + checked = checkedStateMarkGrades.value, + rowClickable = true, + onClick = { + component.markGrades(it) + } + ) + + val value = component.passingGrade.subscribeAsState().value + + //voldoendegrens input field + SettingRow( + text = AnnotatedString(getLocalizedString(MR.strings.passing_grade)), + ) { + OutlinedTextField( + value = value, + onValueChange = { newValue -> + if ((newValue.replace(",", ".").replace(".", "").all { it.isDigit() } && + newValue.replace(",", ".").toFloatOrNull() != null) || newValue.isBlank()) + component.passingGrade(newValue) + }, + isError = !((value.replace(",", ".").replace(".", "").all { it.isDigit() } && + value.replace(",", ".").toFloatOrNull() != null) || value.isBlank()), + singleLine = true, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), + modifier = Modifier.width(100.dp) + ) + } } } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/utils/SettingSlider.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/utils/SettingSlider.kt index c8d77c80..03952cae 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/utils/SettingSlider.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/utils/SettingSlider.kt @@ -1,7 +1,6 @@ package nl.tiebe.otarium.ui.home.settings.utils import androidx.compose.foundation.layout.* -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.Slider import androidx.compose.material3.Text @@ -25,7 +24,7 @@ internal fun SettingSlider( valueRange: ClosedFloatingPointRange, steps: Int = 0 ) { - Column(modifier = Modifier.fillMaxWidth(0.95f).then(modifier).fillMaxHeight(), horizontalAlignment = horizontalAlignment, verticalArrangement = verticalArrangement) { + Column(modifier = Modifier.fillMaxWidth(0.95f).then(modifier), horizontalAlignment = horizontalAlignment, verticalArrangement = verticalArrangement) { Text(text = text, style = LocalTextStyle.current.merge(textStyle)) diff --git a/shared/src/commonMain/resources/MR/base/strings.xml b/shared/src/commonMain/resources/MR/base/strings.xml index f2ebbac7..eca71fd5 100644 --- a/shared/src/commonMain/resources/MR/base/strings.xml +++ b/shared/src/commonMain/resources/MR/base/strings.xml @@ -99,4 +99,14 @@ Tertiary color Show cancelled lessons + + Manually entered grades + Grades + + Grade + Weight + Name + + Mark insufficient grades red + Passing grade \ No newline at end of file diff --git a/shared/src/commonMain/resources/MR/nl/strings.xml b/shared/src/commonMain/resources/MR/nl/strings.xml index da3650c2..940d8c47 100644 --- a/shared/src/commonMain/resources/MR/nl/strings.xml +++ b/shared/src/commonMain/resources/MR/nl/strings.xml @@ -3,7 +3,6 @@ Otarium Cijfers Notificaties voor nieuwe cijfers - De app wacht nu op nieuwe cijfers en zal je een notificatie geven als er eentje binnenkomt Rooster Inloggen Otarium @@ -16,7 +15,7 @@ Wil je niet-storende advertenties in de app? Het zou me heel erg helpen! Laat advertenties zien Ik ben ouder dan 16 - Instellingen + Instellingen ma di wo @@ -91,4 +90,14 @@ Tertiaire kleur Laat lesuitval zien + + Handmatig ingevoerde cijfers + Cijfers + + Cijfer + Weging + Naam + + Onvoldoendes rood maken + Voldoendegrens \ No newline at end of file From 2af15c7899406be263ccd1d0deef2a0063146100 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Tue, 23 May 2023 14:45:49 +0200 Subject: [PATCH 70/82] more fixes --- .../ui/home/messages/message/MessageComponent.kt | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/message/MessageComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/message/MessageComponent.kt index c7159467..b3fb2cbe 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/message/MessageComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/message/MessageComponent.kt @@ -8,10 +8,10 @@ import dev.tiebe.magisterapi.response.messages.Attachment import dev.tiebe.magisterapi.response.messages.MessageData import io.ktor.client.statement.* import io.ktor.http.* -import io.ktor.utils.io.* import kotlinx.coroutines.launch import nl.tiebe.otarium.Data import nl.tiebe.otarium.ui.home.messages.MessagesComponent +import nl.tiebe.otarium.ui.home.messages.folder.FolderComponent import nl.tiebe.otarium.ui.root.componentCoroutineScope import nl.tiebe.otarium.utils.openFileFromCache import nl.tiebe.otarium.utils.requestGET @@ -19,6 +19,7 @@ import nl.tiebe.otarium.utils.writeFile interface MessageComponent { val parentComponent: MessagesComponent + val folderComponent: FolderComponent val messageLink: String val message: Value @@ -30,7 +31,8 @@ interface MessageComponent { class DefaultMessageComponent( componentContext: ComponentContext, override val messageLink: String, - override val parentComponent: MessagesComponent + override val parentComponent: MessagesComponent, + override val folderComponent: FolderComponent ): MessageComponent, ComponentContext by componentContext { val scope = componentCoroutineScope() @@ -82,6 +84,12 @@ class DefaultMessageComponent( true ) + (folderComponent.messages as MutableValue).value = folderComponent.messages.value.toMutableList().map { message -> + if (message.id == it.id ) + message.copy(hasBeenRead = true) + else message + } + unsubscribe() } } From ac1fc12de4464df595f368d3c3d22a3601e3051a Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Tue, 23 May 2023 14:47:34 +0200 Subject: [PATCH 71/82] Revert "more fixes" This reverts commit 2af15c7899406be263ccd1d0deef2a0063146100. --- .../ui/home/messages/message/MessageComponent.kt | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/message/MessageComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/message/MessageComponent.kt index b3fb2cbe..c7159467 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/message/MessageComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/message/MessageComponent.kt @@ -8,10 +8,10 @@ import dev.tiebe.magisterapi.response.messages.Attachment import dev.tiebe.magisterapi.response.messages.MessageData import io.ktor.client.statement.* import io.ktor.http.* +import io.ktor.utils.io.* import kotlinx.coroutines.launch import nl.tiebe.otarium.Data import nl.tiebe.otarium.ui.home.messages.MessagesComponent -import nl.tiebe.otarium.ui.home.messages.folder.FolderComponent import nl.tiebe.otarium.ui.root.componentCoroutineScope import nl.tiebe.otarium.utils.openFileFromCache import nl.tiebe.otarium.utils.requestGET @@ -19,7 +19,6 @@ import nl.tiebe.otarium.utils.writeFile interface MessageComponent { val parentComponent: MessagesComponent - val folderComponent: FolderComponent val messageLink: String val message: Value @@ -31,8 +30,7 @@ interface MessageComponent { class DefaultMessageComponent( componentContext: ComponentContext, override val messageLink: String, - override val parentComponent: MessagesComponent, - override val folderComponent: FolderComponent + override val parentComponent: MessagesComponent ): MessageComponent, ComponentContext by componentContext { val scope = componentCoroutineScope() @@ -84,12 +82,6 @@ class DefaultMessageComponent( true ) - (folderComponent.messages as MutableValue).value = folderComponent.messages.value.toMutableList().map { message -> - if (message.id == it.id ) - message.copy(hasBeenRead = true) - else message - } - unsubscribe() } } From afcdeb77405312e030f567eb8d3ba318ccb6e627 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Tue, 23 May 2023 14:49:16 +0200 Subject: [PATCH 72/82] much simpler fix --- .../tiebe/otarium/ui/home/messages/folder/FolderComponent.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/folder/FolderComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/folder/FolderComponent.kt index f4cf0389..1a406857 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/folder/FolderComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/folder/FolderComponent.kt @@ -4,6 +4,7 @@ import androidx.compose.foundation.ScrollState import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.value.MutableValue import com.arkivanov.decompose.value.Value +import com.arkivanov.essenty.lifecycle.doOnResume import dev.tiebe.magisterapi.api.messages.MessageFlow import dev.tiebe.magisterapi.response.messages.Message import dev.tiebe.magisterapi.response.messages.MessageFolder @@ -61,7 +62,9 @@ class DefaultFolderComponent( } init { - refresh() + lifecycle.doOnResume { + refresh() + } } } \ No newline at end of file From 54af2d87732633f3a5a88c6920da1f2de0e7d83c Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Tue, 23 May 2023 15:11:37 +0200 Subject: [PATCH 73/82] closes #60 --- .../nl/tiebe/otarium/ui/home/timetable/main/DaySelector.kt | 3 ++- .../tiebe/otarium/ui/home/timetable/main/TimetableComponent.kt | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/DaySelector.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/DaySelector.kt index 7b05bf9a..927c6c11 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/DaySelector.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/DaySelector.kt @@ -32,6 +32,7 @@ internal fun DaySelector( weekPageCount: Int ) { val scope = rememberCoroutineScope() + val selectedWeek = component.selectedWeek.subscribeAsState() HorizontalPager(pageCount = weekPageCount, state = weekPagerState) { week -> TabRow( @@ -42,7 +43,7 @@ internal fun DaySelector( dayPagerState, dayPageCount, tabPositions, - shouldShowIndicator = derivedStateOf { week == weekPageCount/2 + component.selectedWeek.value }.value + shouldShowIndicator = derivedStateOf { week == weekPageCount/2 + selectedWeek.value }.value ) ) }) { diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/TimetableComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/TimetableComponent.kt index 968c54e6..20da2d32 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/TimetableComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/TimetableComponent.kt @@ -48,6 +48,8 @@ interface TimetableComponent { fun changeDay(day: Int) { (currentPage as MutableValue).value = day + println(currentPage.value) + if (selectedWeek.value != floor((day - (amountOfDays / 2).toFloat()) / days.size).toInt()) (selectedWeek as MutableValue).value = floor((day - (amountOfDays / 2).toFloat()) / days.size).toInt() } From f53a5f41c08ff6313fe4850b169565b0ec75ac96 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Tue, 23 May 2023 17:34:55 +0200 Subject: [PATCH 74/82] migrate to libs.versions.toml --- androidApp/build.gradle.kts | 35 ++++---- build.gradle.kts | 15 ++-- buildSrc/src/main/kotlin/Dependencies.kt | 107 ----------------------- libs.versions.toml | 96 ++++++++++++++++++++ settings.gradle.kts | 9 ++ shared/build.gradle.kts | 64 +++++++------- 6 files changed, 164 insertions(+), 162 deletions(-) delete mode 100755 buildSrc/src/main/kotlin/Dependencies.kt create mode 100644 libs.versions.toml diff --git a/androidApp/build.gradle.kts b/androidApp/build.gradle.kts index e72248da..87447f3c 100644 --- a/androidApp/build.gradle.kts +++ b/androidApp/build.gradle.kts @@ -6,14 +6,14 @@ plugins { } android { - compileSdk = AndroidSdk.compile + compileSdk = libs.versions.android.sdk.compile.get().toInt() namespace = "nl.tiebe.otarium.androidApp" defaultConfig { applicationId = "nl.tiebe.otarium" - minSdk = AndroidSdk.min - targetSdk = AndroidSdk.target - versionCode = Version.appVersionCode - versionName = Version.appVersion + minSdk = libs.versions.android.sdk.min.get().toInt() + targetSdk = libs.versions.android.sdk.compile.get().toInt() + versionCode = libs.versions.app.version.code.get().toInt() + versionName = libs.versions.app.version.string.get() } buildTypes { getByName("release") { @@ -33,25 +33,26 @@ android { compose = true } composeOptions { - kotlinCompilerExtensionVersion = Version.compose_compiler + kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get() } } dependencies { implementation(project(":shared")) - implementation(Android.appcompat) - implementation(Android.material) + implementation(libs.android.appcompat) + implementation(libs.android.material) - implementation(Compose.activity) - implementation(Compose.runtime) - implementation(Compose.ui) - implementation(Compose.material3) - implementation(Compose.foundationLayout) - implementation(Compose.material) + implementation(libs.compose.activity) + implementation(libs.compose.runtime) + implementation(libs.compose.ui) + implementation(libs.compose.material3) + implementation(libs.compose.foundationlayout) + implementation(libs.compose.material) - implementation(Decompose.core) + implementation(libs.decompose.core) - implementation(project.dependencies.platform(Firebase.bom)) - implementation(Firebase.analytics) + implementation(project.dependencies.platform(libs.firebase.bom)) + implementation(libs.firebase.analytics) + implementation(libs.firebase.crashlytics) } diff --git a/build.gradle.kts b/build.gradle.kts index 11d5c15d..ed8b914a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id(completeKotlin) version Version.complete_kotlin + ///alias(libs.plugins.moko) } buildscript { @@ -12,12 +12,13 @@ buildscript { maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") } dependencies { - classpath(Kotlin.gradle) - classpath(Android.gradle) - classpath(Kotlin.serialization) - classpath(Moko.gradle) - classpath(BuildKonfig.gradle) - classpath(Firebase.classpath) + classpath(libs.kotlin.gradle) + classpath(libs.android.gradle) + classpath(libs.kotlin.gradle) + classpath(libs.kotlin.serialization) + classpath(libs.moko.gradle) + classpath(libs.buildkonfig.gradle) + classpath(libs.firebase.gradle) } } diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt deleted file mode 100755 index bf5ca135..00000000 --- a/buildSrc/src/main/kotlin/Dependencies.kt +++ /dev/null @@ -1,107 +0,0 @@ - -object Version { - const val appVersion = "3.3.0-alpha01" - const val appVersionCode = 32 - - const val magister = "1.1.10-alpha02" - - const val kotlin = "1.8.0" - const val gradle = "7.4.1" - const val appcompat = "1.6.1" - const val material = "1.4.0" - const val compose = "1.4.0" - const val compose_android = "1.4.3" - const val compose_compiler = "1.4.0" - const val ktor = "2.3.0" - const val decompose = "2.0.0-compose-experimental-alpha-02" - const val moko = "0.22.0" - const val buildkonfig = "0.13.3" - const val gms = "4.3.15" - const val firebase = "31.5.0" - const val multiplatform_settings = "1.0.0" - const val admob = "22.0.0" - const val guava = "31.1-android" - const val guava_coroutines = "1.7.0-RC" - - const val complete_kotlin = "1.1.0" - const val colormath = "3.2.0" -} - -object Compose { - const val activity = "androidx.activity:activity-compose:1.7.1" - const val runtime = "androidx.compose.runtime:runtime:${Version.compose_android}" - const val ui = "androidx.compose.ui:ui:${Version.compose_android}" - const val foundationLayout = "androidx.compose.foundation:foundation-layout:${Version.compose_android}" - const val material = "androidx.compose.material:material:${Version.compose_android}" - const val material3 = "androidx.compose.material3:material3:1.0.1" -} - - -object AndroidSdk { - const val min = 21 - const val compile = 33 - const val target = compile -} - -object iOSSdk { - const val deploymentTarget = "11.0" -} - -object Android { - const val appcompat = "androidx.appcompat:appcompat:${Version.appcompat}" - const val material = "com.google.android.material:material:${Version.material}" - const val gradle = "com.android.tools.build:gradle:${Version.gradle}" -} - -object Kotlin { - const val gradle = "org.jetbrains.kotlin:kotlin-gradle-plugin:${Version.kotlin}" - const val serialization = "org.jetbrains.kotlin:kotlin-serialization:${Version.kotlin}" - const val dateTime = "org.jetbrains.kotlinx:kotlinx-datetime:0.4.0" -} - -object Ktor { - const val client_core = "io.ktor:ktor-client-core:${Version.ktor}" - const val client_content_negotiation = "io.ktor:ktor-client-content-negotiation:${Version.ktor}" - const val client_logging = "io.ktor:ktor-client-logging:${Version.ktor}" - const val serialization_json = "io.ktor:ktor-serialization-kotlinx-json:${Version.ktor}" - const val client_logging_jvm = "io.ktor:ktor-client-logging-jvm:${Version.ktor}" - const val client_json_jvm = "io.ktor:ktor-client-json-jvm:${Version.ktor}" - const val client_android = "io.ktor:ktor-client-okhttp:${Version.ktor}" - const val client_ios = "io.ktor:ktor-client-ios:${Version.ktor}" -} - -object Moko { - const val gradle = "dev.icerock.moko:resources-generator:${Version.moko}" - const val plugin = "dev.icerock.mobile.multiplatform-resources" - const val api = "dev.icerock.moko:resources:${Version.moko}" - const val compose = "dev.icerock.moko:resources-compose:${Version.moko}" -} - -object BuildKonfig { - const val gradle = "com.codingfeline.buildkonfig:buildkonfig-gradle-plugin:${Version.buildkonfig}" - const val plugin = "com.codingfeline.buildkonfig" -} - -object Firebase { - const val classpath = "com.google.gms:google-services:${Version.gms}" - const val bom = "com.google.firebase:firebase-bom:${Version.firebase}" - const val analytics = "com.google.firebase:firebase-analytics-ktx" -} - -object Decompose { - const val core = "com.arkivanov.decompose:decompose:${Version.decompose}" - const val compose = "com.arkivanov.decompose:extensions-compose-jetbrains:${Version.decompose}" -} - -const val russhwolf_settings = "com.russhwolf:multiplatform-settings-no-arg:${Version.multiplatform_settings}" -const val admob = "com.google.android.gms:play-services-ads:${Version.admob}" - -const val magisterAPI = "dev.tiebe:magisterapi:${Version.magister}" - -const val completeKotlin = "com.louiscad.complete-kotlin" -const val colorMath = "com.github.ajalt.colormath:colormath:${Version.colormath}" - -object Guava { - const val core = "com.google.guava:guava:${Version.guava}" - const val coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-guava:${Version.guava_coroutines}" -} \ No newline at end of file diff --git a/libs.versions.toml b/libs.versions.toml new file mode 100644 index 00000000..1da69c78 --- /dev/null +++ b/libs.versions.toml @@ -0,0 +1,96 @@ +[versions] +app-version-string = "3.3.0-alpha01" +app-version-code = "32" + +android_sdk-min = "21" +android_sdk-compile = "33" + +ios_target = "11.0" + +magister = "1.1.10-alpha02" + +kotlin = "1.8.0" +gradle = "7.4.1" +appcompat = "1.6.1" +material = "1.4.0" +compose = "1.4.0" +compose-android = "1.4.3" +compose-compiler = "1.4.0" +compose-activity = "1.7.1" +material3 = "1.0.1" +ktor = "2.3.0" +decompose = "2.0.0-compose-experimental-alpha-02" +moko = "0.22.0" +buildkonfig = "0.13.3" +gms = "4.3.15" +firebase = "32.0.0" +multiplatform-settings = "1.0.0" +admob = "22.0.0" +guava = "31.1-android" +guava-coroutines = "1.7.0-RC" + +complete-kotlin = "1.1.0" +colormath = "3.2.0" + +kotlin-datetime = "0.4.0" + + +[libraries] +compose-activity = { group = "androidx.activity", name = "activity-compose", version.ref = "compose-activity" } +compose-runtime = { group = "androidx.compose.runtime", name = "runtime", version.ref = "compose-android" } +compose-ui = { group = "androidx.compose.ui", name = "ui", version.ref = "compose-android" } +compose-foundationlayout = { group = "androidx.compose.foundation", name = "foundation-layout", version.ref = "compose-android" } +compose-material = { group = "androidx.compose.material", name = "material", version.ref = "compose-android" } +compose-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" } + +android-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } +android-material = { group = "com.google.android.material", name = "material", version.ref = "material" } +android-gradle = { group = "com.android.tools.build", name = "gradle", version.ref = "gradle" } + +kotlin-serialization = { group = "org.jetbrains.kotlin", name = "kotlin-serialization", version.ref = "kotlin" } +kotlin-datetime = { group = "org.jetbrains.kotlinx", name = "kotlinx-datetime", version.ref = "kotlin-datetime" } +kotlin-gradle = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" } + +ktor-client-core = { group = "io.ktor", name = "ktor-client-core", version.ref = "ktor" } +ktor-client-content_negotiation = { group = "io.ktor", name = "ktor-client-content-negotiation", version.ref = "ktor" } +ktor-client-logging = { group = "io.ktor", name = "ktor-client-logging", version.ref = "ktor" } +ktor-serialization_json = { group = "io.ktor", name = "ktor-serialization-kotlinx-json", version.ref = "ktor" } +ktor-client-logging_jvm = { group = "io.ktor", name = "ktor-client-logging-jvm", version.ref = "ktor" } +ktor-client-json_jvm = { group = "io.ktor", name = "ktor-client-json-jvm", version.ref = "ktor" } +ktor-client-android = { group = "io.ktor", name = "ktor-client-android", version.ref = "ktor" } +ktor-client-ios = { group = "io.ktor", name = "ktor-client-ios", version.ref = "ktor" } + +moko-gradle = { group = "dev.icerock.moko", name = "resources-generator", version.ref = "moko" } +moko-resources-core = { group = "dev.icerock.moko", name = "resources", version.ref = "moko" } +moko-resources-compose = { group = "dev.icerock.moko", name = "resources-compose", version.ref = "moko" } + +buildkonfig-gradle = { group = "com.codingfeline.buildkonfig", name = "buildkonfig-gradle-plugin", version.ref = "buildkonfig" } + +firebase-bom = { group = "com.google.firebase", name = "firebase-bom", version.ref = "firebase" } +firebase-analytics = { group = "com.google.firebase", name = "firebase-analytics-ktx" } +firebase-crashlytics = { group = "com.google.firebase", name = "firebase-crashlytics-ktx" } +firebase-gradle = { group = "com.google.gms", name = "google-services", version.ref = "gms" } + +decompose-core = { group = "com.arkivanov.decompose", name = "decompose", version.ref = "decompose" } +decompose-compose = { group = "com.arkivanov.decompose", name = "extensions-compose-jetbrains", version.ref = "decompose" } + +multiplatform-settings = { group = "com.russhwolf", name = "multiplatform-settings", version.ref = "multiplatform-settings" } + +admob = { group = "com.google.android.gms", name = "play-services-ads", version.ref = "admob" } + +magister-api = { group = "dev.tiebe", name = "magisterapi", version.ref = "magister" } + +color-math = { group = "com.github.ajalt.colormath", name = "colormath", version.ref = "colormath" } + +guava-core = { group = "com.google.guava", name = "guava", version.ref = "guava" } +guava-coroutines = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-guava", version.ref = "guava-coroutines" } + +[plugins] +mokoresources = { id = "dev.icerock.mobile.multiplatform-resources", version.ref = "moko" } + +complete_kotlin = { id = "com.louiscad.complete-kotlin", version.ref = "complete-kotlin" } + +compose = { id = "org.jetbrains.compose", version.ref = "compose" } +buildkonfig = { id = "com.codingfeline.buildkonfig", version.ref = "buildkonfig" } + +google_services = { id = "com.google.gms.google-services", version.ref = "gms" } \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index ae3abb03..f607aa29 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -5,6 +5,15 @@ pluginManagement { mavenCentral() } } + +dependencyResolutionManagement { + versionCatalogs { + create("libs") { + from(files("./libs.versions.toml")) + } + } +} + rootProject.name = "Otarium" include(":androidApp") diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts index 5e9be125..6f72aed6 100644 --- a/shared/build.gradle.kts +++ b/shared/build.gradle.kts @@ -2,25 +2,25 @@ import com.codingfeline.buildkonfig.compiler.FieldSpec.Type.INT import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget +@Suppress("DSL_SCOPE_VIOLATION") plugins { kotlin("multiplatform") kotlin("native.cocoapods") id("com.android.library") id("kotlinx-serialization") - id("org.jetbrains.compose") version Version.compose - id("com.codingfeline.buildkonfig") - id("dev.icerock.mobile.multiplatform-resources") - id("com.google.gms.google-services") id("kotlin-parcelize") + alias(libs.plugins.compose) + id(libs.plugins.buildkonfig.get().pluginId) + id(libs.plugins.mokoresources.get().pluginId) + id(libs.plugins.google.services.get().pluginId) } -version = Version.appVersion +version = libs.versions.app.version.string.get() android { - compileSdk = AndroidSdk.compile + compileSdk = libs.versions.android.sdk.compile.get().toInt() defaultConfig { - minSdk = AndroidSdk.min - targetSdk = AndroidSdk.target + minSdk = libs.versions.android.sdk.min.get().toInt() } namespace = "nl.tiebe.otarium" @@ -43,14 +43,14 @@ kotlin { cocoapods { summary = "Otarium" homepage = "https://otarium.groosman.nl" - ios.deploymentTarget = iOSSdk.deploymentTarget + ios.deploymentTarget = libs.versions.ios.target.get() podfile = project.file("../iosApp/Podfile") framework { baseName = "shared" isStatic = false - binaryOption("bundleVersion", Version.appVersionCode.toString()) - binaryOption("bundleShortVersionString", Version.appVersion) + binaryOption("bundleVersion",libs.versions.app.version.string.get()) + binaryOption("bundleShortVersionString", libs.versions.app.version.code.get()) } pod("Google-Mobile-Ads-SDK") { @@ -60,44 +60,46 @@ kotlin { sourceSets { val commonMain by getting { dependencies { - implementation(Ktor.client_core) - implementation(Ktor.client_content_negotiation) - implementation(Ktor.client_logging) - implementation(Ktor.serialization_json) + implementation(libs.ktor.client.core) + implementation(libs.ktor.client.content.negotiation) + implementation(libs.ktor.client.logging) + implementation(libs.ktor.serialization.json) + implementation(compose.ui) implementation(compose.foundation) implementation(compose.material) implementation(compose.material3) implementation(compose.runtime) - api(Moko.api) - api(Moko.compose) - implementation(Kotlin.dateTime) - implementation(russhwolf_settings) + api(libs.moko.resources.core) + api(libs.moko.resources.compose) + + implementation(libs.kotlin.datetime) + implementation(libs.multiplatform.settings) + implementation(libs.decompose.core) + implementation(libs.decompose.compose) - implementation(Decompose.core) - implementation(Decompose.compose) + implementation(libs.magister.api) + implementation(libs.color.math) - implementation(magisterAPI) - implementation(colorMath) } } val androidMain by getting { dependencies { - implementation(Ktor.client_logging_jvm) - implementation(Ktor.client_json_jvm) - implementation(Ktor.client_android) + implementation(libs.ktor.client.logging.jvm) + implementation(libs.ktor.client.json.jvm) + implementation(libs.ktor.client.android) - implementation(admob) + implementation(libs.admob) - implementation(Guava.core) - implementation(Guava.coroutines) + implementation(libs.guava.core) + implementation(libs.guava.coroutines) } } val iosMain by getting { dependencies { - implementation(Ktor.client_ios) + implementation(libs.ktor.client.ios) } } } @@ -115,7 +117,7 @@ buildkonfig { packageName = "nl.tiebe.otarium" defaultConfigs { - buildConfigField(INT, "versionCode", Version.appVersionCode.toString()) + buildConfigField(INT, "versionCode", libs.versions.app.version.code.get()) } } From ca4ee6a2245fb12b3f4a2d22aefb57b7bd582855 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Tue, 23 May 2023 19:34:25 +0200 Subject: [PATCH 75/82] crashlytics --- androidApp/build.gradle.kts | 2 ++ build.gradle.kts | 4 ++- buildSrc/.gitignore | 1 - buildSrc/build.gradle.kts | 7 ----- libs.versions.toml | 9 ++++--- .../otarium/utils/ui/ResourcesAndroid.kt | 4 --- .../otarium/ui/home/debug/DebugScreen.kt | 10 +++++++ .../home/timetable/TimetableRootComponent.kt | 4 +-- .../ui/home/timetable/TimetableRootScreen.kt | 27 ++----------------- .../home/timetable/main/TimetableComponent.kt | 5 ++-- 10 files changed, 28 insertions(+), 45 deletions(-) delete mode 100755 buildSrc/.gitignore delete mode 100755 buildSrc/build.gradle.kts diff --git a/androidApp/build.gradle.kts b/androidApp/build.gradle.kts index 87447f3c..a91ccd1d 100644 --- a/androidApp/build.gradle.kts +++ b/androidApp/build.gradle.kts @@ -1,8 +1,10 @@ +@Suppress("DSL_SCOPE_VIOLATION") plugins { id("com.android.application") kotlin("android") id("kotlin-parcelize") id("kotlinx-serialization") + id(libs.plugins.firebase.crashlytics.get().pluginId) } android { diff --git a/build.gradle.kts b/build.gradle.kts index ed8b914a..1845a4df 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,6 @@ +@Suppress("DSL_SCOPE_VIOLATION") plugins { - ///alias(libs.plugins.moko) + alias(libs.plugins.complete.kotlin) } buildscript { @@ -19,6 +20,7 @@ buildscript { classpath(libs.moko.gradle) classpath(libs.buildkonfig.gradle) classpath(libs.firebase.gradle) + classpath(libs.firebase.crashlyticsgradle) } } diff --git a/buildSrc/.gitignore b/buildSrc/.gitignore deleted file mode 100755 index 378eac25..00000000 --- a/buildSrc/.gitignore +++ /dev/null @@ -1 +0,0 @@ -build diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts deleted file mode 100755 index d317042e..00000000 --- a/buildSrc/build.gradle.kts +++ /dev/null @@ -1,7 +0,0 @@ -repositories { - mavenCentral() -} - -plugins { - `kotlin-dsl` -} diff --git a/libs.versions.toml b/libs.versions.toml index 1da69c78..917e920d 100644 --- a/libs.versions.toml +++ b/libs.versions.toml @@ -20,7 +20,7 @@ compose-activity = "1.7.1" material3 = "1.0.1" ktor = "2.3.0" decompose = "2.0.0-compose-experimental-alpha-02" -moko = "0.22.0" +moko = "0.22.2" buildkonfig = "0.13.3" gms = "4.3.15" firebase = "32.0.0" @@ -34,6 +34,7 @@ colormath = "3.2.0" kotlin-datetime = "0.4.0" +crashlytics = "2.9.5" [libraries] compose-activity = { group = "androidx.activity", name = "activity-compose", version.ref = "compose-activity" } @@ -69,12 +70,13 @@ buildkonfig-gradle = { group = "com.codingfeline.buildkonfig", name = "buildkonf firebase-bom = { group = "com.google.firebase", name = "firebase-bom", version.ref = "firebase" } firebase-analytics = { group = "com.google.firebase", name = "firebase-analytics-ktx" } firebase-crashlytics = { group = "com.google.firebase", name = "firebase-crashlytics-ktx" } +firebase-crashlyticsgradle = { group = "com.google.firebase", name = "firebase-crashlytics-gradle", version.ref = "crashlytics" } firebase-gradle = { group = "com.google.gms", name = "google-services", version.ref = "gms" } decompose-core = { group = "com.arkivanov.decompose", name = "decompose", version.ref = "decompose" } decompose-compose = { group = "com.arkivanov.decompose", name = "extensions-compose-jetbrains", version.ref = "decompose" } -multiplatform-settings = { group = "com.russhwolf", name = "multiplatform-settings", version.ref = "multiplatform-settings" } +multiplatform-settings = { group = "com.russhwolf", name = "multiplatform-settings-no-arg", version.ref = "multiplatform-settings" } admob = { group = "com.google.android.gms", name = "play-services-ads", version.ref = "admob" } @@ -93,4 +95,5 @@ complete_kotlin = { id = "com.louiscad.complete-kotlin", version.ref = "complete compose = { id = "org.jetbrains.compose", version.ref = "compose" } buildkonfig = { id = "com.codingfeline.buildkonfig", version.ref = "buildkonfig" } -google_services = { id = "com.google.gms.google-services", version.ref = "gms" } \ No newline at end of file +google_services = { id = "com.google.gms.google-services", version.ref = "gms" } +firebase_crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "firebase" } \ No newline at end of file diff --git a/shared/src/androidMain/kotlin/nl/tiebe/otarium/utils/ui/ResourcesAndroid.kt b/shared/src/androidMain/kotlin/nl/tiebe/otarium/utils/ui/ResourcesAndroid.kt index a1179d76..3c3eb01c 100644 --- a/shared/src/androidMain/kotlin/nl/tiebe/otarium/utils/ui/ResourcesAndroid.kt +++ b/shared/src/androidMain/kotlin/nl/tiebe/otarium/utils/ui/ResourcesAndroid.kt @@ -14,10 +14,6 @@ object Android { lateinit var window: Window lateinit var requestPermissionLauncher: ActivityResultLauncher lateinit var context: Context - - lateinit var okHttpClient: okhttp3.OkHttpClient - val isOkHttpClientInitialized: Boolean - get() = ::okHttpClient.isInitialized } actual fun getLocalizedString(string: StringResource): String { diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/debug/DebugScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/debug/DebugScreen.kt index 33c14dc1..e194a66a 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/debug/DebugScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/debug/DebugScreen.kt @@ -13,7 +13,9 @@ import nl.tiebe.otarium.Data import nl.tiebe.otarium.magister.refreshGrades import nl.tiebe.otarium.setupNotifications import nl.tiebe.otarium.ui.home.settings.utils.SettingRowIconButton +import nl.tiebe.otarium.utils.OtariumIcons import nl.tiebe.otarium.utils.getClipboardText +import nl.tiebe.otarium.utils.otariumicons.BugOutline import nl.tiebe.otarium.utils.refreshGradesBackground import nl.tiebe.otarium.utils.sendNotification import kotlin.random.Random @@ -93,6 +95,14 @@ internal fun DebugScreen(component: DebugComponent) { ) { component.changeLanguage() } + + SettingRowIconButton( + leftText = AnnotatedString("Test crash"), + icon = OtariumIcons.BugOutline, + rowClickable = true, + ) { + throw RuntimeException("Test crash") + } } } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/TimetableRootComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/TimetableRootComponent.kt index 6cd09cd8..e6a91679 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/TimetableRootComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/TimetableRootComponent.kt @@ -47,8 +47,9 @@ interface TimetableRootComponent : MenuItemComponent { class DefaultTimetableRootComponent(componentContext: ComponentContext): TimetableRootComponent, ComponentContext by componentContext { override val navigation = StackNavigation() + override val onBack: MutableValue = MutableValue(BackCallback { back() }) - override val timetableComponent = DefaultTimetableComponent(componentContext, ::navigate, ::back) + override val timetableComponent = DefaultTimetableComponent(componentContext, ::navigate, onBack) override val childStack: Value> = childStack( @@ -58,7 +59,6 @@ class DefaultTimetableRootComponent(componentContext: ComponentContext): Timetab childFactory = ::createChild, ) - override val onBack: MutableValue = MutableValue(BackCallback { back() }) override fun registerBackHandler() { backHandler.register(onBack.value) } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/TimetableRootScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/TimetableRootScreen.kt index 89fd8d8e..f5cd9eed 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/TimetableRootScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/TimetableRootScreen.kt @@ -1,15 +1,11 @@ package nl.tiebe.otarium.ui.home.timetable -import androidx.compose.animation.core.FiniteAnimationSpec import androidx.compose.animation.core.tween import androidx.compose.material.* import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier -import androidx.compose.ui.layout.layout -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.StackAnimator -import com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.stackAnimator import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState import com.arkivanov.essenty.backhandler.BackCallback import kotlinx.coroutines.launch @@ -44,6 +40,7 @@ internal fun TimetableRootScreen(component: TimetableRootComponent) { SwipeToDismiss( state = state, dismissThresholds = { FractionalThreshold(0.5f) }, + directions = setOf(DismissDirection.StartToEnd), background = { } ) { @@ -52,24 +49,4 @@ internal fun TimetableRootScreen(component: TimetableRootComponent) { } } -} - -@Composable -internal fun slide(animationSpec: FiniteAnimationSpec = tween(), factor: Float): StackAnimator { - //println(factor) - - val animator = stackAnimator(animationSpec = animationSpec) { animationFactor, _, content -> - content(Modifier.offsetXFactor(factor = maxOf(animationFactor, factor))) - } - - return animator -} - -private fun Modifier.offsetXFactor(factor: Float): Modifier = - layout { measurable, constraints -> - val placeable = measurable.measure(constraints) - - layout(placeable.width, placeable.height) { - placeable.placeRelative(x = (placeable.width.toFloat() * factor).toInt(), y = 0) - } - } +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/TimetableComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/TimetableComponent.kt index 20da2d32..c6cfaef8 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/TimetableComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/timetable/main/TimetableComponent.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.pager.PagerState import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.value.MutableValue import com.arkivanov.decompose.value.Value +import com.arkivanov.essenty.backhandler.BackCallback import dev.tiebe.magisterapi.response.general.year.agenda.AgendaItem import dev.tiebe.magisterapi.utils.MagisterException import kotlinx.coroutines.CoroutineScope @@ -97,7 +98,7 @@ interface TimetableComponent { class DefaultTimetableComponent( componentContext: ComponentContext, val navigate: (TimetableRootComponent.Config) -> Unit, - val back: () -> Unit, + val back: MutableValue, ): TimetableComponent, ComponentContext by componentContext { override val now: MutableValue = MutableValue(Clock.System.now().toLocalDateTime(TimeZone.of("Europe/Amsterdam"))) override val currentPage = MutableValue(500 + now.value.date.dayOfWeek.ordinal) @@ -163,7 +164,7 @@ class DefaultTimetableComponent( } override fun closeItemPopup() { - back() + back.value.onBack() } From 872b042d5b99e84e38a5ddb651e2e1a3ddda6058 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Tue, 23 May 2023 20:26:30 +0200 Subject: [PATCH 76/82] progress --- androidApp/build.gradle.kts | 1 + .../calculation/subject/GCSubjectList.kt | 132 +++++++++++------- 2 files changed, 85 insertions(+), 48 deletions(-) diff --git a/androidApp/build.gradle.kts b/androidApp/build.gradle.kts index a91ccd1d..5228a255 100644 --- a/androidApp/build.gradle.kts +++ b/androidApp/build.gradle.kts @@ -4,6 +4,7 @@ plugins { kotlin("android") id("kotlin-parcelize") id("kotlinx-serialization") + id(libs.plugins.google.services.get().pluginId) id(libs.plugins.firebase.crashlytics.get().pluginId) } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/subject/GCSubjectList.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/subject/GCSubjectList.kt index 773aa870..cfdaf0f3 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/subject/GCSubjectList.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/grades/calculation/subject/GCSubjectList.kt @@ -1,15 +1,25 @@ package nl.tiebe.otarium.ui.home.grades.calculation.subject -import androidx.compose.animation.AnimatedContent -import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.* +import androidx.compose.material.DismissDirection +import androidx.compose.material.DismissValue +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.FractionalThreshold +import androidx.compose.material.SwipeToDismiss +import androidx.compose.material.rememberDismissState +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ListItem +import androidx.compose.material3.ListItemDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier @@ -26,7 +36,10 @@ import nl.tiebe.otarium.ui.home.grades.calculation.calculateAverage import nl.tiebe.otarium.ui.utils.topBottomRectBorder import nl.tiebe.otarium.utils.ui.format -@OptIn(ExperimentalMaterial3Api::class, ExperimentalAnimationApi::class) + +@OptIn( + ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class, +) @Composable internal fun GCSubjectList( component: GradeCalculationChildComponent, @@ -36,56 +49,79 @@ internal fun GCSubjectList( val subjects = grades.map { it.grade.subject }.distinct().sortedBy { it.description.lowercase() } val popupItem = component.openedSubject.subscribeAsState() + val state = rememberDismissState(DismissValue.DismissedToEnd) - AnimatedContent( - targetState = popupItem.value.first, - modifier = Modifier.fillMaxSize() - ) { visible -> - if (visible) { - GCSubjectPopup( - component = component, - subject = popupItem.value.second!!, - realGradeList = grades.filter { it.grade.subject.id == popupItem.value.second?.id } - ) + LaunchedEffect(popupItem.value) { + if (popupItem.value.first) { + state.animateTo(DismissValue.Default) } else { - Column( - modifier = Modifier - .fillMaxSize() - .verticalScroll(rememberScrollState()), - ) { - subjects.forEach { subject -> - val gradeList = remember { - grades.filter { it.grade.subject.id == subject.id }.map { - (it.grade.grade?.replace(',', '.')?.toFloatOrNull() ?: 0f) to it.gradeInfo.weight.toFloat() - } + manualGrades.filter { it.subjectId == subject.id }.map { - (it.grade.toFloatOrNull() ?: 0f) to it.weight - } - } + state.animateTo(DismissValue.DismissedToEnd) + } + } - ListItem( - modifier = Modifier - .topBottomRectBorder(brush = SolidColor(MaterialTheme.colorScheme.outline)) - .clickable { component.openSubject(subject) }, - headlineText = { Text(subject.description.replaceFirstChar { - if (it.isLowerCase()) it.titlecase() else it.toString() - }) }, - trailingContent = { - val average = derivedStateOf { calculateAverage(gradeList) } + LaunchedEffect(state.currentValue) { + if (state.currentValue != DismissValue.Default) { + component.closeSubject() + } + } - Text( - text = average.value.format(1), - textAlign = TextAlign.Center, - modifier = Modifier.padding(2.dp), - color = if (average.value < Data.passingGrade) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.onSurface, - style = MaterialTheme.typography.displaySmall.copy(fontSize = 18.sp) - ) - }, - colors = ListItemDefaults.colors( - containerColor = MaterialTheme.colorScheme.inverseOnSurface - ), - ) + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()), + ) { + subjects.forEach { subject -> + val gradeList = remember { + grades.filter { it.grade.subject.id == subject.id }.map { + (it.grade.grade?.replace(',', '.')?.toFloatOrNull() ?: 0f) to it.gradeInfo.weight.toFloat() + } + manualGrades.filter { it.subjectId == subject.id }.map { + (it.grade.toFloatOrNull() ?: 0f) to it.weight } } + + ListItem( + modifier = Modifier + .topBottomRectBorder(brush = SolidColor(MaterialTheme.colorScheme.outline)) + .clickable { component.openSubject(subject) }, + headlineText = { Text(subject.description.replaceFirstChar { + if (it.isLowerCase()) it.titlecase() else it.toString() + }) }, + trailingContent = { + val average = derivedStateOf { calculateAverage(gradeList) } + + Text( + text = average.value.format(1), + textAlign = TextAlign.Center, + modifier = Modifier.padding(2.dp), + color = if (average.value < Data.passingGrade) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.onSurface, + style = MaterialTheme.typography.displaySmall.copy(fontSize = 18.sp) + ) + }, + colors = ListItemDefaults.colors( + containerColor = MaterialTheme.colorScheme.inverseOnSurface + ), + ) + } + } + + SwipeToDismiss( + state = state, + dismissThresholds = { + FractionalThreshold(0.5f) + }, + directions = setOf(DismissDirection.StartToEnd), + background = { + + } + ) { + if (popupItem.value.first) { + Surface(Modifier.fillMaxSize()) { + GCSubjectPopup( + component = component, + subject = popupItem.value.second!!, + realGradeList = grades.filter { it.grade.subject.id == popupItem.value.second?.id } + ) + } } } From d7ec330872f8fa6b3b47a76943391536233b14ce Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Tue, 23 May 2023 22:28:36 +0200 Subject: [PATCH 77/82] commit --- .../ui/home/messages/MessagesScreen.kt | 81 +++++++++++++++---- 1 file changed, 67 insertions(+), 14 deletions(-) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/MessagesScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/MessagesScreen.kt index 03b2132e..a3a0a4a7 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/MessagesScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/MessagesScreen.kt @@ -1,19 +1,35 @@ package nl.tiebe.otarium.ui.home.messages -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material.DismissDirection import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.SwipeToDismiss import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.pullrefresh.PullRefreshIndicator import androidx.compose.material.pullrefresh.pullRefresh import androidx.compose.material.pullrefresh.rememberPullRefreshState -import androidx.compose.material3.* +import androidx.compose.material.rememberDismissState +import androidx.compose.material3.Divider +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable +import androidx.compose.runtime.State import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp +import com.arkivanov.decompose.Child import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState +import com.arkivanov.decompose.router.stack.ChildStack import com.arkivanov.decompose.router.stack.pop import dev.icerock.moko.resources.compose.stringResource import nl.tiebe.otarium.MR @@ -21,22 +37,52 @@ import nl.tiebe.otarium.ui.home.messages.folder.FolderScreen import nl.tiebe.otarium.ui.home.messages.message.MessageScreen import nl.tiebe.otarium.ui.home.messages.message.receiver.ReceiverInfoScreen -@OptIn(ExperimentalMaterial3Api::class) +@OptIn(ExperimentalMaterialApi::class) @Composable internal fun MessagesScreen(component: MessagesComponent) { - Column { - val screen = component.childStack.subscribeAsState() + val screen = component.childStack.subscribeAsState() + + Box(modifier = Modifier.padding(start = 5.dp, end = 5.dp)) { + MessageScreenChild(component, screen, screen.value.items[0], false) - val name = when (val child = screen.value.active.instance) { - is MessagesComponent.Child.FolderChild -> child.component.folder.name - is MessagesComponent.Child.MessageChild -> child.component.message.subscribeAsState().value.subject - else -> stringResource(MR.strings.messagesItem) + for (item in screen.value.items.subList(1, screen.value.items.size)) { + val state = rememberDismissState() + + //pop on finish + if (state.isDismissed(DismissDirection.StartToEnd)) { + component.navigation.pop() + } + + SwipeToDismiss( + state = state, + background = { + }, + directions = setOf(DismissDirection.StartToEnd) + ) { + MessageScreenChild(component, screen, item) + } } + } +} +@OptIn(ExperimentalMaterial3Api::class) +@Composable +internal fun MessageScreenChild( + component: MessagesComponent, + screen: State>, + item: Child.Created, + poppable: Boolean = screen.value.backStack.isNotEmpty() +) { + val name = when (val child = item.instance) { + is MessagesComponent.Child.FolderChild -> child.component.folder.name + is MessagesComponent.Child.MessageChild -> child.component.message.subscribeAsState().value.subject + else -> stringResource(MR.strings.messagesItem) + } + Column { TopAppBar( title = { Text(name, overflow = TextOverflow.Ellipsis, maxLines = 1) }, navigationIcon = { - if (screen.value.backStack.isNotEmpty()) { + if (poppable) { IconButton(onClick = { component.navigation.pop() }) { Icon(Icons.Default.ArrowBack, contentDescription = "Back") } @@ -45,8 +91,8 @@ internal fun MessagesScreen(component: MessagesComponent) { windowInsets = WindowInsets(0.dp) ) - Box(modifier = Modifier.padding(start = 5.dp, end = 5.dp)) { - when (val child = screen.value.active.instance) { + Surface(modifier = Modifier.fillMaxSize()) { + when (val child = item.instance) { is MessagesComponent.Child.FolderChild -> FolderScreen(child.component) is MessagesComponent.Child.MainChild -> MessagesFolderSelectScreen(child.component) is MessagesComponent.Child.MessageChild -> MessageScreen(child.component) @@ -60,7 +106,10 @@ internal fun MessagesScreen(component: MessagesComponent) { @Composable internal fun MessagesFolderSelectScreen(component: MessagesComponent) { val foldersState = component.folders.subscribeAsState() - val refreshState = rememberPullRefreshState(refreshing = component.refreshState.subscribeAsState().value, onRefresh = component::getFolders) + val refreshState = rememberPullRefreshState( + refreshing = component.refreshState.subscribeAsState().value, + onRefresh = component::getFolders + ) Box(modifier = Modifier.fillMaxSize().pullRefresh(refreshState)) { val folders = foldersState.value.filter { it.parentId == 0 } @@ -72,6 +121,10 @@ internal fun MessagesFolderSelectScreen(component: MessagesComponent) { } } - PullRefreshIndicator(component.refreshState.subscribeAsState().value, refreshState, modifier = Modifier.align(Alignment.TopCenter)) + PullRefreshIndicator( + component.refreshState.subscribeAsState().value, + refreshState, + modifier = Modifier.align(Alignment.TopCenter) + ) } } \ No newline at end of file From 4a9914c88c996fd454ebd8590355c73ea1074b3c Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Tue, 23 May 2023 22:46:46 +0200 Subject: [PATCH 78/82] bugfix --- libs.versions.toml | 2 +- .../nl/tiebe/otarium/ui/home/messages/MessagesComponent.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libs.versions.toml b/libs.versions.toml index 917e920d..640c84ca 100644 --- a/libs.versions.toml +++ b/libs.versions.toml @@ -7,7 +7,7 @@ android_sdk-compile = "33" ios_target = "11.0" -magister = "1.1.10-alpha02" +magister = "1.1.10-alpha03" kotlin = "1.8.0" gradle = "7.4.1" diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/MessagesComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/MessagesComponent.kt index 233b8ea2..44619fa9 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/MessagesComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/MessagesComponent.kt @@ -45,7 +45,7 @@ interface MessagesComponent: MenuItemComponent { } fun navigateToMessage(message: Message) { - navigate(Config.Message(message.links.self.href)) + navigate(Config.Message(message.links.self?.href ?: return)) } val folders: Value> From 5369dc59fe9e53097452a2fe4d2fc7c8f72f9d6b Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Wed, 24 May 2023 11:12:46 +0200 Subject: [PATCH 79/82] bugfixes --- .../home/children/StoreMessagesComponent.kt | 11 ++++++ .../ui/home/messages/MessagesComponent.kt | 36 ++++++++++++++++++- .../ui/home/messages/MessagesScreen.kt | 12 ++++++- 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/store/component/home/children/StoreMessagesComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/store/component/home/children/StoreMessagesComponent.kt index d9fc9afe..311b8272 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/store/component/home/children/StoreMessagesComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/store/component/home/children/StoreMessagesComponent.kt @@ -4,8 +4,10 @@ import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.router.stack.ChildStack import com.arkivanov.decompose.router.stack.StackNavigation import com.arkivanov.decompose.router.stack.childStack +import com.arkivanov.decompose.router.stack.pop import com.arkivanov.decompose.value.MutableValue import com.arkivanov.decompose.value.Value +import com.arkivanov.essenty.backhandler.BackCallback import dev.tiebe.magisterapi.response.messages.MessageFolder import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay @@ -24,7 +26,15 @@ class StoreMessagesComponent( override val refreshState: MutableValue = MutableValue(false) override val scope: CoroutineScope = componentCoroutineScope() + override val onBack: MutableValue<() -> Unit> = MutableValue { navigation.pop() } override val folders: MutableValue> = MutableValue(listOf()) + override fun registerBackHandler() { + backHandler.register(BackCallback { onBack.value() }) + } + + override fun unregisterBackHandler() { + + } override val navigation = StackNavigation() @@ -82,6 +92,7 @@ class StoreMessagesComponent( } init { + registerBackHandler() getFolders() } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/MessagesComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/MessagesComponent.kt index 44619fa9..721272f9 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/MessagesComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/MessagesComponent.kt @@ -7,6 +7,7 @@ import com.arkivanov.decompose.router.stack.childStack import com.arkivanov.decompose.router.stack.push import com.arkivanov.decompose.value.MutableValue import com.arkivanov.decompose.value.Value +import com.arkivanov.essenty.backhandler.BackCallback import com.arkivanov.essenty.parcelable.Parcelable import com.arkivanov.essenty.parcelable.Parcelize import dev.tiebe.magisterapi.api.messages.MessageFlow @@ -33,6 +34,9 @@ interface MessagesComponent: MenuItemComponent { val refreshState: Value val scope: CoroutineScope + val onBack: MutableValue<() -> Unit> + + suspend fun getFoldersAsync() fun getFolders() @@ -70,6 +74,9 @@ interface MessagesComponent: MenuItemComponent { @Parcelize data class ReceiverInfo(val messageLink: String, val receiverType: ReceiverInfoComponent.ReceiverType) : Config() } + + fun registerBackHandler() + fun unregisterBackHandler() } class DefaultMessagesComponent( @@ -78,6 +85,9 @@ class DefaultMessagesComponent( override val refreshState: MutableValue = MutableValue(false) override val scope: CoroutineScope = componentCoroutineScope() + override val onBack: MutableValue<() -> Unit> = MutableValue { + + } override val folders: MutableValue> = MutableValue(listOf()) override val navigation = StackNavigation() @@ -86,7 +96,7 @@ class DefaultMessagesComponent( childStack( source = navigation, initialConfiguration = MessagesComponent.Config.Main, - handleBackButton = true, // Pop the back stack on back button press + handleBackButton = false, // Pop the back stack on back button press childFactory = ::createChild, ) @@ -143,7 +153,31 @@ class DefaultMessagesComponent( } } + private val registered = MutableValue(false) + private val backCallback = BackCallback { onBack.value() } + + override fun registerBackHandler() { + if (registered.value) return + backHandler.register(backCallback) + registered.value = true + } + + override fun unregisterBackHandler() { + if (!registered.value) return + backHandler.unregister(backCallback) + registered.value = false + } + + init { + childStack.subscribe { childStack -> + if (childStack.active.configuration is MessagesComponent.Config.Main) { + unregisterBackHandler() + } else { + registerBackHandler() + } + } + scope.launch { getFolders() } diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/MessagesScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/MessagesScreen.kt index a3a0a4a7..9939a0a2 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/MessagesScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/MessagesScreen.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material.DismissDirection +import androidx.compose.material.DismissValue import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.SwipeToDismiss import androidx.compose.material.icons.Icons @@ -23,6 +24,7 @@ import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.State +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextOverflow @@ -32,6 +34,7 @@ import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState import com.arkivanov.decompose.router.stack.ChildStack import com.arkivanov.decompose.router.stack.pop import dev.icerock.moko.resources.compose.stringResource +import kotlinx.coroutines.launch import nl.tiebe.otarium.MR import nl.tiebe.otarium.ui.home.messages.folder.FolderScreen import nl.tiebe.otarium.ui.home.messages.message.MessageScreen @@ -41,6 +44,7 @@ import nl.tiebe.otarium.ui.home.messages.message.receiver.ReceiverInfoScreen @Composable internal fun MessagesScreen(component: MessagesComponent) { val screen = component.childStack.subscribeAsState() + val scope = rememberCoroutineScope() Box(modifier = Modifier.padding(start = 5.dp, end = 5.dp)) { MessageScreenChild(component, screen, screen.value.items[0], false) @@ -48,6 +52,12 @@ internal fun MessagesScreen(component: MessagesComponent) { for (item in screen.value.items.subList(1, screen.value.items.size)) { val state = rememberDismissState() + component.onBack.value = { + scope.launch { + state.animateTo(DismissValue.DismissedToEnd) + } + } + //pop on finish if (state.isDismissed(DismissDirection.StartToEnd)) { component.navigation.pop() @@ -83,7 +93,7 @@ internal fun MessageScreenChild( title = { Text(name, overflow = TextOverflow.Ellipsis, maxLines = 1) }, navigationIcon = { if (poppable) { - IconButton(onClick = { component.navigation.pop() }) { + IconButton(onClick = { component.onBack.value() }) { Icon(Icons.Default.ArrowBack, contentDescription = "Back") } } From ca6b11b2e1bb208a3daf1481987e1031d0e5470b Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Wed, 24 May 2023 13:02:26 +0200 Subject: [PATCH 80/82] settings --- .../ui/home/messages/MessagesComponent.kt | 5 +- .../ui/home/settings/SettingsComponent.kt | 39 +++++++- .../ui/home/settings/SettingsScreen.kt | 95 ++++++++++++++----- 3 files changed, 111 insertions(+), 28 deletions(-) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/MessagesComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/MessagesComponent.kt index 721272f9..43fd836d 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/MessagesComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/MessagesComponent.kt @@ -85,9 +85,8 @@ class DefaultMessagesComponent( override val refreshState: MutableValue = MutableValue(false) override val scope: CoroutineScope = componentCoroutineScope() - override val onBack: MutableValue<() -> Unit> = MutableValue { + override val onBack: MutableValue<() -> Unit> = MutableValue {} - } override val folders: MutableValue> = MutableValue(listOf()) override val navigation = StackNavigation() @@ -171,7 +170,7 @@ class DefaultMessagesComponent( init { childStack.subscribe { childStack -> - if (childStack.active.configuration is MessagesComponent.Config.Main) { + if (childStack.backStack.isEmpty()) { unregisterBackHandler() } else { registerBackHandler() diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/SettingsComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/SettingsComponent.kt index 7cf9570e..3a175b08 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/SettingsComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/SettingsComponent.kt @@ -1,8 +1,14 @@ package nl.tiebe.otarium.ui.home.settings import com.arkivanov.decompose.ComponentContext -import com.arkivanov.decompose.router.stack.* +import com.arkivanov.decompose.router.stack.ChildStack +import com.arkivanov.decompose.router.stack.StackNavigation +import com.arkivanov.decompose.router.stack.childStack +import com.arkivanov.decompose.router.stack.pop +import com.arkivanov.decompose.router.stack.push +import com.arkivanov.decompose.value.MutableValue import com.arkivanov.decompose.value.Value +import com.arkivanov.essenty.backhandler.BackCallback import com.arkivanov.essenty.parcelable.Parcelable import com.arkivanov.essenty.parcelable.Parcelize import nl.tiebe.otarium.MR @@ -28,6 +34,8 @@ interface SettingsComponent: MenuItemComponent { val navigateRootComponent: (RootComponent.ChildScreen) -> Unit + val onBack: MutableValue<() -> Unit> + fun navigate(child: Config) { navigation.push(child) } @@ -65,6 +73,8 @@ interface SettingsComponent: MenuItemComponent { object Colors : Config(getLocalizedString(MR.strings.color_settings)) } + fun registerBackHandler() + fun unregisterBackHandler() } class DefaultSettingsComponent( @@ -127,4 +137,31 @@ class DefaultSettingsComponent( componentContext = componentContext, _navigate = ::navigate ) + + private val registered = MutableValue(false) + private val backCallback = BackCallback { onBack.value() } + override val onBack: MutableValue<() -> Unit> = MutableValue {} + + override fun registerBackHandler() { + if (registered.value) return + backHandler.register(backCallback) + registered.value = true + } + + override fun unregisterBackHandler() { + if (!registered.value) return + backHandler.unregister(backCallback) + registered.value = false + } + + + init { + childStack.subscribe { childStack -> + if (childStack.backStack.isEmpty()) { + unregisterBackHandler() + } else { + registerBackHandler() + } + } + } } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/SettingsScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/SettingsScreen.kt index 80360778..c2ed712f 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/SettingsScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/SettingsScreen.kt @@ -1,16 +1,28 @@ package nl.tiebe.otarium.ui.home.settings import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.padding +import androidx.compose.material.DismissDirection +import androidx.compose.material.DismissValue +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.SwipeToDismiss import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack -import androidx.compose.material3.* +import androidx.compose.material.rememberDismissState +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import com.arkivanov.decompose.Child import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState +import com.arkivanov.decompose.router.stack.pop +import kotlinx.coroutines.launch import nl.tiebe.otarium.ui.home.settings.items.ads.AdsChildScreen import nl.tiebe.otarium.ui.home.settings.items.bugs.BugsChildScreen import nl.tiebe.otarium.ui.home.settings.items.main.MainChildScreen @@ -18,33 +30,68 @@ import nl.tiebe.otarium.ui.home.settings.items.ui.UIChildScreen import nl.tiebe.otarium.ui.home.settings.items.ui.colors.ColorChildScreen import nl.tiebe.otarium.ui.home.settings.items.users.UserChildScreen -@OptIn(ExperimentalMaterial3Api::class) +@OptIn(ExperimentalMaterialApi::class) @Composable internal fun SettingsScreen(component: SettingsComponent) { - Column { - val screen = component.childStack.subscribeAsState() - - if (screen.value.active.instance !is SettingsComponent.Child.MainChild) { - TopAppBar( - title = { Text(screen.value.active.configuration.localizedString) }, - navigationIcon = { - IconButton(onClick = { component.back() }) { - Icon(Icons.Default.ArrowBack, contentDescription = "Back") - } + val screen = component.childStack.subscribeAsState() + val scope = rememberCoroutineScope() + + Box(modifier = Modifier.padding(start = 5.dp, end = 5.dp)) { + SettingsScreenChild(component, screen.value.items[0]) + + for (item in screen.value.items.subList(1, screen.value.items.size)) { + val state = rememberDismissState() + + component.onBack.value = { + scope.launch { + state.animateTo(DismissValue.DismissedToEnd) + } + } + + //pop on finish + if (state.isDismissed(DismissDirection.StartToEnd)) { + component.navigation.pop() + } + + SwipeToDismiss( + state = state, + background = { }, - windowInsets = WindowInsets(0.dp) - ) + directions = setOf(DismissDirection.StartToEnd) + ) { + SettingsScreenChild(component, screen.value.active) + } } + } +} + - Box(modifier = Modifier.padding(start = 5.dp, end = 5.dp)) { - when (val child = screen.value.active.instance) { - is SettingsComponent.Child.MainChild -> MainChildScreen(child.component) - is SettingsComponent.Child.AdsChild -> AdsChildScreen(child.component) - is SettingsComponent.Child.UsersChild -> UserChildScreen(child.component) - is SettingsComponent.Child.BugsChild -> BugsChildScreen() - is SettingsComponent.Child.UIChild -> UIChildScreen(child.component) - is SettingsComponent.Child.ColorChild -> ColorChildScreen(child.component) +@OptIn(ExperimentalMaterial3Api::class) +@Composable +internal fun SettingsScreenChild( + component: SettingsComponent, + screen: Child.Created +) { + TopAppBar( + title = { Text(screen.configuration.localizedString) }, + navigationIcon = { + if (component.childStack.value.backStack.isNotEmpty()) { + IconButton(onClick = { component.back() }) { + Icon(Icons.Default.ArrowBack, contentDescription = "Back") + } } + }, + windowInsets = WindowInsets(0.dp) + ) + + Box(modifier = Modifier.padding(start = 5.dp, end = 5.dp)) { + when (val child = screen.instance) { + is SettingsComponent.Child.MainChild -> MainChildScreen(child.component) + is SettingsComponent.Child.AdsChild -> AdsChildScreen(child.component) + is SettingsComponent.Child.UsersChild -> UserChildScreen(child.component) + is SettingsComponent.Child.BugsChild -> BugsChildScreen() + is SettingsComponent.Child.UIChild -> UIChildScreen(child.component) + is SettingsComponent.Child.ColorChild -> ColorChildScreen(child.component) } } -} +} \ No newline at end of file From 24a22cb351a58df4ffaa0d90cf213888e98dc3b6 Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Wed, 24 May 2023 13:02:26 +0200 Subject: [PATCH 81/82] settings --- .../settings/StoreSettingsComponent.kt | 10 +++ .../ui/home/messages/MessagesComponent.kt | 5 +- .../ui/home/settings/SettingsComponent.kt | 41 +++++++++- .../ui/home/settings/SettingsScreen.kt | 82 +++++++++++++++---- 4 files changed, 119 insertions(+), 19 deletions(-) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/store/component/home/children/settings/StoreSettingsComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/store/component/home/children/settings/StoreSettingsComponent.kt index 98dd6f7a..98c50a3e 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/store/component/home/children/settings/StoreSettingsComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/store/component/home/children/settings/StoreSettingsComponent.kt @@ -4,7 +4,9 @@ import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.router.stack.ChildStack import com.arkivanov.decompose.router.stack.StackNavigation import com.arkivanov.decompose.router.stack.childStack +import com.arkivanov.decompose.value.MutableValue import com.arkivanov.decompose.value.Value +import com.arkivanov.essenty.backhandler.BackCallback import nl.tiebe.otarium.store.component.home.children.settings.children.StoreUserChildComponent import nl.tiebe.otarium.ui.home.settings.SettingsComponent import nl.tiebe.otarium.ui.home.settings.items.ads.AdsChildComponent @@ -33,6 +35,14 @@ class StoreSettingsComponent( handleBackButton = true, // Pop the back stack on back button press childFactory = ::createChild, ) + override val onBack: MutableValue<() -> Unit> = MutableValue {} + + override fun registerBackHandler() { + backHandler.register(BackCallback { onBack.value() }) + } + + override fun unregisterBackHandler() { + } private fun createChild(config: SettingsComponent.Config, componentContext: ComponentContext): SettingsComponent.Child = when (config) { diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/MessagesComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/MessagesComponent.kt index 721272f9..43fd836d 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/MessagesComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/messages/MessagesComponent.kt @@ -85,9 +85,8 @@ class DefaultMessagesComponent( override val refreshState: MutableValue = MutableValue(false) override val scope: CoroutineScope = componentCoroutineScope() - override val onBack: MutableValue<() -> Unit> = MutableValue { + override val onBack: MutableValue<() -> Unit> = MutableValue {} - } override val folders: MutableValue> = MutableValue(listOf()) override val navigation = StackNavigation() @@ -171,7 +170,7 @@ class DefaultMessagesComponent( init { childStack.subscribe { childStack -> - if (childStack.active.configuration is MessagesComponent.Config.Main) { + if (childStack.backStack.isEmpty()) { unregisterBackHandler() } else { registerBackHandler() diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/SettingsComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/SettingsComponent.kt index 7cf9570e..6f40baef 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/SettingsComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/SettingsComponent.kt @@ -1,8 +1,14 @@ package nl.tiebe.otarium.ui.home.settings import com.arkivanov.decompose.ComponentContext -import com.arkivanov.decompose.router.stack.* +import com.arkivanov.decompose.router.stack.ChildStack +import com.arkivanov.decompose.router.stack.StackNavigation +import com.arkivanov.decompose.router.stack.childStack +import com.arkivanov.decompose.router.stack.pop +import com.arkivanov.decompose.router.stack.push +import com.arkivanov.decompose.value.MutableValue import com.arkivanov.decompose.value.Value +import com.arkivanov.essenty.backhandler.BackCallback import com.arkivanov.essenty.parcelable.Parcelable import com.arkivanov.essenty.parcelable.Parcelize import nl.tiebe.otarium.MR @@ -28,6 +34,8 @@ interface SettingsComponent: MenuItemComponent { val navigateRootComponent: (RootComponent.ChildScreen) -> Unit + val onBack: MutableValue<() -> Unit> + fun navigate(child: Config) { navigation.push(child) } @@ -47,7 +55,7 @@ interface SettingsComponent: MenuItemComponent { sealed class Config(val localizedString: String) : Parcelable { @Parcelize - object Main : Config("") + object Main : Config(getLocalizedString(MR.strings.settingsItem)) @Parcelize object Ads : Config(getLocalizedString(MR.strings.advertisements)) @@ -65,6 +73,8 @@ interface SettingsComponent: MenuItemComponent { object Colors : Config(getLocalizedString(MR.strings.color_settings)) } + fun registerBackHandler() + fun unregisterBackHandler() } class DefaultSettingsComponent( @@ -127,4 +137,31 @@ class DefaultSettingsComponent( componentContext = componentContext, _navigate = ::navigate ) + + private val registered = MutableValue(false) + private val backCallback = BackCallback { onBack.value() } + override val onBack: MutableValue<() -> Unit> = MutableValue {} + + override fun registerBackHandler() { + if (registered.value) return + backHandler.register(backCallback) + registered.value = true + } + + override fun unregisterBackHandler() { + if (!registered.value) return + backHandler.unregister(backCallback) + registered.value = false + } + + + init { + childStack.subscribe { childStack -> + if (childStack.backStack.isEmpty()) { + unregisterBackHandler() + } else { + registerBackHandler() + } + } + } } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/SettingsScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/SettingsScreen.kt index 80360778..e916c473 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/SettingsScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/SettingsScreen.kt @@ -3,14 +3,29 @@ package nl.tiebe.otarium.ui.home.settings import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding +import androidx.compose.material.DismissDirection +import androidx.compose.material.DismissValue +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material3.Surface +import androidx.compose.material.SwipeToDismiss import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack -import androidx.compose.material3.* +import androidx.compose.material.rememberDismissState +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import com.arkivanov.decompose.Child import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState +import com.arkivanov.decompose.router.stack.pop +import kotlinx.coroutines.launch import nl.tiebe.otarium.ui.home.settings.items.ads.AdsChildScreen import nl.tiebe.otarium.ui.home.settings.items.bugs.BugsChildScreen import nl.tiebe.otarium.ui.home.settings.items.main.MainChildScreen @@ -18,26 +33,65 @@ import nl.tiebe.otarium.ui.home.settings.items.ui.UIChildScreen import nl.tiebe.otarium.ui.home.settings.items.ui.colors.ColorChildScreen import nl.tiebe.otarium.ui.home.settings.items.users.UserChildScreen -@OptIn(ExperimentalMaterial3Api::class) +@OptIn(ExperimentalMaterialApi::class) @Composable internal fun SettingsScreen(component: SettingsComponent) { - Column { - val screen = component.childStack.subscribeAsState() + val screen = component.childStack.subscribeAsState() + val scope = rememberCoroutineScope() + + Box(modifier = Modifier.padding(start = 5.dp, end = 5.dp)) { + SettingsScreenChild(component, screen.value.items[0]) + + for (item in screen.value.items.subList(1, screen.value.items.size)) { + val state = rememberDismissState() + + component.onBack.value = { + scope.launch { + state.animateTo(DismissValue.DismissedToEnd) + } + } - if (screen.value.active.instance !is SettingsComponent.Child.MainChild) { - TopAppBar( - title = { Text(screen.value.active.configuration.localizedString) }, - navigationIcon = { + //pop on finish + if (state.isDismissed(DismissDirection.StartToEnd)) { + component.navigation.pop() + } + + SwipeToDismiss( + state = state, + background = { + }, + directions = setOf(DismissDirection.StartToEnd) + ) { + Surface(Modifier.fillMaxSize()) { + SettingsScreenChild(component, screen.value.active) + } + } + } + } +} + + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +internal fun SettingsScreenChild( + component: SettingsComponent, + screen: Child.Created +) { + Column { + TopAppBar( + title = { Text(screen.configuration.localizedString) }, + navigationIcon = { + if (screen.instance !is SettingsComponent.Child.MainChild) { IconButton(onClick = { component.back() }) { Icon(Icons.Default.ArrowBack, contentDescription = "Back") } - }, - windowInsets = WindowInsets(0.dp) - ) - } + } + }, + windowInsets = WindowInsets(0.dp) + ) Box(modifier = Modifier.padding(start = 5.dp, end = 5.dp)) { - when (val child = screen.value.active.instance) { + when (val child = screen.instance) { is SettingsComponent.Child.MainChild -> MainChildScreen(child.component) is SettingsComponent.Child.AdsChild -> AdsChildScreen(child.component) is SettingsComponent.Child.UsersChild -> UserChildScreen(child.component) @@ -47,4 +101,4 @@ internal fun SettingsScreen(component: SettingsComponent) { } } } -} +} \ No newline at end of file From 7c0b5128cd0aa1d3e268216379553ed059f610ea Mon Sep 17 00:00:00 2001 From: Tiebe Groosman Date: Wed, 24 May 2023 13:39:13 +0200 Subject: [PATCH 82/82] elo --- .../assignments/AssignmentsChildComponent.kt | 34 +++++++++++ .../assignments/AssignmentsChildScreen.kt | 56 +++++++++++++++++-- .../studyguides/StudyGuidesChildComponent.kt | 34 +++++++++++ .../studyguides/StudyGuidesChildScreen.kt | 44 +++++++++++++-- .../ui/home/settings/SettingsComponent.kt | 3 +- 5 files changed, 162 insertions(+), 9 deletions(-) diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/AssignmentsChildComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/AssignmentsChildComponent.kt index 5e8cdb52..4d52fa60 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/AssignmentsChildComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/AssignmentsChildComponent.kt @@ -7,6 +7,7 @@ import com.arkivanov.decompose.router.stack.childStack import com.arkivanov.decompose.router.stack.push import com.arkivanov.decompose.value.MutableValue import com.arkivanov.decompose.value.Value +import com.arkivanov.essenty.backhandler.BackCallback import com.arkivanov.essenty.parcelable.Parcelable import com.arkivanov.essenty.parcelable.Parcelize import kotlinx.coroutines.CoroutineScope @@ -40,6 +41,12 @@ interface AssignmentsChildComponent : ELOChildComponent { @Parcelize data class Assignment(val assignmentLink: String) : Config() } + + + val onBack: MutableValue<() -> Unit> + + fun registerBackHandler() + fun unregisterBackHandler() } class DefaultAssignmentsChildComponent(componentContext: ComponentContext) : AssignmentsChildComponent, ComponentContext by componentContext { @@ -76,6 +83,33 @@ class DefaultAssignmentsChildComponent(componentContext: ComponentContext) : Ass componentContext = componentContext, assignmentLink = assignmentLink, ) + + private val registered = MutableValue(false) + private val backCallback = BackCallback { onBack.value() } + override val onBack: MutableValue<() -> Unit> = MutableValue {} + + override fun registerBackHandler() { + if (registered.value) return + backHandler.register(backCallback) + registered.value = true + } + + override fun unregisterBackHandler() { + if (!registered.value) return + backHandler.unregister(backCallback) + registered.value = false + } + + + init { + childStack.subscribe { childStack -> + if (childStack.backStack.isEmpty()) { + unregisterBackHandler() + } else { + registerBackHandler() + } + } + } } interface AssignmentChildScreen \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/AssignmentsChildScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/AssignmentsChildScreen.kt index c01c33a3..9d199727 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/AssignmentsChildScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/assignments/AssignmentsChildScreen.kt @@ -1,23 +1,71 @@ package nl.tiebe.otarium.ui.home.elo.children.assignments import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding +import androidx.compose.material.DismissDirection +import androidx.compose.material.DismissValue +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.SwipeToDismiss +import androidx.compose.material.rememberDismissState +import androidx.compose.material3.Surface import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState +import com.arkivanov.decompose.router.stack.pop +import kotlinx.coroutines.launch import nl.tiebe.otarium.ui.home.elo.children.assignments.assignment.AssignmentScreen import nl.tiebe.otarium.ui.home.elo.children.assignments.listscreen.AssignmentListScreen +@OptIn(ExperimentalMaterialApi::class) @Composable internal fun AssignmentsChildScreen(component: AssignmentsChildComponent) { - val child = component.childStack.subscribeAsState().value + val screen = component.childStack.subscribeAsState() + val scope = rememberCoroutineScope() Box(modifier = Modifier.padding(start = 5.dp, end = 5.dp)) { - when (val screen = child.active.instance) { - is AssignmentsChildComponent.Child.Assignment -> AssignmentScreen(screen.component) - is AssignmentsChildComponent.Child.AssignmentList -> AssignmentListScreen(screen.component) + when (val child = screen.value.items[0].instance) { + is AssignmentsChildComponent.Child.Assignment -> AssignmentScreen(child.component) + is AssignmentsChildComponent.Child.AssignmentList -> AssignmentListScreen(child.component) } + + + for (item in screen.value.items.subList(1, screen.value.items.size)) { + val state = rememberDismissState() + + component.onBack.value = { + scope.launch { + state.animateTo(DismissValue.DismissedToEnd) + } + } + + //pop on finish + if (state.isDismissed(DismissDirection.StartToEnd)) { + component.navigation.pop() + } + + SwipeToDismiss( + state = state, + background = { + }, + directions = setOf(DismissDirection.StartToEnd) + ) { + Surface(Modifier.fillMaxSize()) { + when (val child = item.instance) { + is AssignmentsChildComponent.Child.Assignment -> AssignmentScreen(child.component) + is AssignmentsChildComponent.Child.AssignmentList -> AssignmentListScreen(child.component) + } + } + } + } + } + + + + Box(modifier = Modifier.padding(start = 5.dp, end = 5.dp)) { + } } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/StudyGuidesChildComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/StudyGuidesChildComponent.kt index 25f8b850..66ef6685 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/StudyGuidesChildComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/StudyGuidesChildComponent.kt @@ -7,6 +7,7 @@ import com.arkivanov.decompose.router.stack.childStack import com.arkivanov.decompose.router.stack.push import com.arkivanov.decompose.value.MutableValue import com.arkivanov.decompose.value.Value +import com.arkivanov.essenty.backhandler.BackCallback import com.arkivanov.essenty.parcelable.Parcelable import com.arkivanov.essenty.parcelable.Parcelize import dev.tiebe.magisterapi.response.studyguide.StudyGuide @@ -44,6 +45,12 @@ interface StudyGuidesChildComponent : ELOChildComponent { data class StudyGuide(val studyGuideLink: String) : Config() } + + val onBack: MutableValue<() -> Unit> + + fun registerBackHandler() + fun unregisterBackHandler() + } class DefaultStudyGuidesChildComponent(componentContext: ComponentContext) : StudyGuidesChildComponent, ComponentContext by componentContext { @@ -81,6 +88,33 @@ class DefaultStudyGuidesChildComponent(componentContext: ComponentContext) : Stu componentContext = componentContext, studyGuideLink = studyGuideLink, ) + + private val registered = MutableValue(false) + private val backCallback = BackCallback { onBack.value() } + override val onBack: MutableValue<() -> Unit> = MutableValue {} + + override fun registerBackHandler() { + if (registered.value) return + backHandler.register(backCallback) + registered.value = true + } + + override fun unregisterBackHandler() { + if (!registered.value) return + backHandler.unregister(backCallback) + registered.value = false + } + + + init { + childStack.subscribe { childStack -> + if (childStack.backStack.isEmpty()) { + unregisterBackHandler() + } else { + registerBackHandler() + } + } + } } interface StudyGuideChildScreen \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/StudyGuidesChildScreen.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/StudyGuidesChildScreen.kt index 1256b763..4345625e 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/StudyGuidesChildScreen.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/elo/children/studyguides/StudyGuidesChildScreen.kt @@ -2,22 +2,58 @@ package nl.tiebe.otarium.ui.home.elo.children.studyguides import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.padding +import androidx.compose.material.DismissDirection +import androidx.compose.material.DismissValue import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.SwipeToDismiss +import androidx.compose.material.rememberDismissState import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState +import com.arkivanov.decompose.router.stack.pop +import kotlinx.coroutines.launch import nl.tiebe.otarium.ui.home.elo.children.studyguides.folder.StudyGuideFolderScreen import nl.tiebe.otarium.ui.home.elo.children.studyguides.listscreen.StudyGuideListScreen +@OptIn(ExperimentalMaterialApi::class) @Composable internal fun StudyGuidesChildScreen(component: StudyGuidesChildComponent) { - val child = component.childStack.subscribeAsState().value + val screen = component.childStack.subscribeAsState() + val scope = rememberCoroutineScope() Box(modifier = Modifier.padding(start = 5.dp, end = 5.dp)) { - when (val screen = child.active.instance) { - is StudyGuidesChildComponent.Child.StudyGuideListChild -> StudyGuideListScreen(screen.component) - is StudyGuidesChildComponent.Child.FolderChild -> StudyGuideFolderScreen(screen.component) + when (val child = screen.value.items[0].instance) { + is StudyGuidesChildComponent.Child.StudyGuideListChild -> StudyGuideListScreen(child.component) + is StudyGuidesChildComponent.Child.FolderChild -> StudyGuideFolderScreen(child.component) + } + + for (item in screen.value.items.subList(1, screen.value.items.size)) { + val state = rememberDismissState() + + component.onBack.value = { + scope.launch { + state.animateTo(DismissValue.DismissedToEnd) + } + } + + //pop on finish + if (state.isDismissed(DismissDirection.StartToEnd)) { + component.navigation.pop() + } + + SwipeToDismiss( + state = state, + background = { + }, + directions = setOf(DismissDirection.StartToEnd) + ) { + when (val child = item.instance) { + is StudyGuidesChildComponent.Child.StudyGuideListChild -> StudyGuideListScreen(child.component) + is StudyGuidesChildComponent.Child.FolderChild -> StudyGuideFolderScreen(child.component) + } + } } } } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/SettingsComponent.kt b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/SettingsComponent.kt index 6f40baef..27c4cc90 100644 --- a/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/SettingsComponent.kt +++ b/shared/src/commonMain/kotlin/nl/tiebe/otarium/ui/home/settings/SettingsComponent.kt @@ -34,7 +34,6 @@ interface SettingsComponent: MenuItemComponent { val navigateRootComponent: (RootComponent.ChildScreen) -> Unit - val onBack: MutableValue<() -> Unit> fun navigate(child: Config) { navigation.push(child) @@ -73,6 +72,8 @@ interface SettingsComponent: MenuItemComponent { object Colors : Config(getLocalizedString(MR.strings.color_settings)) } + val onBack: MutableValue<() -> Unit> + fun registerBackHandler() fun unregisterBackHandler() }