From e956985de7fd658453a937b992a07d28e77b7511 Mon Sep 17 00:00:00 2001 From: Yuyu1115 <105599462+Yuyu-1115@users.noreply.github.com> Date: Thu, 13 Aug 2026 14:12:27 +0800 Subject: [PATCH 1/5] deps: add ktlint and ktlintPlugin as formatter --- .editorconfig | 23 +++++++++++++++++++++++ androidApp/build.gradle.kts | 10 ++++++++++ build.gradle.kts | 12 ++++++++++++ gradle/libs.versions.toml | 3 +++ shared/build.gradle.kts | 10 ++++++++++ 5 files changed, 58 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..56dc71f --- /dev/null +++ b/.editorconfig @@ -0,0 +1,23 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{kt,kts}] +indent_size = 4 +indent_style = space +ktlint_code_style = intellij_idea +ktlint_function_naming_ignore_when_annotated_with = Composable + +[shared/src/*Main/kotlin/org/mpc/core/*.kt] +# Factory files are organized by function and use KMP target suffixes, so their +# names intentionally do not mirror one declaration in PascalCase. +ktlint_standard_filename = disabled + +[shared/src/commonMain/kotlin/org/mpc/presentation/icon/*.kt] +# These are generated-style Material icon definitions whose private cache names +# intentionally follow the upstream icon source rather than property conventions. +ktlint_standard_backing-property-naming = disabled diff --git a/androidApp/build.gradle.kts b/androidApp/build.gradle.kts index 6764dfc..efe83f4 100644 --- a/androidApp/build.gradle.kts +++ b/androidApp/build.gradle.kts @@ -5,6 +5,7 @@ plugins { alias(libs.plugins.composeMultiplatform) alias(libs.plugins.composeCompiler) alias(libs.plugins.detekt) + alias(libs.plugins.ktlint) alias(libs.plugins.testBalloon) } @@ -72,3 +73,12 @@ detekt { config.setFrom(rootProject.file("config/detekt/detekt.yml")) buildUponDefaultConfig = true } + +ktlint { + version.set(libs.versions.ktlint) + filter { + exclude { + "/build/generated/" in it.file.invariantSeparatorsPath + } + } +} diff --git a/build.gradle.kts b/build.gradle.kts index 0e880d1..d5bb338 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -14,9 +14,20 @@ plugins { alias(libs.plugins.ksp) apply false alias(libs.plugins.androidx.room) apply false alias(libs.plugins.detekt) apply false + alias(libs.plugins.ktlint) apply false alias(libs.plugins.testBalloon) apply false } +val ktlintCheck by tasks.registering { + group = "verification" + description = "Runs ktlint checks for all Kotlin modules." + + dependsOn( + ":shared:ktlintCheck", + ":androidApp:ktlintCheck", + ) +} + val swiftLint by tasks.registering(Exec::class) { group = "verification" description = "Runs SwiftLint against the iOS application." @@ -55,6 +66,7 @@ tasks.register("check") { ":androidApp:check", ":androidApp:detektDebug", ":androidApp:lintDebug", + ktlintCheck, swiftLint, ) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e7ce371..9ea29e0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -26,6 +26,8 @@ sqlite = "2.7.0" ksp = "2.3.10" detekt = "2.0.0-alpha.5" +ktlint = "1.8.0" +ktlintPlugin = "14.2.0" testBalloon = "1.0.1-K2.4.0" @@ -86,3 +88,4 @@ testBalloon = { id = "de.infix.testBalloon", version.ref = "testBalloon" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } detekt = { id = "dev.detekt", version.ref = "detekt" } +ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlintPlugin" } diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts index 7ca48ae..0438bf3 100644 --- a/shared/build.gradle.kts +++ b/shared/build.gradle.kts @@ -10,6 +10,7 @@ plugins { alias(libs.plugins.ksp) alias(libs.plugins.androidx.room) alias(libs.plugins.detekt) + alias(libs.plugins.ktlint) alias(libs.plugins.testBalloon) } @@ -106,3 +107,12 @@ detekt { config.setFrom(rootProject.file("config/detekt/detekt.yml")) buildUponDefaultConfig = true } + +ktlint { + version.set(libs.versions.ktlint) + filter { + exclude { + "/build/generated/" in it.file.invariantSeparatorsPath + } + } +} From 7b092c5331409cb715e6df799f0ac5baa5de424e Mon Sep 17 00:00:00 2001 From: Yuyu1115 <105599462+Yuyu-1115@users.noreply.github.com> Date: Thu, 13 Aug 2026 14:13:26 +0800 Subject: [PATCH 2/5] ci: add action check for detekt, ktlint, and swiftlint --- .github/workflows/lint.yml | 64 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 .github/workflows/lint.yml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..c858a65 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,64 @@ +name: Lint + +on: + pull_request: + push: + branches: + - dev + - main + +permissions: + contents: read + +concurrency: + group: lint-${{ github.ref }} + cancel-in-progress: true + +jobs: + kotlin: + name: Detekt and ktlint + runs-on: ubuntu-latest + timeout-minutes: 30 + + steps: + - name: Check out repository + uses: actions/checkout@v7 + + # required for gradle 9.5.0 and android gradle plugin 9.3.0 + - name: Set up Java + uses: actions/setup-java@v5 + with: + distribution: temurin + java-version: "17" + + - name: Set up Gradle + uses: gradle/actions/setup-gradle@v6 + with: + cache-provider: basic + + - name: Install Android SDK 37 + run: sdkmanager "platforms;android-37" + + - name: Run Detekt and ktlint + run: >- + ./gradlew + ktlintCheck + :shared:detektMainAndroid + :shared:detektHostTestAndroid + :androidApp:detektDebug + --no-daemon + + swift: + name: SwiftLint + runs-on: macos-latest + timeout-minutes: 10 + + steps: + - name: Check out repository + uses: actions/checkout@v7 + + - name: Install SwiftLint + run: brew install swiftlint + + - name: Run SwiftLint + run: swiftlint lint --config .swiftlint.yml --no-cache iosApp/iosApp From a15baa739d36e8df7e23514a5f8ae94bc0423eba Mon Sep 17 00:00:00 2001 From: Yuyu1115 <105599462+Yuyu-1115@users.noreply.github.com> Date: Thu, 13 Aug 2026 14:17:16 +0800 Subject: [PATCH 3/5] style: apply ktlint formatting This commit is generated via ./gradlew ktlintFormat, containing no changes to the actual behaviours --- .../src/main/kotlin/org/mpc/AndroidShell.kt | 6 +- .../CourseSelectionSearchScreen.kt | 24 ++-- .../org/mpc/core/dataStoreFactory.android.kt | 19 ++- .../kotlin/org/mpc/core/dataBaseBuilder.kt | 9 +- .../kotlin/org/mpc/core/httpClientFactory.kt | 3 +- .../mpc/data/local/dao/CourseCatalogDao.kt | 8 +- .../local/entities/LocalCatalogEntities.kt | 48 ++++---- .../org/mpc/data/mapper/dto/CourseMapper.kt | 108 +++++++++--------- .../mpc/data/mapper/entities/CourseMapper.kt | 103 ++++++++--------- .../data/mapper/entities/CoursePlanMapper.kt | 20 ++-- .../repository/DefaultCourseRepository.kt | 14 +-- .../kotlin/org/mpc/domain/model/CoursePlan.kt | 14 +-- .../mpc/domain/repository/CourseRepository.kt | 2 - .../model/CourseTimetableBlock.kt | 86 +++++++------- .../CourseSelectionTimetableSuccessView.kt | 99 ++++++++-------- .../courseSelection/components/CourseCard.kt | 65 +++++------ .../courseSelection/CourseDraftModelTest.kt | 101 ++++++++-------- .../iosMain/kotlin/org/mpc/IosSharedHost.kt | 18 ++- ...eBuidler.ios.kt => DatabaseBuilder.ios.kt} | 0 .../org/mpc/core/dataStoreFactory.ios.kt | 43 ++++--- 20 files changed, 380 insertions(+), 410 deletions(-) rename shared/src/iosMain/kotlin/org/mpc/core/{databaseBuidler.ios.kt => DatabaseBuilder.ios.kt} (100%) diff --git a/androidApp/src/main/kotlin/org/mpc/AndroidShell.kt b/androidApp/src/main/kotlin/org/mpc/AndroidShell.kt index 45f563e..666f116 100644 --- a/androidApp/src/main/kotlin/org/mpc/AndroidShell.kt +++ b/androidApp/src/main/kotlin/org/mpc/AndroidShell.kt @@ -27,9 +27,9 @@ fun AndroidAppShell(appGraph: AppGraph) { ) { paddingValues -> Box( modifier = - Modifier - .fillMaxSize() - .padding(paddingValues), + Modifier + .fillMaxSize() + .padding(paddingValues), ) { CourseSelectionSearchScreen(modifier = Modifier.fillMaxSize()) } diff --git a/androidApp/src/main/kotlin/org/mpc/presentation/CourseSelectionSearchScreen.kt b/androidApp/src/main/kotlin/org/mpc/presentation/CourseSelectionSearchScreen.kt index 8fd205d..218f0c9 100644 --- a/androidApp/src/main/kotlin/org/mpc/presentation/CourseSelectionSearchScreen.kt +++ b/androidApp/src/main/kotlin/org/mpc/presentation/CourseSelectionSearchScreen.kt @@ -64,21 +64,21 @@ fun CourseSelectionSearchScreen( ) { SearchBar( modifier = - Modifier - .fillMaxWidth() - .padding( - horizontal = 16.dp, - vertical = 8.dp, - ), + Modifier + .fillMaxWidth() + .padding( + horizontal = 16.dp, + vertical = 8.dp, + ), state = searchBarState, inputField = inputField, ) CourseSearchResultView( modifier = - Modifier - .fillMaxWidth() - .weight(1f), + Modifier + .fillMaxWidth() + .weight(1f), uiState = searchUiState.result, selectedCourseSerialNumbers = selectedCourseSerialNumbers, onToggleCourse = planViewModel::toggleCourse, @@ -91,9 +91,9 @@ fun CourseSelectionSearchScreen( ) { CourseSearchResultView( modifier = - Modifier - .fillMaxWidth() - .weight(1f), + Modifier + .fillMaxWidth() + .weight(1f), uiState = searchUiState.result, selectedCourseSerialNumbers = selectedCourseSerialNumbers, onToggleCourse = planViewModel::toggleCourse, diff --git a/shared/src/androidMain/kotlin/org/mpc/core/dataStoreFactory.android.kt b/shared/src/androidMain/kotlin/org/mpc/core/dataStoreFactory.android.kt index 4c4b79d..7687a47 100644 --- a/shared/src/androidMain/kotlin/org/mpc/core/dataStoreFactory.android.kt +++ b/shared/src/androidMain/kotlin/org/mpc/core/dataStoreFactory.android.kt @@ -8,13 +8,12 @@ import androidx.datastore.preferences.core.PreferencesSerializer import okio.FileSystem import okio.Path.Companion.toOkioPath -fun createDataStore(context: Context): Storage = - OkioStorage( - fileSystem = FileSystem.SYSTEM, - serializer = PreferencesSerializer, - producePath = { - context.filesDir - .resolve(DATA_STORE_FILE_NAME) - .toOkioPath() - }, - ) +fun createDataStore(context: Context): Storage = OkioStorage( + fileSystem = FileSystem.SYSTEM, + serializer = PreferencesSerializer, + producePath = { + context.filesDir + .resolve(DATA_STORE_FILE_NAME) + .toOkioPath() + }, +) diff --git a/shared/src/commonMain/kotlin/org/mpc/core/dataBaseBuilder.kt b/shared/src/commonMain/kotlin/org/mpc/core/dataBaseBuilder.kt index 2fafcf8..af172c8 100644 --- a/shared/src/commonMain/kotlin/org/mpc/core/dataBaseBuilder.kt +++ b/shared/src/commonMain/kotlin/org/mpc/core/dataBaseBuilder.kt @@ -8,10 +8,9 @@ import org.mpc.data.local.database.AppDatabase // This is the database composition root, before the Metro graph exists. @Suppress("InjectDispatcher") -fun createDatabase(builder: RoomDatabase.Builder): AppDatabase = - builder - .setDriver(BundledSQLiteDriver()) - .setQueryCoroutineContext(Dispatchers.IO) - .build() +fun createDatabase(builder: RoomDatabase.Builder): AppDatabase = builder + .setDriver(BundledSQLiteDriver()) + .setQueryCoroutineContext(Dispatchers.IO) + .build() internal const val DATABASE_NAME = "mpc.db" diff --git a/shared/src/commonMain/kotlin/org/mpc/core/httpClientFactory.kt b/shared/src/commonMain/kotlin/org/mpc/core/httpClientFactory.kt index 6ef0acd..513a204 100644 --- a/shared/src/commonMain/kotlin/org/mpc/core/httpClientFactory.kt +++ b/shared/src/commonMain/kotlin/org/mpc/core/httpClientFactory.kt @@ -13,8 +13,7 @@ import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonNamingStrategy @OptIn(ExperimentalSerializationApi::class) -fun createHttpClient(): HttpClient = - // TODO: maybe assign platform specific engine via expect/actual could be an option +fun createHttpClient(): HttpClient = // TODO: maybe assign platform specific engine via expect/actual could be an option HttpClient { install(Logging) { logger = diff --git a/shared/src/commonMain/kotlin/org/mpc/data/local/dao/CourseCatalogDao.kt b/shared/src/commonMain/kotlin/org/mpc/data/local/dao/CourseCatalogDao.kt index 5ef36d6..5d5ec74 100644 --- a/shared/src/commonMain/kotlin/org/mpc/data/local/dao/CourseCatalogDao.kt +++ b/shared/src/commonMain/kotlin/org/mpc/data/local/dao/CourseCatalogDao.kt @@ -34,11 +34,13 @@ interface CourseCatalogDao { @Query("DELETE FROM course_catalog WHERE semester = :semester") suspend fun deleteCatalog(semester: String) - @Query(""" + @Query( + """ DELETE FROM course WHERE semester = :semester AND serialNo IN (:serialNos) - """) + """, + ) suspend fun deleteCourses(semester: String, serialNos: List) @Query("SELECT * FROM course WHERE semester = :semester ORDER BY serialNo") @@ -67,7 +69,7 @@ interface CourseCatalogDao { if (removedCourseSerialNos.isNotEmpty()) { deleteCourses( semester = entities.catalog.semester, - serialNos = removedCourseSerialNos + serialNos = removedCourseSerialNos, ) } diff --git a/shared/src/commonMain/kotlin/org/mpc/data/local/entities/LocalCatalogEntities.kt b/shared/src/commonMain/kotlin/org/mpc/data/local/entities/LocalCatalogEntities.kt index b66410f..b230e21 100644 --- a/shared/src/commonMain/kotlin/org/mpc/data/local/entities/LocalCatalogEntities.kt +++ b/shared/src/commonMain/kotlin/org/mpc/data/local/entities/LocalCatalogEntities.kt @@ -44,10 +44,10 @@ internal fun CourseResult.toLocalCatalogEntities(): LocalCatalogEntities { return LocalCatalogEntities( catalog = - CourseCatalogEntity( - semester = semester, - lastUpdatedEpochMillis = lastUpdated.toEpochMilliseconds(), - ), + CourseCatalogEntity( + semester = semester, + lastUpdatedEpochMillis = lastUpdated.toEpochMilliseconds(), + ), courses = courseEntities, teachers = teacherEntities, courseTimes = timeEntities, @@ -82,26 +82,26 @@ internal fun LocalCatalogEntities.toDomain(): CourseResult { lastUpdated = Instant.fromEpochMilliseconds(catalog.lastUpdatedEpochMillis), semester = catalog.semester, courses = - courses.map { course -> - val key = - CourseKey( - semester = course.semester, - serialNo = course.serialNo, - ) - - course.toDomain( - teachers = teachersByCourse[key].orEmpty().map { it.teacherName }, - classTimes = - timesByCourse[key] - .orEmpty() - .mapNotNull { it.toDomainOrNull() } - .sortedWith( - compareBy( - { it.day.order }, - { it.period.order }, - ), - ), + courses.map { course -> + val key = + CourseKey( + semester = course.semester, + serialNo = course.serialNo, ) - }, + + course.toDomain( + teachers = teachersByCourse[key].orEmpty().map { it.teacherName }, + classTimes = + timesByCourse[key] + .orEmpty() + .mapNotNull { it.toDomainOrNull() } + .sortedWith( + compareBy( + { it.day.order }, + { it.period.order }, + ), + ), + ) + }, ) } diff --git a/shared/src/commonMain/kotlin/org/mpc/data/mapper/dto/CourseMapper.kt b/shared/src/commonMain/kotlin/org/mpc/data/mapper/dto/CourseMapper.kt index ac300e9..ae7f1df 100644 --- a/shared/src/commonMain/kotlin/org/mpc/data/mapper/dto/CourseMapper.kt +++ b/shared/src/commonMain/kotlin/org/mpc/data/mapper/dto/CourseMapper.kt @@ -18,37 +18,35 @@ import org.mpc.domain.model.CourseType import org.mpc.domain.model.DistributionCondition import org.mpc.domain.model.PasswordCardType -internal fun CourseResultDto.toDomain(): CourseResult = - CourseResult( - lastUpdated = lastUpdated, - semester = "$academicYear-$semester", - courses = courses.map { it.toDomain() }, - ) +internal fun CourseResultDto.toDomain(): CourseResult = CourseResult( + lastUpdated = lastUpdated, + semester = "$academicYear-$semester", + courses = courses.map { it.toDomain() }, +) -internal fun CourseDto.toDomain(): CourseSummary = - CourseSummary( - serialNo = CourseSerialNo(serialNo), - classNo = classNo, - title = title, - credit = credit, - passwordCard = passwordCard.toDomain(), - teachers = teachers, - classTimes = - classTimes.mapNotNull { rawTime -> - rawTime.toCourseTimeOrNull().also { parsed -> - if (parsed == null) { - Logger.w { "Ignoring invalid course time $parsed" } - } - } - }, - limitCnt = limitCnt, - admitCnt = admitCnt, - waitCnt = waitCnt, - collegeName = collegeName, - departmentName = departmentName, - courseType = courseType.toDomain(), - detailUrl = detailUrl, - ) +internal fun CourseDto.toDomain(): CourseSummary = CourseSummary( + serialNo = CourseSerialNo(serialNo), + classNo = classNo, + title = title, + credit = credit, + passwordCard = passwordCard.toDomain(), + teachers = teachers, + classTimes = + classTimes.mapNotNull { rawTime -> + rawTime.toCourseTimeOrNull().also { parsed -> + if (parsed == null) { + Logger.w { "Ignoring invalid course time $parsed" } + } + } + }, + limitCnt = limitCnt, + admitCnt = admitCnt, + waitCnt = waitCnt, + collegeName = collegeName, + departmentName = departmentName, + courseType = courseType.toDomain(), + detailUrl = detailUrl, +) private fun String.toCourseTimeOrNull(): CourseTime? { val parts = split("-", limit = 2) @@ -66,33 +64,29 @@ private fun String.toCourseTimeOrNull(): CourseTime? { } } -internal fun PasswordCardTypeDto.toDomain(): PasswordCardType = - when (this) { - PasswordCardTypeDto.ALL -> PasswordCardType.ALL - PasswordCardTypeDto.OPTIONAL -> PasswordCardType.OPTIONAL - PasswordCardTypeDto.NONE -> PasswordCardType.NONE - } +internal fun PasswordCardTypeDto.toDomain(): PasswordCardType = when (this) { + PasswordCardTypeDto.ALL -> PasswordCardType.ALL + PasswordCardTypeDto.OPTIONAL -> PasswordCardType.OPTIONAL + PasswordCardTypeDto.NONE -> PasswordCardType.NONE +} -internal fun CourseTypeDto.toDomain(): CourseType = - when (this) { - CourseTypeDto.REQUIRED -> CourseType.REQUIRED - CourseTypeDto.ELECTIVE -> CourseType.ELECTIVE - CourseTypeDto.UNKNOWN -> CourseType.UNKNOWN - } +internal fun CourseTypeDto.toDomain(): CourseType = when (this) { + CourseTypeDto.REQUIRED -> CourseType.REQUIRED + CourseTypeDto.ELECTIVE -> CourseType.ELECTIVE + CourseTypeDto.UNKNOWN -> CourseType.UNKNOWN +} -internal fun CourseDetailDto.toDomain(): CourseDetail = - CourseDetail( - serialNo = serialNo, - objectives = objectives, - content = content, - books = books, - teachingMethod = teachingMethod, - gradingPolicy = gradingPolicy, - distributionConditions = distributionConditions.map { it.toDomain() }, - ) +internal fun CourseDetailDto.toDomain(): CourseDetail = CourseDetail( + serialNo = serialNo, + objectives = objectives, + content = content, + books = books, + teachingMethod = teachingMethod, + gradingPolicy = gradingPolicy, + distributionConditions = distributionConditions.map { it.toDomain() }, +) -internal fun DistributionConditionDto.toDomain(): DistributionCondition = - DistributionCondition( - priority = priority, - rule = rule, - ) +internal fun DistributionConditionDto.toDomain(): DistributionCondition = DistributionCondition( + priority = priority, + rule = rule, +) diff --git a/shared/src/commonMain/kotlin/org/mpc/data/mapper/entities/CourseMapper.kt b/shared/src/commonMain/kotlin/org/mpc/data/mapper/entities/CourseMapper.kt index 6972ae7..b7fa33c 100644 --- a/shared/src/commonMain/kotlin/org/mpc/data/mapper/entities/CourseMapper.kt +++ b/shared/src/commonMain/kotlin/org/mpc/data/mapper/entities/CourseMapper.kt @@ -13,40 +13,38 @@ import org.mpc.domain.model.PasswordCardType internal fun CourseEntity.toDomain( teachers: List, classTimes: List, -): CourseSummary = - CourseSummary( - serialNo = CourseSerialNo(serialNo), - classNo = classNo, - title = title, - credit = credit, - passwordCard = passwordCard.toPasswordCardType(), - teachers = teachers, - classTimes = classTimes, - limitCnt = limitCnt, - admitCnt = admitCnt, - waitCnt = waitCnt, - collegeName = collegeName, - departmentName = departmentName, - courseType = courseType.toCourseType(), - detailUrl = detailUrl, - ) +): CourseSummary = CourseSummary( + serialNo = CourseSerialNo(serialNo), + classNo = classNo, + title = title, + credit = credit, + passwordCard = passwordCard.toPasswordCardType(), + teachers = teachers, + classTimes = classTimes, + limitCnt = limitCnt, + admitCnt = admitCnt, + waitCnt = waitCnt, + collegeName = collegeName, + departmentName = departmentName, + courseType = courseType.toCourseType(), + detailUrl = detailUrl, +) -internal fun CourseSummary.toEntity(semester: String): CourseEntity = - CourseEntity( - semester = semester, - serialNo = serialNo.value, - classNo = classNo, - title = title, - credit = credit, - passwordCard = passwordCard.name, - limitCnt = limitCnt, - admitCnt = admitCnt, - waitCnt = waitCnt, - collegeName = collegeName, - departmentName = departmentName, - courseType = courseType.name, - detailUrl = detailUrl, - ) +internal fun CourseSummary.toEntity(semester: String): CourseEntity = CourseEntity( + semester = semester, + serialNo = serialNo.value, + classNo = classNo, + title = title, + credit = credit, + passwordCard = passwordCard.name, + limitCnt = limitCnt, + admitCnt = admitCnt, + waitCnt = waitCnt, + collegeName = collegeName, + departmentName = departmentName, + courseType = courseType.name, + detailUrl = detailUrl, +) internal fun CourseTimeEntity.toDomainOrNull(): CourseTime? { val day = CourseDay.entries.firstOrNull { it.code == dayCode.toString() } @@ -62,26 +60,23 @@ internal fun CourseTimeEntity.toDomainOrNull(): CourseTime? { internal fun CourseTime.toEntity( semester: String, serialNo: CourseSerialNo, -): CourseTimeEntity = - CourseTimeEntity( - semester = semester, - serialNo = serialNo.value, - dayCode = day.code.toInt(), - periodCode = period.description, - ) +): CourseTimeEntity = CourseTimeEntity( + semester = semester, + serialNo = serialNo.value, + dayCode = day.code.toInt(), + periodCode = period.description, +) -internal fun String.toPasswordCardType(): PasswordCardType = - when (this) { - "ALL" -> PasswordCardType.ALL - "OPTIONAL" -> PasswordCardType.OPTIONAL - "NONE" -> PasswordCardType.NONE - else -> error("Unknown password card type in local storage: $this") - } +internal fun String.toPasswordCardType(): PasswordCardType = when (this) { + "ALL" -> PasswordCardType.ALL + "OPTIONAL" -> PasswordCardType.OPTIONAL + "NONE" -> PasswordCardType.NONE + else -> error("Unknown password card type in local storage: $this") +} -internal fun String.toCourseType(): CourseType = - when (this) { - "REQUIRED" -> CourseType.REQUIRED - "ELECTIVE" -> CourseType.ELECTIVE - "UNKNOWN" -> CourseType.UNKNOWN - else -> CourseType.UNKNOWN - } +internal fun String.toCourseType(): CourseType = when (this) { + "REQUIRED" -> CourseType.REQUIRED + "ELECTIVE" -> CourseType.ELECTIVE + "UNKNOWN" -> CourseType.UNKNOWN + else -> CourseType.UNKNOWN +} diff --git a/shared/src/commonMain/kotlin/org/mpc/data/mapper/entities/CoursePlanMapper.kt b/shared/src/commonMain/kotlin/org/mpc/data/mapper/entities/CoursePlanMapper.kt index 1c68d7f..4d7de13 100644 --- a/shared/src/commonMain/kotlin/org/mpc/data/mapper/entities/CoursePlanMapper.kt +++ b/shared/src/commonMain/kotlin/org/mpc/data/mapper/entities/CoursePlanMapper.kt @@ -5,16 +5,14 @@ import org.mpc.data.local.database.CoursePlanItemEntity import org.mpc.domain.model.CoursePlan import kotlin.time.Clock -internal fun CoursePlan.toCoursePlanEntity(): CoursePlanEntity = - CoursePlanEntity( +internal fun CoursePlan.toCoursePlanEntity(): CoursePlanEntity = CoursePlanEntity( + semester = semester, + updatedAtEpochMillis = Clock.System.now().toEpochMilliseconds(), +) + +internal fun CoursePlan.toCoursePlanItemsEntity(): List = selectedCourses.values.map { + CoursePlanItemEntity( semester = semester, - updatedAtEpochMillis = Clock.System.now().toEpochMilliseconds(), + serialNo = it.serialNo.value, ) - -internal fun CoursePlan.toCoursePlanItemsEntity(): List = - selectedCourses.values.map { - CoursePlanItemEntity( - semester = semester, - serialNo = it.serialNo.value, - ) - } \ No newline at end of file +} diff --git a/shared/src/commonMain/kotlin/org/mpc/data/repository/DefaultCourseRepository.kt b/shared/src/commonMain/kotlin/org/mpc/data/repository/DefaultCourseRepository.kt index ce83a2e..2abcfa6 100644 --- a/shared/src/commonMain/kotlin/org/mpc/data/repository/DefaultCourseRepository.kt +++ b/shared/src/commonMain/kotlin/org/mpc/data/repository/DefaultCourseRepository.kt @@ -20,11 +20,10 @@ import org.mpc.domain.repository.CourseRepository @Inject internal class DefaultCourseRepository( private val courseDataSource: CourseDataSource, - private val courseCatalogDao: CourseCatalogDao + private val courseCatalogDao: CourseCatalogDao, ) : CourseRepository { - override suspend fun fetchAllCourses(semester: String): CourseResult = - courseCatalogDao.findCatalogEntities(semester)?.toDomain() - ?: refreshCatalog(semester) + override suspend fun fetchAllCourses(semester: String): CourseResult = courseCatalogDao.findCatalogEntities(semester)?.toDomain() + ?: refreshCatalog(semester) override suspend fun fetchCourses( semester: String, @@ -67,10 +66,9 @@ internal class DefaultCourseRepository( serialNo: String, ): CourseDetail = courseDataSource.getCourseDetails(semester, serialNo).toDomain() - override fun observeCatalog(semester: String): Flow = - courseCatalogDao.observeCatalogMetadata(semester).map { - courseCatalogDao.findCatalogEntities(semester)?.toDomain() - } + override fun observeCatalog(semester: String): Flow = courseCatalogDao.observeCatalogMetadata(semester).map { + courseCatalogDao.findCatalogEntities(semester)?.toDomain() + } override suspend fun refreshCatalog(semester: String): CourseResult { val remoteResult = courseDataSource.getAllCourses(semester).toDomain() diff --git a/shared/src/commonMain/kotlin/org/mpc/domain/model/CoursePlan.kt b/shared/src/commonMain/kotlin/org/mpc/domain/model/CoursePlan.kt index 80b80d9..b4b6653 100644 --- a/shared/src/commonMain/kotlin/org/mpc/domain/model/CoursePlan.kt +++ b/shared/src/commonMain/kotlin/org/mpc/domain/model/CoursePlan.kt @@ -5,12 +5,10 @@ data class CoursePlan( val selectedCourses: Map, ) -fun CoursePlan.addCourse(course: CourseSummary): CoursePlan = - copy( - selectedCourses = selectedCourses + (course.serialNo to course), - ) +fun CoursePlan.addCourse(course: CourseSummary): CoursePlan = copy( + selectedCourses = selectedCourses + (course.serialNo to course), +) -fun CoursePlan.removeCourse(course: CourseSummary): CoursePlan = - copy( - selectedCourses = selectedCourses - course.serialNo, - ) +fun CoursePlan.removeCourse(course: CourseSummary): CoursePlan = copy( + selectedCourses = selectedCourses - course.serialNo, +) diff --git a/shared/src/commonMain/kotlin/org/mpc/domain/repository/CourseRepository.kt b/shared/src/commonMain/kotlin/org/mpc/domain/repository/CourseRepository.kt index 04ebf77..edc3d77 100644 --- a/shared/src/commonMain/kotlin/org/mpc/domain/repository/CourseRepository.kt +++ b/shared/src/commonMain/kotlin/org/mpc/domain/repository/CourseRepository.kt @@ -25,9 +25,7 @@ interface CourseRepository { fun observeCatalog(semester: String): Flow - // the only function that should interact with the data source // everything else should interact with room suspend fun refreshCatalog(semester: String): CourseResult - } diff --git a/shared/src/commonMain/kotlin/org/mpc/presentation/model/CourseTimetableBlock.kt b/shared/src/commonMain/kotlin/org/mpc/presentation/model/CourseTimetableBlock.kt index 0afb254..cafaed9 100644 --- a/shared/src/commonMain/kotlin/org/mpc/presentation/model/CourseTimetableBlock.kt +++ b/shared/src/commonMain/kotlin/org/mpc/presentation/model/CourseTimetableBlock.kt @@ -15,50 +15,48 @@ data class CourseTimetableBlock( val type: CourseType, ) -internal fun CoursePlan.toTimetableBlocks(): List = - selectedCourses.values - .flatMap(CourseSummary::toTimetableBlocks) - .sortedWith( - compareBy( - { it.time.day.order }, - { it.time.period.order }, - { it.serialNo.value }, - ), - ) +internal fun CoursePlan.toTimetableBlocks(): List = selectedCourses.values + .flatMap(CourseSummary::toTimetableBlocks) + .sortedWith( + compareBy( + { it.time.day.order }, + { it.time.period.order }, + { it.serialNo.value }, + ), + ) -internal fun CourseSummary.toTimetableBlocks(): List = - classTimes - .asSequence() - .filter { it.day != CourseDay.UNKNOWN } - .sortedWith( - compareBy( - { it.day.order }, - { it.period.order }, - ), - ).distinct() - .fold(mutableListOf()) { blocks, current -> - val previous = blocks.lastOrNull() +internal fun CourseSummary.toTimetableBlocks(): List = classTimes + .asSequence() + .filter { it.day != CourseDay.UNKNOWN } + .sortedWith( + compareBy( + { it.day.order }, + { it.period.order }, + ), + ).distinct() + .fold(mutableListOf()) { blocks, current -> + val previous = blocks.lastOrNull() - if ( - previous == null || - current.day != previous.time.day || - // every entry is within the same course, so we don't need to worry if it extends on other courses - current.period.order != previous.time.period.order + previous.span - ) { - blocks += - CourseTimetableBlock( - title = title, - serialNo = serialNo, - time = current, - span = 1, - type = courseType, - ) - } else { - blocks[blocks.lastIndex] = - previous.copy( - span = previous.span + 1, - ) - } - - blocks + if ( + previous == null || + current.day != previous.time.day || + // every entry is within the same course, so we don't need to worry if it extends on other courses + current.period.order != previous.time.period.order + previous.span + ) { + blocks += + CourseTimetableBlock( + title = title, + serialNo = serialNo, + time = current, + span = 1, + type = courseType, + ) + } else { + blocks[blocks.lastIndex] = + previous.copy( + span = previous.span + 1, + ) } + + blocks + } diff --git a/shared/src/commonMain/kotlin/org/mpc/presentation/views/courseSelection/CourseSelectionTimetableSuccessView.kt b/shared/src/commonMain/kotlin/org/mpc/presentation/views/courseSelection/CourseSelectionTimetableSuccessView.kt index ea867d5..b47d4b9 100644 --- a/shared/src/commonMain/kotlin/org/mpc/presentation/views/courseSelection/CourseSelectionTimetableSuccessView.kt +++ b/shared/src/commonMain/kotlin/org/mpc/presentation/views/courseSelection/CourseSelectionTimetableSuccessView.kt @@ -54,8 +54,8 @@ fun CourseSelectionTimetableSuccessView( Box( modifier = - modifier - .padding(horizontal = 16.dp, vertical = 8.dp), + modifier + .padding(horizontal = 16.dp, vertical = 8.dp), ) { Column( modifier = Modifier.fillMaxSize(), @@ -66,7 +66,7 @@ fun CourseSelectionTimetableSuccessView( horizontalArrangement = Arrangement.SpaceAround, modifier = Modifier .fillMaxWidth() - .height(20.dp) + .height(20.dp), ) { CourseDay.entries .take(columns) @@ -75,7 +75,7 @@ fun CourseSelectionTimetableSuccessView( contentAlignment = Alignment.Center, modifier = Modifier .fillMaxHeight() - .weight(1f) + .weight(1f), ) { Text( text = it.description, @@ -90,16 +90,16 @@ fun CourseSelectionTimetableSuccessView( Row( modifier = - Modifier - .fillMaxWidth() - .weight(1f), + Modifier + .fillMaxWidth() + .weight(1f), ) { Column( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.SpaceAround, modifier = Modifier .width(16.dp) - .fillMaxHeight() + .fillMaxHeight(), ) { CoursePeriod.entries .forEach { @@ -107,7 +107,7 @@ fun CourseSelectionTimetableSuccessView( contentAlignment = Alignment.Center, modifier = Modifier .fillMaxWidth() - .weight(1f) + .weight(1f), ) { Text( text = it.description, @@ -137,12 +137,12 @@ fun CourseSelectionTimetableSuccessView( TimetableForegroundCell( block = block, modifier = - Modifier - .offset( - x = columnWidth * (block.time.day.order - 1), - y = rowHeight * (block.time.period.order - 1), - ).width(columnWidth) - .height(rowHeight * block.span), + Modifier + .offset( + x = columnWidth * (block.time.day.order - 1), + y = rowHeight * (block.time.period.order - 1), + ).width(columnWidth) + .height(rowHeight * block.span), ) } @@ -201,8 +201,7 @@ fun TimetableBackground( } @Composable -fun TimetableBackgroundCell( -) { +fun TimetableBackgroundCell() { Surface( modifier = Modifier .fillMaxSize() @@ -234,17 +233,17 @@ fun TimetableForegroundCell( Surface( modifier = - modifier - .padding(2.dp), + modifier + .padding(2.dp), shape = RoundedCornerShape(4.dp), color = containerColor, ) { Box( contentAlignment = Alignment.Center, modifier = - Modifier - .fillMaxSize() - .padding(2.dp), + Modifier + .fillMaxSize() + .padding(2.dp), ) { Text( text = block.title, @@ -266,31 +265,31 @@ internal fun PreviewCourseSelectionTimetableSuccessView( CoursePlan( semester = "115-1", selectedCourses = - mapOf( - CourseSerialNo("36019") - to - CourseSummary( - serialNo = CourseSerialNo("36019"), - classNo = "ENA103-*", - title = "專題討論(III)", - credit = 0.0, - passwordCard = PasswordCardType.NONE, - teachers = listOf("鄭明敏", "林居慶", "林進榮", "林伯勳"), - classTimes = - listOf( - CourseTime(CourseDay.FRIDAY, CoursePeriod.A), - CourseTime(CourseDay.FRIDAY, CoursePeriod.B), - CourseTime(CourseDay.FRIDAY, CoursePeriod.C), - ), - limitCnt = 0, - admitCnt = 0, - waitCnt = 0, - collegeName = "工學院", - departmentName = "環境工程研究所碩士班", - courseType = CourseType.ELECTIVE, - detailUrl = "https://cis.ncu.edu.tw/Course/main/support/courseDetail.html?crs=36019", + mapOf( + CourseSerialNo("36019") + to + CourseSummary( + serialNo = CourseSerialNo("36019"), + classNo = "ENA103-*", + title = "專題討論(III)", + credit = 0.0, + passwordCard = PasswordCardType.NONE, + teachers = listOf("鄭明敏", "林居慶", "林進榮", "林伯勳"), + classTimes = + listOf( + CourseTime(CourseDay.FRIDAY, CoursePeriod.A), + CourseTime(CourseDay.FRIDAY, CoursePeriod.B), + CourseTime(CourseDay.FRIDAY, CoursePeriod.C), ), - ), + limitCnt = 0, + admitCnt = 0, + waitCnt = 0, + collegeName = "工學院", + departmentName = "環境工程研究所碩士班", + courseType = CourseType.ELECTIVE, + detailUrl = "https://cis.ncu.edu.tw/Course/main/support/courseDetail.html?crs=36019", + ), + ), ), ) { MaterialTheme { @@ -299,7 +298,7 @@ internal fun PreviewCourseSelectionTimetableSuccessView( CenterAlignedTopAppBar( title = { Text("我的課表") - } + }, ) }, modifier = Modifier.fillMaxSize(), @@ -307,9 +306,9 @@ internal fun PreviewCourseSelectionTimetableSuccessView( CourseSelectionTimetableSuccessView( plan = plan, modifier = - Modifier - .fillMaxSize() - .padding(innerPadding), + Modifier + .fillMaxSize() + .padding(innerPadding), ) } } diff --git a/shared/src/commonMain/kotlin/org/mpc/presentation/views/courseSelection/components/CourseCard.kt b/shared/src/commonMain/kotlin/org/mpc/presentation/views/courseSelection/components/CourseCard.kt index c293489..75e68d8 100644 --- a/shared/src/commonMain/kotlin/org/mpc/presentation/views/courseSelection/components/CourseCard.kt +++ b/shared/src/commonMain/kotlin/org/mpc/presentation/views/courseSelection/components/CourseCard.kt @@ -120,11 +120,11 @@ fun CourseCard( private fun CourseTypeBadge(courseType: CourseType) { Box( modifier = - Modifier - .background( - color = MaterialTheme.colorScheme.secondaryContainer, - shape = RoundedCornerShape(8.dp), - ).padding(horizontal = 10.dp, vertical = 5.dp), + Modifier + .background( + color = MaterialTheme.colorScheme.secondaryContainer, + shape = RoundedCornerShape(8.dp), + ).padding(horizontal = 10.dp, vertical = 5.dp), contentAlignment = Alignment.Center, ) { Text( @@ -141,9 +141,9 @@ private fun CourseTypeBadge(courseType: CourseType) { private fun CourseInfoRail(courseSummary: CourseSummary) { Row( modifier = - Modifier - .fillMaxWidth() - .horizontalScroll(rememberScrollState()), + Modifier + .fillMaxWidth() + .horizontalScroll(rememberScrollState()), horizontalArrangement = Arrangement.spacedBy(6.dp), ) { CourseInfoBadge( @@ -168,11 +168,11 @@ private fun CourseInfoBadge( ) { Row( modifier = - Modifier - .background( - color = MaterialTheme.colorScheme.surfaceContainerHighest, - shape = RoundedCornerShape(6.dp), - ).padding(horizontal = 8.dp, vertical = 5.dp), + Modifier + .background( + color = MaterialTheme.colorScheme.surfaceContainerHighest, + shape = RoundedCornerShape(6.dp), + ).padding(horizontal = 8.dp, vertical = 5.dp), verticalAlignment = Alignment.CenterVertically, ) { Icon( @@ -197,28 +197,25 @@ private fun CourseInfoBadge( */ private fun CourseSummary.teacherText(): String = teachers.joinToString(separator = "、") -private fun CourseSummary.informationText(): String = - listOf( - classNo, - "${credit.toInt()} 學分", - teacherText(), - ).joinToString(separator = " · ") +private fun CourseSummary.informationText(): String = listOf( + classNo, + "${credit.toInt()} 學分", + teacherText(), +).joinToString(separator = " · ") private fun CourseSummary.departmentText(): String = "$collegeName / $departmentName" -private fun CourseSummary.enrollmentText(): String = - if (waitCnt > 0) { - "$admitCnt / $limitCnt · 候補 $waitCnt" - } else { - "$admitCnt / $limitCnt" - } +private fun CourseSummary.enrollmentText(): String = if (waitCnt > 0) { + "$admitCnt / $limitCnt · 候補 $waitCnt" +} else { + "$admitCnt / $limitCnt" +} private fun CourseSummary.passwordText(): String = passwordCard.description -private fun CourseSummary.scheduleText(): String = - classTimes.joinToString(separator = "、") { - "${it.day.code}-${it.period.description}" - } +private fun CourseSummary.scheduleText(): String = classTimes.joinToString(separator = "、") { + "${it.day.code}-${it.period.description}" +} @Composable @Preview @@ -232,11 +229,11 @@ internal fun PreviewCourseCard( passwordCard = PasswordCardType.NONE, teachers = listOf("鄭明敏", "林居慶", "林進榮", "林伯勳"), classTimes = - listOf( - CourseTime(CourseDay.FRIDAY, CoursePeriod.A), - CourseTime(CourseDay.FRIDAY, CoursePeriod.B), - CourseTime(CourseDay.FRIDAY, CoursePeriod.C), - ), + listOf( + CourseTime(CourseDay.FRIDAY, CoursePeriod.A), + CourseTime(CourseDay.FRIDAY, CoursePeriod.B), + CourseTime(CourseDay.FRIDAY, CoursePeriod.C), + ), limitCnt = 0, admitCnt = 0, waitCnt = 0, diff --git a/shared/src/commonTest/kotlin/org/mpc/presentation/model/courseSelection/CourseDraftModelTest.kt b/shared/src/commonTest/kotlin/org/mpc/presentation/model/courseSelection/CourseDraftModelTest.kt index 48ee7e5..a13ce26 100644 --- a/shared/src/commonTest/kotlin/org/mpc/presentation/model/courseSelection/CourseDraftModelTest.kt +++ b/shared/src/commonTest/kotlin/org/mpc/presentation/model/courseSelection/CourseDraftModelTest.kt @@ -22,56 +22,55 @@ private data class CoursePlanFixture( val serviceLearning: CourseSummary, ) -private fun coursePlanFixture() = - CoursePlanFixture( - emptyPlan = - CoursePlan( - semester = SEMESTER, - selectedCourses = emptyMap(), - ), - freshmanPhysicalEducation = - CourseSummary( - serialNo = CourseSerialNo("01001"), - classNo = "PE1011-A", - title = "大一體育", - credit = 0.0, - passwordCard = PasswordCardType.OPTIONAL, - teachers = listOf("沈淑貞"), - classTimes = - listOf( - CourseTime(CourseDay.MONDAY, CoursePeriod.THREE), - CourseTime(CourseDay.MONDAY, CoursePeriod.FOUR), - ), - limitCnt = 0, - admitCnt = 0, - waitCnt = 0, - collegeName = "中心、處室", - departmentName = "體育室", - courseType = CourseType.REQUIRED, - detailUrl = "https://cis.ncu.edu.tw/Course/main/support/courseDetail.html?crs=01001", - ), - serviceLearning = - CourseSummary( - serialNo = CourseSerialNo("08025"), - classNo = "SC0003-1", - title = "服務學習課程", - credit = 0.0, - passwordCard = PasswordCardType.OPTIONAL, - teachers = listOf("孫致文", "李元皓", "王矞慈"), - classTimes = - listOf( - CourseTime(CourseDay.MONDAY, CoursePeriod.A), - CourseTime(CourseDay.MONDAY, CoursePeriod.B), - ), - limitCnt = 0, - admitCnt = 0, - waitCnt = 0, - collegeName = "中心、處室", - departmentName = "學務處-服務學習發展中心", - courseType = CourseType.REQUIRED, - detailUrl = "https://cis.ncu.edu.tw/Course/main/support/courseDetail.html?crs=08025", - ), - ) +private fun coursePlanFixture() = CoursePlanFixture( + emptyPlan = + CoursePlan( + semester = SEMESTER, + selectedCourses = emptyMap(), + ), + freshmanPhysicalEducation = + CourseSummary( + serialNo = CourseSerialNo("01001"), + classNo = "PE1011-A", + title = "大一體育", + credit = 0.0, + passwordCard = PasswordCardType.OPTIONAL, + teachers = listOf("沈淑貞"), + classTimes = + listOf( + CourseTime(CourseDay.MONDAY, CoursePeriod.THREE), + CourseTime(CourseDay.MONDAY, CoursePeriod.FOUR), + ), + limitCnt = 0, + admitCnt = 0, + waitCnt = 0, + collegeName = "中心、處室", + departmentName = "體育室", + courseType = CourseType.REQUIRED, + detailUrl = "https://cis.ncu.edu.tw/Course/main/support/courseDetail.html?crs=01001", + ), + serviceLearning = + CourseSummary( + serialNo = CourseSerialNo("08025"), + classNo = "SC0003-1", + title = "服務學習課程", + credit = 0.0, + passwordCard = PasswordCardType.OPTIONAL, + teachers = listOf("孫致文", "李元皓", "王矞慈"), + classTimes = + listOf( + CourseTime(CourseDay.MONDAY, CoursePeriod.A), + CourseTime(CourseDay.MONDAY, CoursePeriod.B), + ), + limitCnt = 0, + admitCnt = 0, + waitCnt = 0, + collegeName = "中心、處室", + departmentName = "學務處-服務學習發展中心", + courseType = CourseType.REQUIRED, + detailUrl = "https://cis.ncu.edu.tw/Course/main/support/courseDetail.html?crs=08025", + ), +) val courseDraftModelTests by testSuite { testFixture { @@ -82,7 +81,7 @@ val courseDraftModelTests by testSuite { assertEquals( expected = setOf(freshmanPhysicalEducation.serialNo), - actual = updatedPlan.selectedCourses.keys + actual = updatedPlan.selectedCourses.keys, ) assertTrue(emptyPlan.selectedCourses.isEmpty()) } diff --git a/shared/src/iosMain/kotlin/org/mpc/IosSharedHost.kt b/shared/src/iosMain/kotlin/org/mpc/IosSharedHost.kt index 40d7466..d88f965 100644 --- a/shared/src/iosMain/kotlin/org/mpc/IosSharedHost.kt +++ b/shared/src/iosMain/kotlin/org/mpc/IosSharedHost.kt @@ -18,19 +18,17 @@ class IosSharedHost internal constructor( fun courseSearchScreenController( bridge: CourseSearchBridge, planBridge: CoursePlanBridge, - ): UIViewController = - ComposeUIViewController { - ProvideAppDependencies(appGraph) { - CourseSearchResultViewBinding(bridge, planBridge) - } + ): UIViewController = ComposeUIViewController { + ProvideAppDependencies(appGraph) { + CourseSearchResultViewBinding(bridge, planBridge) } + } - fun courseSelectionTimetableScreenController(planBridge: CoursePlanBridge): UIViewController = - ComposeUIViewController { - ProvideAppDependencies(appGraph) { - CourseSelectionTimetableViewBinding(planBridge) - } + fun courseSelectionTimetableScreenController(planBridge: CoursePlanBridge): UIViewController = ComposeUIViewController { + ProvideAppDependencies(appGraph) { + CourseSelectionTimetableViewBinding(planBridge) } + } } fun createIosSharedHost(): IosSharedHost { diff --git a/shared/src/iosMain/kotlin/org/mpc/core/databaseBuidler.ios.kt b/shared/src/iosMain/kotlin/org/mpc/core/DatabaseBuilder.ios.kt similarity index 100% rename from shared/src/iosMain/kotlin/org/mpc/core/databaseBuidler.ios.kt rename to shared/src/iosMain/kotlin/org/mpc/core/DatabaseBuilder.ios.kt diff --git a/shared/src/iosMain/kotlin/org/mpc/core/dataStoreFactory.ios.kt b/shared/src/iosMain/kotlin/org/mpc/core/dataStoreFactory.ios.kt index ea933a3..c3bbaad 100644 --- a/shared/src/iosMain/kotlin/org/mpc/core/dataStoreFactory.ios.kt +++ b/shared/src/iosMain/kotlin/org/mpc/core/dataStoreFactory.ios.kt @@ -11,26 +11,25 @@ import platform.Foundation.NSFileManager import platform.Foundation.NSUserDomainMask @OptIn(ExperimentalForeignApi::class) -fun createDataStore(): OkioStorage = - OkioStorage( - fileSystem = FileSystem.SYSTEM, - serializer = PreferencesSerializer, - producePath = { - val applicationSupportPath = - requireNotNull( - NSFileManager.defaultManager.URLForDirectory( - directory = NSApplicationSupportDirectory, - inDomain = NSUserDomainMask, - appropriateForURL = null, - create = true, - error = null, - ), - ) - val filePath = - requireNotNull( - applicationSupportPath.URLByAppendingPathComponent(DATA_STORE_FILE_NAME), - ) +fun createDataStore(): OkioStorage = OkioStorage( + fileSystem = FileSystem.SYSTEM, + serializer = PreferencesSerializer, + producePath = { + val applicationSupportPath = + requireNotNull( + NSFileManager.defaultManager.URLForDirectory( + directory = NSApplicationSupportDirectory, + inDomain = NSUserDomainMask, + appropriateForURL = null, + create = true, + error = null, + ), + ) + val filePath = + requireNotNull( + applicationSupportPath.URLByAppendingPathComponent(DATA_STORE_FILE_NAME), + ) - requireNotNull(filePath.path).toPath() - }, - ) + requireNotNull(filePath.path).toPath() + }, +) From 35ff21b48c19ca988cd9b484907a628c021d782a Mon Sep 17 00:00:00 2001 From: Yuyu1115 <105599462+Yuyu-1115@users.noreply.github.com> Date: Thu, 13 Aug 2026 14:28:06 +0800 Subject: [PATCH 4/5] fix(ci): remove redundant setup for android sdk 37 according to the runner images docs, the android sdk is already included in the images, so there is no need for it. https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2404-Readme.md --- .github/workflows/lint.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index c858a65..bac62d7 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -36,9 +36,6 @@ jobs: with: cache-provider: basic - - name: Install Android SDK 37 - run: sdkmanager "platforms;android-37" - - name: Run Detekt and ktlint run: >- ./gradlew From a96cbfb922b6781f0496d98e98be74db90226117 Mon Sep 17 00:00:00 2001 From: Yuyu1115 <105599462+Yuyu-1115@users.noreply.github.com> Date: Thu, 13 Aug 2026 14:31:43 +0800 Subject: [PATCH 5/5] fix(ci): disable persistent credentials for checkout --- .github/workflows/lint.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index bac62d7..50c1dd2 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -23,6 +23,8 @@ jobs: steps: - name: Check out repository uses: actions/checkout@v7 + with: + persist-credentials: false # required for gradle 9.5.0 and android gradle plugin 9.3.0 - name: Set up Java @@ -53,6 +55,8 @@ jobs: steps: - name: Check out repository uses: actions/checkout@v7 + with: + persist-credentials: false - name: Install SwiftLint run: brew install swiftlint