From 36704739b7a87911ae7b897d463f0ce66399e471 Mon Sep 17 00:00:00 2001 From: taetae98coding Date: Mon, 18 Nov 2024 01:50:25 +0900 Subject: [PATCH] 1.2.0 feat: app - Tag fix: app - Change the date update time in CalendarScreen(Background/Foreground -> Focus Change) feat: server - Tag --- .github/workflows/build.yml | 3 +- .github/workflows/check_code_style.yml | 2 +- .github/workflows/dependency_guard.yml | 2 +- CHANGELOG.md | 13 +- app/core/account-preferences-memory/README.md | 3 - .../build.gradle.kts | 19 - .../memory/AccountMemoryPreferences.kt | 36 - .../memory/AccountPreferencesMemoryModule.kt | 15 - .../README.md | 4 +- .../backup-database-room/build.gradle.kts | 20 + .../1.json | 61 + .../backup/database/room/BackupDatabase.kt | 29 + .../database/room/BackupRoomDatabaseModule.kt | 17 + .../database/room/dao/MemoBackupEntityDao.kt | 16 + .../database/room/dao/MemoBackupRoomDao.kt | 24 + .../database/room/dao/TagBackupEntityDao.kt | 19 + .../database/room/dao/TagBackupRoomDao.kt | 24 + .../database/room/entity/MemoBackupEntity.kt | 11 + .../database/room/entity/TagBackupEntity.kt | 11 + .../internal/BackupDatabaseConstructor.kt | 6 + .../README.md | 4 +- app/core/backup-database/build.gradle.kts | 14 + .../core/backup/database/MemoBackupDao.kt | 10 + .../core/backup/database/TagBackupDao.kt | 10 + app/core/calendar-compose/build.gradle.kts | 1 - .../core/calendar/compose/DayOfWeekRow.kt | 23 +- .../calendar/compose/topbar/CalendarTopBar.kt | 15 +- app/core/{resources => compose}/README.md | 4 +- app/core/compose/build.gradle.kts | 28 + .../core/compose/back/KBackHandler.android.kt | 15 + .../adaptive/ThreePaneScaffoldNavigatorExt.kt | 36 + .../diary/core/compose/back/KBackHandler.kt | 9 + .../core/compose/button/FloatingAddButton.kt | 30 + .../core/compose/runtime/SkipProperty.kt | 16 + .../compose/swipe/FinishAndDeleteSwipeBox.kt | 91 + .../core/compose/back/KBackHandler.ios.kt | 6 + .../core/compose/back/KBackHandler.jvm.kt | 6 + .../core/compose/back/KBackHandler.wasmJs.kt | 6 + app/core/coroutines/build.gradle.kts | 1 - app/core/design-system/build.gradle.kts | 3 +- .../design/system/chip/DiaryFilterChip.kt | 49 + .../core/design/system/color/DiaryColor.kt | 1 + .../design/system/color/DiaryColorPicker.kt | 2 +- .../system/color/DiaryColorPickerDialog.kt | 8 +- .../system/date/DiaryDatePickerDialog.kt | 8 +- .../system/diary/component/DiaryComponent.kt | 12 +- .../design/system/diary/date/DiaryDate.kt | 8 +- .../diary/core/design/system/emoji/Emoji.kt | 5 +- .../core/design/system}/icon/AccountIcon.kt | 2 +- .../diary/core/design/system}/icon/AddIcon.kt | 2 +- .../core/design/system}/icon/CalendarIcon.kt | 2 +- .../design/system/icon/ChevronRightIcon.kt | 18 + .../core/design/system/icon/CircleIcon.kt | 18 + .../core/design/system}/icon/ClearIcon.kt | 2 +- .../core/design/system}/icon/DeleteIcon.kt | 6 +- .../core/design/system}/icon/DropDownIcon.kt | 2 +- .../core/design/system}/icon/DropUpIcon.kt | 2 +- .../core/design/system}/icon/EmailIcon.kt | 2 +- .../core/design/system}/icon/FinishIcon.kt | 6 +- .../diary/core/design/system}/icon/KeyIcon.kt | 2 +- .../core/design/system}/icon/LoginIcon.kt | 2 +- .../core/design/system}/icon/LogoutIcon.kt | 2 +- .../core/design/system}/icon/MarkdownIcon.kt | 2 +- .../core/design/system}/icon/MemoIcon.kt | 2 +- .../core/design/system}/icon/MoreIcon.kt | 2 +- .../design/system}/icon/NavigateUpIcon.kt | 2 +- .../core/design/system}/icon/RefreshIcon.kt | 2 +- .../diary/core/design/system/icon/TagIcon.kt | 18 + .../core/design/system}/icon/TextFieldIcon.kt | 2 +- .../design/system}/icon/VisibilityOffIcon.kt | 0 .../design/system}/icon/VisibilityOnIcon.kt | 0 .../core/design/system/text/ClearTextField.kt | 2 +- .../core/design/system/theme/DiaryTheme.kt | 2 + .../system/typography/DiaryTypography.kt | 1 + .../diary-database-memory/build.gradle.kts | 21 - .../database/memory/MemoBackupMemoryDao.kt | 54 - .../diary/database/memory/MemoMemoryDao.kt | 103 - .../2.json | 272 ++ .../core/diary/database/room/DiaryDatabase.kt | 23 +- .../database/room/DiaryRoomDatabaseModule.kt | 1 + .../core/diary/database/room/dao/EntityDao.kt | 19 - .../database/room/dao/MemoBackupEntityDao.kt | 24 - .../database/room/dao/MemoBackupRoomDao.kt | 33 - .../diary/database/room/dao/MemoEntityDao.kt | 48 + .../diary/database/room/dao/MemoRoomDao.kt | 31 +- .../database/room/dao/MemoTagEntityDao.kt | 24 + .../diary/database/room/dao/MemoTagRoomDao.kt | 29 + .../diary/database/room/dao/TagEntityDao.kt | 86 + .../diary/database/room/dao/TagRoomDao.kt | 68 + .../database/room/entity/MemoBackupEntity.kt | 21 - .../diary/database/room/entity/MemoEntity.kt | 19 +- .../database/room/entity/MemoTagEntity.kt | 35 + .../diary/database/room/entity/TagEntity.kt | 29 + .../diary/database/room/mapper/MemoMapper.kt | 4 +- .../database/room/mapper/MemoTagMapper.kt | 11 + .../diary/database/room/mapper/TagMapper.kt | 35 + .../database/room/migration/Migration.kt | 13 + .../core/diary/database/MemoBackupDao.kt | 12 - .../diary/core/diary/database/MemoDao.kt | 9 +- .../diary/core/diary/database/MemoTagDao.kt | 11 + .../diary/core/diary/database/TagDao.kt | 23 + .../core/diary/service/memo/MemoService.kt | 33 +- .../core/diary/service/memo/MemoTagService.kt | 35 + .../core/diary/service/memo/TagService.kt | 65 + .../holiday-database-memory/build.gradle.kts | 19 - .../database/memory/HolidayMemoryDao.kt | 24 - .../memory/HolidayMemoryDatabaseModule.kt | 15 - app/core/holiday-preferences-memory/README.md | 3 - .../build.gradle.kts | 19 - .../memory/HolidayMemoryPreferences.kt | 23 - .../memory/HolidayPreferencesMemoryModule.kt | 15 - .../diary/core/model/mapper/MemoMapper.kt | 14 + .../diary/core/model/mapper/TagMapper.kt | 27 + .../diary/core/model/memo/Memo.kt | 1 + .../diary/core/model/memo/MemoAndTagIds.kt | 6 + .../diary/core/model/memo/MemoDto.kt | 1 + .../diary/core/model/memo/MemoTag.kt | 10 + .../diary/core/model/memo/MemoTagDto.kt | 10 + .../diary/core/model/tag/Tag.kt | 12 + .../diary/core/model/tag/TagDetail.kt | 7 + .../diary/core/model/tag/TagDto.kt | 13 + .../diary/core/model/tag/TagWithPrimary.kt | 6 + .../navigation/memo/MemoDetailDestination.kt | 8 +- .../core/navigation/tag/TagAddDestination.kt | 6 + .../core/navigation/tag/TagDestination.kt | 6 + .../navigation/tag/TagDetailDestination.kt | 14 + app/core/resources/build.gradle.kts | 28 - .../composeResources/values-ko/strings.xml | 47 - .../composeResources/values/strings.xml | 47 - app/data/backup/build.gradle.kts | 1 + .../backup/repository/BackupRepositoryImpl.kt | 29 - .../repository/MemoBackupRepositoryImpl.kt | 39 + .../repository/TagBackupRepositoryImpl.kt | 39 + .../fetch/repository/FetchRepositoryImpl.kt | 29 - .../repository/MemoFetchRepositoryImpl.kt | 35 + .../repository/TagFetchRepositoryImpl.kt | 35 + .../memo/repository/MemoRepositoryImpl.kt | 22 +- .../memo/repository/MemoTagRepositoryImpl.kt | 28 + app/data/tag/README.md | 3 + app/data/tag/build.gradle.kts | 14 + .../diary/data/tag/TagDataModule.kt} | 4 +- .../data/tag/repository/TagRepositoryImpl.kt | 49 + ...pRepository.kt => MemoBackupRepository.kt} | 4 +- .../backup/repository/TagBackupRepository.kt | 6 + .../domain/backup/usecase/BackupUseCase.kt | 9 +- .../usecase/PushMemoBackupQueueUseCase.kt | 31 + .../usecase/PushTagBackupQueueUseCase.kt | 31 + app/domain/calendar/README.md | 3 + app/domain/calendar/build.gradle.kts | 15 + .../domain/calendar/CalendarDomainModule.kt | 8 + .../usecase/FindCalendarMemoUseCase.kt | 50 + app/domain/fetch/build.gradle.kts | 1 + .../fetch/repository/FetchRepository.kt | 5 - .../fetch/repository/MemoFetchRepository.kt | 5 + .../fetch/repository/TagFetchRepository.kt | 5 + .../domain/fetch/usecase/FetchUseCase.kt | 12 +- .../domain/memo/repository/MemoRepository.kt | 3 +- .../memo/repository/MemoTagRepository.kt | 11 + .../domain/memo/usecase/AddMemoUseCase.kt | 34 +- .../usecase/DeleteMemoPrimaryTagUseCase.kt | 20 + .../domain/memo/usecase/DeleteMemoUseCase.kt | 21 +- .../domain/memo/usecase/FindMemoTagUseCase.kt | 46 + .../domain/memo/usecase/FinishMemoUseCase.kt | 20 +- .../domain/memo/usecase/RestartMemoUseCase.kt | 22 +- .../memo/usecase/SelectMemoTagUseCase.kt | 20 + .../memo/usecase/UnselectMemoTagUseCase.kt | 29 + .../usecase/UpdateMemoPrimaryTagUseCase.kt | 20 + .../domain/memo/usecase/UpdateMemoUseCase.kt | 20 +- app/domain/tag/README.md | 3 + app/domain/tag/build.gradle.kts | 14 + .../diary/domain/tag/TagDomainModule.kt | 8 + .../domain/tag/repository/TagRepository.kt | 17 + .../diary/domain/tag/usecase/AddTagUseCase.kt | 43 + .../domain/tag/usecase/DeleteTagUseCase.kt | 20 + .../domain/tag/usecase/FindTagUseCase.kt | 26 + .../domain/tag/usecase/FinishTagUseCase.kt | 20 + .../domain/tag/usecase/PageTagUseCase.kt} | 15 +- .../domain/tag/usecase/RestartTagUseCase.kt | 20 + .../domain/tag/usecase/RestoreTagUseCase.kt | 20 + .../domain/tag/usecase/UpdateTagUseCase.kt | 27 + .../account/common/BasePasswordTextField.kt | 2 +- .../feature/account/common/EmailTextField.kt | 7 +- .../diary/feature/account/join/JoinScreen.kt | 41 +- .../feature/account/join/JoinViewModel.kt | 2 +- .../feature/account/join/state/JoinUiState.kt | 1 - .../feature/account/login/LoginScreen.kt | 32 +- app/feature/calendar/build.gradle.kts | 2 +- .../feature/calendar/CalendarMemoViewModel.kt | 2 +- .../diary/feature/calendar/CalendarScreen.kt | 6 +- app/feature/memo/build.gradle.kts | 1 + .../diary/feature/memo/MemoNavigation.kt | 6 + .../diary/feature/memo/add/MemoAddRoute.kt | 67 +- .../feature/memo/add/MemoAddViewModel.kt | 82 +- .../add/RememberMemoDetailScreenAddState.kt | 33 + .../feature/memo/detail/MemoDetailRoute.kt | 62 +- .../feature/memo/detail/MemoDetailScreen.kt | 118 +- .../memo/detail/MemoDetailScreenState.kt | 3 +- .../memo/detail/MemoDetailTagViewModel.kt | 119 + ...=> RememberMemoDetailScreenDetailState.kt} | 24 - .../diary/feature/memo/tag/MemoTag.kt | 86 + .../memo/tag/MemoTagNavigationButton.kt | 6 + .../diary/feature/memo/tag/MemoTagScreen.kt | 105 + .../diary/feature/memo/tag/TagFlow.kt | 65 + .../diary/feature/memo/tag/TagUiState.kt | 12 + .../diary/feature/more/MoreScreen.kt | 5 +- .../diary/feature/more/account/MoreAccount.kt | 20 +- app/feature/tag/README.md | 3 + app/feature/tag/build.gradle.kts | 17 + .../diary/feature/tag/TagFeatureModule.kt | 8 + .../diary/feature/tag/TagNavigate.kt | 9 + .../diary/feature/tag/TagNavigation.kt | 35 + .../diary/feature/tag/TagRoute.kt | 247 ++ .../add/RememberTagDetailScreenAddState.kt | 23 + .../diary/feature/tag/add/TagAddRoute.kt | 51 + .../diary/feature/tag/add/TagAddViewModel.kt | 48 + .../RememberTagDetailScreenDetailState.kt | 38 + .../tag/detail/TagDetailActionButton.kt | 11 + .../tag/detail/TagDetailFloatingButton.kt | 6 + .../tag/detail/TagDetailNavigationButton.kt | 6 + .../feature/tag/detail/TagDetailRoute.kt | 55 + .../feature/tag/detail/TagDetailScreen.kt | 228 ++ .../tag/detail/TagDetailScreenState.kt | 61 + .../tag/detail/TagDetailScreenUiState.kt | 13 + .../feature/tag/detail/TagDetailViewModel.kt | 115 + .../tag/list/RememberTagListScreenState.kt | 14 + .../feature/tag/list/TagListFloatingButton.kt | 6 + .../feature/tag/list/TagListItemUiState.kt | 10 + .../diary/feature/tag/list/TagListScreen.kt | 192 ++ .../feature/tag/list/TagListScreenState.kt | 43 + .../feature/tag/list/TagListScreenUiState.kt | 12 + .../feature/tag/list/TagListViewModel.kt | 92 + app/platform/android/build.gradle.kts | 4 - .../realReleaseRuntimeClasspath.txt | 82 +- .../taetae98coding/diary/DiaryActivity.kt | 4 +- .../diary/initializer/KoinInitializer.kt | 8 - app/platform/common/build.gradle.kts | 13 + .../io/github/taetae98coding/diary/app/App.kt | 41 +- .../taetae98coding/diary/app/AppModule.kt | 23 + .../taetae98coding/diary/app/AppNavHost.kt | 2 + .../diary/app/navigation/AppNavigation.kt | 19 +- app/platform/ios/build.gradle.kts | 4 - app/platform/jvm/build.gradle.kts | 4 - .../diary/initializer/KoinInitializer.kt | 8 - app/platform/wasm/README.md | 3 - app/platform/wasm/build.gradle.kts | 73 - .../github/taetae98coding/diary/WasmJsApp.kt | 31 - .../AppLifecycleOwnerInitializer.kt | 8 - .../initializer/BackupManagerInitializer.kt | 14 - .../initializer/FetchManagerInitializer.kt | 14 - .../diary/initializer/JsInitializer.kt | 9 - .../diary/initializer/KoinInitializer.kt | 43 - .../wasm/src/wasmJsMain/resources/index.html | 13 - .../src/main/kotlin/ext/DependencyExt.kt | 24 - .../plugin/convention/AppFeaturePlugin.kt | 12 +- .../kotlin/plugin/koin/KoinDataStorePlugin.kt | 6 +- .../main/kotlin/plugin/koin/KoinRoomPlugin.kt | 6 +- .../kotlin/KotlinMultiplatformAllPlugin.kt | 5 - .../kotlin/KotlinMultiplatformCommonPlugin.kt | 6 - .../src/main/kotlin/plugin/room/RoomPlugin.kt | 4 +- .../exception/tag/TagTitleBlankException.kt | 3 + .../diary/common/model/memo/MemoEntity.kt | 24 +- .../diary/common/model/memo/MemoTagEntity.kt | 17 + .../diary/common/model/memo/TagEntity.kt | 25 + ...dep_graph_app_core_account_preferences.svg | 9 - ...app_core_account_preferences_datastore.svg | 25 - ...ph_app_core_account_preferences_memory.svg | 17 - .../dep_graph_app_core_backup_database.svg | 17 + ...ep_graph_app_core_backup_database_room.svg | 57 + .../dep_graph_app_core_calendar_compose.svg | 52 +- .../graphs/dep_graph_app_core_compose.svg | 33 + .../graphs/dep_graph_app_core_coroutines.svg | 9 - .../dep_graph_app_core_design_system.svg | 32 +- .../dep_graph_app_core_diary_database.svg | 17 - ...p_graph_app_core_diary_database_memory.svg | 41 - ...dep_graph_app_core_diary_database_room.svg | 57 - .../dep_graph_app_core_diary_service.svg | 41 - .../dep_graph_app_core_holiday_database.svg | 17 - ...graph_app_core_holiday_database_memory.svg | 25 - ...p_graph_app_core_holiday_database_room.svg | 57 - ...dep_graph_app_core_holiday_preferences.svg | 9 - ...app_core_holiday_preferences_datastore.svg | 33 - ...ph_app_core_holiday_preferences_memory.svg | 17 - .../dep_graph_app_core_holiday_service.svg | 17 - .../graphs/dep_graph_app_core_model.svg | 9 - .../graphs/dep_graph_app_core_navigation.svg | 17 - .../graphs/dep_graph_app_core_resources.svg | 9 - .../graphs/dep_graph_app_data_account.svg | 73 - .../graphs/dep_graph_app_data_backup.svg | 148 +- .../graphs/dep_graph_app_data_credential.svg | 254 +- docs/images/graphs/dep_graph_app_data_fcm.svg | 141 - .../graphs/dep_graph_app_data_fetch.svg | 164 +- .../graphs/dep_graph_app_data_holiday.svg | 97 - .../images/graphs/dep_graph_app_data_memo.svg | 137 - docs/images/graphs/dep_graph_app_data_tag.svg | 137 + .../graphs/dep_graph_app_domain_account.svg | 49 - .../graphs/dep_graph_app_domain_backup.svg | 77 - .../graphs/dep_graph_app_domain_calendar.svg | 177 + .../dep_graph_app_domain_credential.svg | 194 +- .../graphs/dep_graph_app_domain_fcm.svg | 77 - .../graphs/dep_graph_app_domain_fetch.svg | 122 +- .../graphs/dep_graph_app_domain_holiday.svg | 49 - .../graphs/dep_graph_app_domain_memo.svg | 109 - .../graphs/dep_graph_app_domain_tag.svg | 109 + .../graphs/dep_graph_app_feature_account.svg | 306 +- .../graphs/dep_graph_app_feature_calendar.svg | 352 +- .../graphs/dep_graph_app_feature_memo.svg | 236 +- .../graphs/dep_graph_app_feature_more.svg | 306 +- .../graphs/dep_graph_app_feature_tag.svg | 201 ++ .../graphs/dep_graph_app_platform_android.svg | 1260 ++++---- .../graphs/dep_graph_app_platform_common.svg | 1216 ++++--- .../graphs/dep_graph_app_platform_ios.svg | 1258 ++++---- .../graphs/dep_graph_app_platform_jvm.svg | 1266 ++++---- .../graphs/dep_graph_app_platform_wasm.svg | 925 ------ .../graphs/dep_graph_common_exception.svg | 9 - docs/images/graphs/dep_graph_common_model.svg | 9 - .../images/graphs/dep_graph_library_color.svg | 9 - .../graphs/dep_graph_library_coroutines.svg | 9 - .../graphs/dep_graph_library_datetime.svg | 9 - .../dep_graph_library_firebase_common.svg | 9 - .../dep_graph_library_firebase_messaging.svg | 17 - .../dep_graph_library_koin_datastore.svg | 9 - .../graphs/dep_graph_library_koin_room.svg | 9 - .../graphs/dep_graph_library_kotlin.svg | 9 - .../graphs/dep_graph_library_navigation.svg | 17 - docs/images/graphs/dep_graph_library_room.svg | 17 - .../graphs/dep_graph_library_shimmer_m3.svg | 17 - docs/images/graphs/dep_graph_server_app.svg | 316 +- .../graphs/dep_graph_server_core_database.svg | 17 - .../graphs/dep_graph_server_core_model.svg | 9 - .../graphs/dep_graph_server_data_account.svg | 57 - .../graphs/dep_graph_server_data_fcm.svg | 57 - .../graphs/dep_graph_server_data_memo.svg | 57 - .../graphs/dep_graph_server_data_tag.svg | 57 + .../dep_graph_server_domain_account.svg | 33 - .../graphs/dep_graph_server_domain_fcm.svg | 33 - .../graphs/dep_graph_server_domain_memo.svg | 33 - .../graphs/dep_graph_server_domain_tag.svg | 33 + .../dep_graph_server_feature_account.svg | 57 - .../graphs/dep_graph_server_feature_fcm.svg | 57 - .../graphs/dep_graph_server_feature_home.svg | 33 - .../graphs/dep_graph_server_feature_memo.svg | 57 - .../graphs/dep_graph_server_feature_tag.svg | 57 + gradle/libs.versions.toml | 4 +- kotlin-js-store/yarn.lock | 2845 ----------------- .../diary/library/coroutines/FlowExt.kt | 12 + library/firebase-messaging/build.gradle.kts | 1 - .../diary/library/room/dao/EntityDao.kt | 19 + server/app/build.gradle.kts | 3 + server/app/dependencies/runtimeClasspath.txt | 14 +- .../diary/plugin/DatabasePlugin.kt | 10 +- .../taetae98coding/diary/plugin/KoinPlugin.kt | 4 + .../diary/plugin/RoutingPlugin.kt | 2 + .../diary/core/database/AccountTable.kt | 35 +- .../diary/core/database/FCMTokenTable.kt | 19 +- .../diary/core/database/MemoTable.kt | 62 +- .../diary/core/database/MemoTagTable.kt | 44 + .../diary/core/database/TagTable.kt | 70 + .../taetae98coding/diary/core/model/Memo.kt | 1 + .../diary/core/model/MemoAndTagIds.kt | 6 + .../taetae98coding/diary/core/model/Tag.kt | 14 + .../repository/AccountRepositoryImpl.kt | 9 +- .../data/fcm/repository/FCMRepositoryImpl.kt | 9 +- .../memo/repository/MemoRepositoryImpl.kt | 31 +- server/data/tag/README.md | 3 + server/data/tag/build.gradle.kts | 11 + .../diary/data/tag/TagDataModule.kt | 8 + .../data/tag/repository/TagRepositoryImpl.kt | 23 + .../domain/memo/repository/MemoRepository.kt | 7 +- .../domain/memo/usecase/FetchMemoUseCase.kt | 6 +- .../domain/memo/usecase/UpsertMemoUseCase.kt | 31 +- server/domain/tag/README.md | 3 + server/domain/tag/build.gradle.kts | 3 + .../diary/domain/tag/TagDomainModule.kt | 8 + .../domain/tag/repository/TagRepository.kt | 13 + .../domain/tag/usecase/FetchTagUseCase.kt | 23 + .../domain/tag/usecase/UpsertTagUseCase.kt | 41 + .../diary/feature/memo/MemoRouting.kt | 36 +- server/feature/tag/README.md | 3 + server/feature/tag/build.gradle.kts | 7 + .../diary/feature/tag/TagRouting.kt | 78 + settings.gradle.kts | 17 +- 381 files changed, 10551 insertions(+), 10399 deletions(-) delete mode 100644 app/core/account-preferences-memory/README.md delete mode 100644 app/core/account-preferences-memory/build.gradle.kts delete mode 100644 app/core/account-preferences-memory/src/commonMain/kotlin/io/github/taetae98coding/diary/fore/account/preferences/memory/AccountMemoryPreferences.kt delete mode 100644 app/core/account-preferences-memory/src/commonMain/kotlin/io/github/taetae98coding/diary/fore/account/preferences/memory/AccountPreferencesMemoryModule.kt rename app/core/{diary-database-memory => backup-database-room}/README.md (54%) create mode 100644 app/core/backup-database-room/build.gradle.kts create mode 100644 app/core/backup-database-room/schemas/io.github.taetae98coding.diary.core.backup.database.room.BackupDatabase/1.json create mode 100644 app/core/backup-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/room/BackupDatabase.kt create mode 100644 app/core/backup-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/room/BackupRoomDatabaseModule.kt create mode 100644 app/core/backup-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/room/dao/MemoBackupEntityDao.kt create mode 100644 app/core/backup-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/room/dao/MemoBackupRoomDao.kt create mode 100644 app/core/backup-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/room/dao/TagBackupEntityDao.kt create mode 100644 app/core/backup-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/room/dao/TagBackupRoomDao.kt create mode 100644 app/core/backup-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/room/entity/MemoBackupEntity.kt create mode 100644 app/core/backup-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/room/entity/TagBackupEntity.kt create mode 100644 app/core/backup-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/room/internal/BackupDatabaseConstructor.kt rename app/core/{holiday-database-memory => backup-database}/README.md (52%) create mode 100644 app/core/backup-database/build.gradle.kts create mode 100644 app/core/backup-database/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/MemoBackupDao.kt create mode 100644 app/core/backup-database/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/TagBackupDao.kt rename app/core/{resources => compose}/README.md (64%) create mode 100644 app/core/compose/build.gradle.kts create mode 100644 app/core/compose/src/androidMain/kotlin/io/github/taetae98coding/diary/core/compose/back/KBackHandler.android.kt create mode 100644 app/core/compose/src/commonMain/kotlin/io/github/taetae98coding/diary/core/compose/adaptive/ThreePaneScaffoldNavigatorExt.kt create mode 100644 app/core/compose/src/commonMain/kotlin/io/github/taetae98coding/diary/core/compose/back/KBackHandler.kt create mode 100644 app/core/compose/src/commonMain/kotlin/io/github/taetae98coding/diary/core/compose/button/FloatingAddButton.kt create mode 100644 app/core/compose/src/commonMain/kotlin/io/github/taetae98coding/diary/core/compose/runtime/SkipProperty.kt create mode 100644 app/core/compose/src/commonMain/kotlin/io/github/taetae98coding/diary/core/compose/swipe/FinishAndDeleteSwipeBox.kt create mode 100644 app/core/compose/src/iosMain/kotlin/io/github/taetae98coding/diary/core/compose/back/KBackHandler.ios.kt create mode 100644 app/core/compose/src/jvmMain/kotlin/io/github/taetae98coding/diary/core/compose/back/KBackHandler.jvm.kt create mode 100644 app/core/compose/src/wasmJsMain/kotlin/io/github/taetae98coding/diary/core/compose/back/KBackHandler.wasmJs.kt create mode 100644 app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/chip/DiaryFilterChip.kt rename app/core/{resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources => design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system}/icon/AccountIcon.kt (86%) rename app/core/{resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources => design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system}/icon/AddIcon.kt (86%) rename app/core/{resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources => design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system}/icon/CalendarIcon.kt (87%) create mode 100644 app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/ChevronRightIcon.kt create mode 100644 app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/CircleIcon.kt rename app/core/{resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources => design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system}/icon/ClearIcon.kt (86%) rename app/core/{resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources => design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system}/icon/DeleteIcon.kt (65%) rename app/core/{resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources => design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system}/icon/DropDownIcon.kt (87%) rename app/core/{resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources => design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system}/icon/DropUpIcon.kt (87%) rename app/core/{resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources => design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system}/icon/EmailIcon.kt (87%) rename app/core/{resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources => design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system}/icon/FinishIcon.kt (65%) rename app/core/{resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources => design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system}/icon/KeyIcon.kt (86%) rename app/core/{resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources => design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system}/icon/LoginIcon.kt (87%) rename app/core/{resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources => design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system}/icon/LogoutIcon.kt (87%) rename app/core/{resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources => design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system}/icon/MarkdownIcon.kt (86%) rename app/core/{resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources => design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system}/icon/MemoIcon.kt (87%) rename app/core/{resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources => design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system}/icon/MoreIcon.kt (87%) rename app/core/{resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources => design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system}/icon/NavigateUpIcon.kt (87%) rename app/core/{resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources => design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system}/icon/RefreshIcon.kt (87%) create mode 100644 app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/TagIcon.kt rename app/core/{resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources => design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system}/icon/TextFieldIcon.kt (87%) rename app/core/{resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources => design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system}/icon/VisibilityOffIcon.kt (100%) rename app/core/{resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources => design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system}/icon/VisibilityOnIcon.kt (100%) delete mode 100644 app/core/diary-database-memory/build.gradle.kts delete mode 100644 app/core/diary-database-memory/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/memory/MemoBackupMemoryDao.kt delete mode 100644 app/core/diary-database-memory/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/memory/MemoMemoryDao.kt create mode 100644 app/core/diary-database-room/schemas/io.github.taetae98coding.diary.core.diary.database.room.DiaryDatabase/2.json delete mode 100644 app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/dao/EntityDao.kt delete mode 100644 app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/dao/MemoBackupEntityDao.kt delete mode 100644 app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/dao/MemoBackupRoomDao.kt create mode 100644 app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/dao/MemoTagEntityDao.kt create mode 100644 app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/dao/MemoTagRoomDao.kt create mode 100644 app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/dao/TagEntityDao.kt create mode 100644 app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/dao/TagRoomDao.kt delete mode 100644 app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/entity/MemoBackupEntity.kt create mode 100644 app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/entity/MemoTagEntity.kt create mode 100644 app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/entity/TagEntity.kt create mode 100644 app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/mapper/MemoTagMapper.kt create mode 100644 app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/mapper/TagMapper.kt create mode 100644 app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/migration/Migration.kt delete mode 100644 app/core/diary-database/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/MemoBackupDao.kt create mode 100644 app/core/diary-database/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/MemoTagDao.kt create mode 100644 app/core/diary-database/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/TagDao.kt create mode 100644 app/core/diary-service/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/service/memo/MemoTagService.kt create mode 100644 app/core/diary-service/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/service/memo/TagService.kt delete mode 100644 app/core/holiday-database-memory/build.gradle.kts delete mode 100644 app/core/holiday-database-memory/src/commonMain/kotlin/io/github/taetae98coding/diary/core/holiday/database/memory/HolidayMemoryDao.kt delete mode 100644 app/core/holiday-database-memory/src/commonMain/kotlin/io/github/taetae98coding/diary/core/holiday/database/memory/HolidayMemoryDatabaseModule.kt delete mode 100644 app/core/holiday-preferences-memory/README.md delete mode 100644 app/core/holiday-preferences-memory/build.gradle.kts delete mode 100644 app/core/holiday-preferences-memory/src/commonMain/kotlin/io/github/taetae98coding/diary/core/holiday/preferences/memory/HolidayMemoryPreferences.kt delete mode 100644 app/core/holiday-preferences-memory/src/commonMain/kotlin/io/github/taetae98coding/diary/core/holiday/preferences/memory/HolidayPreferencesMemoryModule.kt create mode 100644 app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/mapper/TagMapper.kt create mode 100644 app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/memo/MemoAndTagIds.kt create mode 100644 app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/memo/MemoTag.kt create mode 100644 app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/memo/MemoTagDto.kt create mode 100644 app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/tag/Tag.kt create mode 100644 app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/tag/TagDetail.kt create mode 100644 app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/tag/TagDto.kt create mode 100644 app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/tag/TagWithPrimary.kt create mode 100644 app/core/navigation/src/commonMain/kotlin/io/github/taetae98coding/diary/core/navigation/tag/TagAddDestination.kt create mode 100644 app/core/navigation/src/commonMain/kotlin/io/github/taetae98coding/diary/core/navigation/tag/TagDestination.kt create mode 100644 app/core/navigation/src/commonMain/kotlin/io/github/taetae98coding/diary/core/navigation/tag/TagDetailDestination.kt delete mode 100644 app/core/resources/build.gradle.kts delete mode 100644 app/core/resources/src/commonMain/composeResources/values-ko/strings.xml delete mode 100644 app/core/resources/src/commonMain/composeResources/values/strings.xml delete mode 100644 app/data/backup/src/commonMain/kotlin/io/github/taetae98coding/diary/data/backup/repository/BackupRepositoryImpl.kt create mode 100644 app/data/backup/src/commonMain/kotlin/io/github/taetae98coding/diary/data/backup/repository/MemoBackupRepositoryImpl.kt create mode 100644 app/data/backup/src/commonMain/kotlin/io/github/taetae98coding/diary/data/backup/repository/TagBackupRepositoryImpl.kt delete mode 100644 app/data/fetch/src/commonMain/kotlin/io/github/taetae98coding/diary/data/fetch/repository/FetchRepositoryImpl.kt create mode 100644 app/data/fetch/src/commonMain/kotlin/io/github/taetae98coding/diary/data/fetch/repository/MemoFetchRepositoryImpl.kt create mode 100644 app/data/fetch/src/commonMain/kotlin/io/github/taetae98coding/diary/data/fetch/repository/TagFetchRepositoryImpl.kt create mode 100644 app/data/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/data/memo/repository/MemoTagRepositoryImpl.kt create mode 100644 app/data/tag/README.md create mode 100644 app/data/tag/build.gradle.kts rename app/{core/diary-database-memory/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/memory/DiaryMemoryDatabaseModule.kt => data/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/data/tag/TagDataModule.kt} (51%) create mode 100644 app/data/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/data/tag/repository/TagRepositoryImpl.kt rename app/domain/backup/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/backup/repository/{BackupRepository.kt => MemoBackupRepository.kt} (50%) create mode 100644 app/domain/backup/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/backup/repository/TagBackupRepository.kt create mode 100644 app/domain/backup/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/backup/usecase/PushMemoBackupQueueUseCase.kt create mode 100644 app/domain/backup/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/backup/usecase/PushTagBackupQueueUseCase.kt create mode 100644 app/domain/calendar/README.md create mode 100644 app/domain/calendar/build.gradle.kts create mode 100644 app/domain/calendar/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/calendar/CalendarDomainModule.kt create mode 100644 app/domain/calendar/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/calendar/usecase/FindCalendarMemoUseCase.kt delete mode 100644 app/domain/fetch/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/fetch/repository/FetchRepository.kt create mode 100644 app/domain/fetch/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/fetch/repository/MemoFetchRepository.kt create mode 100644 app/domain/fetch/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/fetch/repository/TagFetchRepository.kt create mode 100644 app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/repository/MemoTagRepository.kt create mode 100644 app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/DeleteMemoPrimaryTagUseCase.kt create mode 100644 app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/FindMemoTagUseCase.kt create mode 100644 app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/SelectMemoTagUseCase.kt create mode 100644 app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/UnselectMemoTagUseCase.kt create mode 100644 app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/UpdateMemoPrimaryTagUseCase.kt create mode 100644 app/domain/tag/README.md create mode 100644 app/domain/tag/build.gradle.kts create mode 100644 app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/TagDomainModule.kt create mode 100644 app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/repository/TagRepository.kt create mode 100644 app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/AddTagUseCase.kt create mode 100644 app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/DeleteTagUseCase.kt create mode 100644 app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/FindTagUseCase.kt create mode 100644 app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/FinishTagUseCase.kt rename app/domain/{memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/FindCalendarMemoUseCase.kt => tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/PageTagUseCase.kt} (60%) create mode 100644 app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/RestartTagUseCase.kt create mode 100644 app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/RestoreTagUseCase.kt create mode 100644 app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/UpdateTagUseCase.kt create mode 100644 app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/add/RememberMemoDetailScreenAddState.kt create mode 100644 app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/detail/MemoDetailTagViewModel.kt rename app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/detail/{RememberMemoDetailScreenState.kt => RememberMemoDetailScreenDetailState.kt} (71%) create mode 100644 app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/tag/MemoTag.kt create mode 100644 app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/tag/MemoTagNavigationButton.kt create mode 100644 app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/tag/MemoTagScreen.kt create mode 100644 app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/tag/TagFlow.kt create mode 100644 app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/tag/TagUiState.kt create mode 100644 app/feature/tag/README.md create mode 100644 app/feature/tag/build.gradle.kts create mode 100644 app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/TagFeatureModule.kt create mode 100644 app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/TagNavigate.kt create mode 100644 app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/TagNavigation.kt create mode 100644 app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/TagRoute.kt create mode 100644 app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/add/RememberTagDetailScreenAddState.kt create mode 100644 app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/add/TagAddRoute.kt create mode 100644 app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/add/TagAddViewModel.kt create mode 100644 app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/RememberTagDetailScreenDetailState.kt create mode 100644 app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailActionButton.kt create mode 100644 app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailFloatingButton.kt create mode 100644 app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailNavigationButton.kt create mode 100644 app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailRoute.kt create mode 100644 app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailScreen.kt create mode 100644 app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailScreenState.kt create mode 100644 app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailScreenUiState.kt create mode 100644 app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailViewModel.kt create mode 100644 app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/list/RememberTagListScreenState.kt create mode 100644 app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/list/TagListFloatingButton.kt create mode 100644 app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/list/TagListItemUiState.kt create mode 100644 app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/list/TagListScreen.kt create mode 100644 app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/list/TagListScreenState.kt create mode 100644 app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/list/TagListScreenUiState.kt create mode 100644 app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/list/TagListViewModel.kt delete mode 100644 app/platform/wasm/README.md delete mode 100644 app/platform/wasm/build.gradle.kts delete mode 100644 app/platform/wasm/src/wasmJsMain/kotlin/io/github/taetae98coding/diary/WasmJsApp.kt delete mode 100644 app/platform/wasm/src/wasmJsMain/kotlin/io/github/taetae98coding/diary/initializer/AppLifecycleOwnerInitializer.kt delete mode 100644 app/platform/wasm/src/wasmJsMain/kotlin/io/github/taetae98coding/diary/initializer/BackupManagerInitializer.kt delete mode 100644 app/platform/wasm/src/wasmJsMain/kotlin/io/github/taetae98coding/diary/initializer/FetchManagerInitializer.kt delete mode 100644 app/platform/wasm/src/wasmJsMain/kotlin/io/github/taetae98coding/diary/initializer/JsInitializer.kt delete mode 100644 app/platform/wasm/src/wasmJsMain/kotlin/io/github/taetae98coding/diary/initializer/KoinInitializer.kt delete mode 100644 app/platform/wasm/src/wasmJsMain/resources/index.html create mode 100644 common/exception/src/commonMain/kotlin/io/github/taetae98coding/diary/common/exception/tag/TagTitleBlankException.kt create mode 100644 common/model/src/commonMain/kotlin/io/github/taetae98coding/diary/common/model/memo/MemoTagEntity.kt create mode 100644 common/model/src/commonMain/kotlin/io/github/taetae98coding/diary/common/model/memo/TagEntity.kt delete mode 100644 docs/images/graphs/dep_graph_app_core_account_preferences_memory.svg create mode 100644 docs/images/graphs/dep_graph_app_core_backup_database.svg create mode 100644 docs/images/graphs/dep_graph_app_core_backup_database_room.svg create mode 100644 docs/images/graphs/dep_graph_app_core_compose.svg delete mode 100644 docs/images/graphs/dep_graph_app_core_diary_database_memory.svg delete mode 100644 docs/images/graphs/dep_graph_app_core_holiday_database_memory.svg delete mode 100644 docs/images/graphs/dep_graph_app_core_holiday_preferences_memory.svg delete mode 100644 docs/images/graphs/dep_graph_app_core_resources.svg create mode 100644 docs/images/graphs/dep_graph_app_data_tag.svg create mode 100644 docs/images/graphs/dep_graph_app_domain_calendar.svg create mode 100644 docs/images/graphs/dep_graph_app_domain_tag.svg create mode 100644 docs/images/graphs/dep_graph_app_feature_tag.svg delete mode 100644 docs/images/graphs/dep_graph_app_platform_wasm.svg create mode 100644 docs/images/graphs/dep_graph_server_data_tag.svg create mode 100644 docs/images/graphs/dep_graph_server_domain_tag.svg create mode 100644 docs/images/graphs/dep_graph_server_feature_tag.svg delete mode 100644 kotlin-js-store/yarn.lock create mode 100644 library/room/src/commonMain/kotlin/io/github/taetae98coding/diary/library/room/dao/EntityDao.kt create mode 100644 server/core/database/src/main/kotlin/io/github/taetae98coding/diary/core/database/MemoTagTable.kt create mode 100644 server/core/database/src/main/kotlin/io/github/taetae98coding/diary/core/database/TagTable.kt create mode 100644 server/core/model/src/main/kotlin/io/github/taetae98coding/diary/core/model/MemoAndTagIds.kt create mode 100644 server/core/model/src/main/kotlin/io/github/taetae98coding/diary/core/model/Tag.kt create mode 100644 server/data/tag/README.md create mode 100644 server/data/tag/build.gradle.kts create mode 100644 server/data/tag/src/main/kotlin/io/github/taetae98coding/diary/data/tag/TagDataModule.kt create mode 100644 server/data/tag/src/main/kotlin/io/github/taetae98coding/diary/data/tag/repository/TagRepositoryImpl.kt create mode 100644 server/domain/tag/README.md create mode 100644 server/domain/tag/build.gradle.kts create mode 100644 server/domain/tag/src/main/kotlin/io/github/taetae98coding/diary/domain/tag/TagDomainModule.kt create mode 100644 server/domain/tag/src/main/kotlin/io/github/taetae98coding/diary/domain/tag/repository/TagRepository.kt create mode 100644 server/domain/tag/src/main/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/FetchTagUseCase.kt create mode 100644 server/domain/tag/src/main/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/UpsertTagUseCase.kt create mode 100644 server/feature/tag/README.md create mode 100644 server/feature/tag/build.gradle.kts create mode 100644 server/feature/tag/src/main/kotlin/io/github/taetae98coding/diary/feature/tag/TagRouting.kt diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f183da74..bbfbf9ae 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,6 +1,6 @@ name: Build -on: [ push, pull_request ] +on: [ pull_request ] jobs: Linux-Build: @@ -9,7 +9,6 @@ jobs: matrix: command: [ './gradlew :app:platform:jvm:assemble', - './gradlew :app:platform:wasm:assemble', './gradlew :app:platform:android:assembleRealRelease', ] steps: diff --git a/.github/workflows/check_code_style.yml b/.github/workflows/check_code_style.yml index c56e8c32..304533d1 100644 --- a/.github/workflows/check_code_style.yml +++ b/.github/workflows/check_code_style.yml @@ -1,6 +1,6 @@ name: Check Code Style -on: [ push, pull_request ] +on: [ pull_request ] jobs: Spotless: diff --git a/.github/workflows/dependency_guard.yml b/.github/workflows/dependency_guard.yml index 913c4e4d..f8d9d26e 100644 --- a/.github/workflows/dependency_guard.yml +++ b/.github/workflows/dependency_guard.yml @@ -1,6 +1,6 @@ name: Dependency Guard -on: [ push, pull_request ] +on: [ pull_request ] jobs: Dependency-Guard: diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c78aa2b..19f76195 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,18 @@ All notable changes to this project will be documented in this file. -## [1.1.0] - 2024-11-17 +## [1.2.0] - 2024-11-23 + +### 🚀 Features + +- App - Tag +- Server - Tag + +### 🐛 Bug Fixes + +- App - Change the date update time in CalendarScreen(Background/Foreground -> Focus Change) + +## [1.1.0] - 2024-11-18 ### 🚀 Features diff --git a/app/core/account-preferences-memory/README.md b/app/core/account-preferences-memory/README.md deleted file mode 100644 index 68b1d1a4..00000000 --- a/app/core/account-preferences-memory/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# :app:core:account-preferences-memory module -## Dependency graph -![Dependency graph](../../../docs/images/graphs/dep_graph_app_core_account_preferences_memory.svg) diff --git a/app/core/account-preferences-memory/build.gradle.kts b/app/core/account-preferences-memory/build.gradle.kts deleted file mode 100644 index 9811752b..00000000 --- a/app/core/account-preferences-memory/build.gradle.kts +++ /dev/null @@ -1,19 +0,0 @@ -plugins { - id("diary.android.library") - id("diary.kotlin.multiplatform.all") - id("diary.koin.all") -} - -kotlin { - sourceSets { - commonMain { - dependencies { - implementation(project(":app:core:account-preferences")) - } - } - } -} - -android { - namespace = "${Build.NAMESPACE}.core.account.preferences.memory" -} diff --git a/app/core/account-preferences-memory/src/commonMain/kotlin/io/github/taetae98coding/diary/fore/account/preferences/memory/AccountMemoryPreferences.kt b/app/core/account-preferences-memory/src/commonMain/kotlin/io/github/taetae98coding/diary/fore/account/preferences/memory/AccountMemoryPreferences.kt deleted file mode 100644 index 7d768667..00000000 --- a/app/core/account-preferences-memory/src/commonMain/kotlin/io/github/taetae98coding/diary/fore/account/preferences/memory/AccountMemoryPreferences.kt +++ /dev/null @@ -1,36 +0,0 @@ -package io.github.taetae98coding.diary.fore.account.preferences.memory - -import io.github.taetae98coding.diary.core.account.preferences.AccountPreferences -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow - -internal data object AccountMemoryPreferences : AccountPreferences { - private val emailFlow = MutableStateFlow(null) - private val uidFlow = MutableStateFlow(null) - private val tokenFlow = MutableStateFlow(null) - - override suspend fun save(email: String, uid: String, token: String) { - emailFlow.emit(email) - uidFlow.emit(uid) - tokenFlow.emit(token) - } - - override suspend fun clear() { - emailFlow.emit(null) - uidFlow.emit(null) - tokenFlow.emit(null) - } - - override fun getEmail(): Flow { - return emailFlow.asStateFlow() - } - - override fun getUid(): Flow { - return uidFlow.asStateFlow() - } - - override fun getToken(): Flow { - return tokenFlow.asStateFlow() - } -} diff --git a/app/core/account-preferences-memory/src/commonMain/kotlin/io/github/taetae98coding/diary/fore/account/preferences/memory/AccountPreferencesMemoryModule.kt b/app/core/account-preferences-memory/src/commonMain/kotlin/io/github/taetae98coding/diary/fore/account/preferences/memory/AccountPreferencesMemoryModule.kt deleted file mode 100644 index acd85650..00000000 --- a/app/core/account-preferences-memory/src/commonMain/kotlin/io/github/taetae98coding/diary/fore/account/preferences/memory/AccountPreferencesMemoryModule.kt +++ /dev/null @@ -1,15 +0,0 @@ -package io.github.taetae98coding.diary.fore.account.preferences.memory - -import io.github.taetae98coding.diary.core.account.preferences.AccountPreferences -import org.koin.core.annotation.ComponentScan -import org.koin.core.annotation.Module -import org.koin.core.annotation.Singleton - -@Module -@ComponentScan -public class AccountPreferencesMemoryModule { - @Singleton - internal fun providesAccountPreferences(): AccountPreferences { - return AccountMemoryPreferences - } -} diff --git a/app/core/diary-database-memory/README.md b/app/core/backup-database-room/README.md similarity index 54% rename from app/core/diary-database-memory/README.md rename to app/core/backup-database-room/README.md index bc6d136a..73242012 100644 --- a/app/core/diary-database-memory/README.md +++ b/app/core/backup-database-room/README.md @@ -1,3 +1,3 @@ -# :app:core:diary-database-memory module +# :app:core:backup-database-room module ## Dependency graph -![Dependency graph](../../../docs/images/graphs/dep_graph_app_core_diary_database_memory.svg) +![Dependency graph](../../../docs/images/graphs/dep_graph_app_core_backup_database_room.svg) diff --git a/app/core/backup-database-room/build.gradle.kts b/app/core/backup-database-room/build.gradle.kts new file mode 100644 index 00000000..0d6a85b0 --- /dev/null +++ b/app/core/backup-database-room/build.gradle.kts @@ -0,0 +1,20 @@ +plugins { + id("diary.room") + id("diary.koin.room") +} + +kotlin { + sourceSets { + commonMain { + dependencies { + implementation(project(":app:core:backup-database")) + implementation(project(":library:coroutines")) + implementation(project(":library:room")) + } + } + } +} + +android { + namespace = "${Build.NAMESPACE}.core.backup.database.room" +} diff --git a/app/core/backup-database-room/schemas/io.github.taetae98coding.diary.core.backup.database.room.BackupDatabase/1.json b/app/core/backup-database-room/schemas/io.github.taetae98coding.diary.core.backup.database.room.BackupDatabase/1.json new file mode 100644 index 00000000..742351d4 --- /dev/null +++ b/app/core/backup-database-room/schemas/io.github.taetae98coding.diary.core.backup.database.room.BackupDatabase/1.json @@ -0,0 +1,61 @@ +{ + "formatVersion": 1, + "database": { + "version": 1, + "identityHash": "5f647fc9749a8d467a0a3c1fe9ef80ff", + "entities": [ + { + "tableName": "MemoBackupEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`memoId` TEXT NOT NULL, `uid` TEXT NOT NULL, PRIMARY KEY(`memoId`))", + "fields": [ + { + "fieldPath": "memoId", + "columnName": "memoId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "memoId" + ] + } + }, + { + "tableName": "TagBackupEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`tagId` TEXT NOT NULL, `uid` TEXT NOT NULL, PRIMARY KEY(`tagId`))", + "fields": [ + { + "fieldPath": "tagId", + "columnName": "tagId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "tagId" + ] + } + } + ], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '5f647fc9749a8d467a0a3c1fe9ef80ff')" + ] + } +} \ No newline at end of file diff --git a/app/core/backup-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/room/BackupDatabase.kt b/app/core/backup-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/room/BackupDatabase.kt new file mode 100644 index 00000000..86e407d6 --- /dev/null +++ b/app/core/backup-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/room/BackupDatabase.kt @@ -0,0 +1,29 @@ +package io.github.taetae98coding.diary.core.backup.database.room + +import androidx.room.ConstructedBy +import androidx.room.Database +import androidx.room.RoomDatabase +import androidx.room.TypeConverters +import io.github.taetae98coding.diary.core.backup.database.room.dao.MemoBackupEntityDao +import io.github.taetae98coding.diary.core.backup.database.room.dao.TagBackupEntityDao +import io.github.taetae98coding.diary.core.backup.database.room.entity.MemoBackupEntity +import io.github.taetae98coding.diary.core.backup.database.room.entity.TagBackupEntity +import io.github.taetae98coding.diary.core.backup.database.room.internal.BackupDatabaseConstructor +import io.github.taetae98coding.diary.library.room.InstantConverter + + +@Database( + entities = [ + MemoBackupEntity::class, + TagBackupEntity::class, + ], + version = 1, +) +@ConstructedBy(BackupDatabaseConstructor::class) +@TypeConverters( + InstantConverter::class, +) +internal abstract class BackupDatabase : RoomDatabase() { + abstract fun memo(): MemoBackupEntityDao + abstract fun tag(): TagBackupEntityDao +} diff --git a/app/core/backup-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/room/BackupRoomDatabaseModule.kt b/app/core/backup-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/room/BackupRoomDatabaseModule.kt new file mode 100644 index 00000000..248712b9 --- /dev/null +++ b/app/core/backup-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/room/BackupRoomDatabaseModule.kt @@ -0,0 +1,17 @@ +package io.github.taetae98coding.diary.core.backup.database.room + +import io.github.taetae98coding.diary.library.koin.room.getDatabaseBuilder +import org.koin.core.annotation.ComponentScan +import org.koin.core.annotation.Module +import org.koin.core.annotation.Singleton +import org.koin.core.component.KoinComponent + +@Module +@ComponentScan +public class BackupRoomDatabaseModule : KoinComponent { + @Singleton + internal fun providesDiaryDatabase(): BackupDatabase { + return getDatabaseBuilder("backup.db") + .build() + } +} diff --git a/app/core/backup-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/room/dao/MemoBackupEntityDao.kt b/app/core/backup-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/room/dao/MemoBackupEntityDao.kt new file mode 100644 index 00000000..5b3cc72e --- /dev/null +++ b/app/core/backup-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/room/dao/MemoBackupEntityDao.kt @@ -0,0 +1,16 @@ +package io.github.taetae98coding.diary.core.backup.database.room.dao + +import androidx.room.Dao +import androidx.room.Query +import io.github.taetae98coding.diary.core.backup.database.room.entity.MemoBackupEntity +import io.github.taetae98coding.diary.library.room.dao.EntityDao +import kotlinx.coroutines.flow.Flow + +@Dao +internal abstract class MemoBackupEntityDao : EntityDao() { + @Query("DELETE FROM MemoBackupEntity WHERE memoId IN (:memoIds)") + abstract suspend fun delete(memoIds: Set) + + @Query("SELECT memoId FROM MemoBackupEntity WHERE uid = :uid") + abstract fun findMemoIdByUid(uid: String): Flow> +} diff --git a/app/core/backup-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/room/dao/MemoBackupRoomDao.kt b/app/core/backup-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/room/dao/MemoBackupRoomDao.kt new file mode 100644 index 00000000..ecbd0811 --- /dev/null +++ b/app/core/backup-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/room/dao/MemoBackupRoomDao.kt @@ -0,0 +1,24 @@ +package io.github.taetae98coding.diary.core.backup.database.room.dao + +import io.github.taetae98coding.diary.core.backup.database.MemoBackupDao +import io.github.taetae98coding.diary.core.backup.database.room.BackupDatabase +import io.github.taetae98coding.diary.core.backup.database.room.entity.MemoBackupEntity +import kotlinx.coroutines.flow.Flow +import org.koin.core.annotation.Factory + +@Factory +internal class MemoBackupRoomDao( + private val database: BackupDatabase, +) : MemoBackupDao { + override suspend fun upsert(uid: String, id: String) { + database.memo().upsert(MemoBackupEntity(memoId = id, uid = uid)) + } + + override suspend fun delete(ids: Set) { + database.memo().delete(ids) + } + + override fun findMemoIdByUid(uid: String): Flow> { + return database.memo().findMemoIdByUid(uid) + } +} diff --git a/app/core/backup-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/room/dao/TagBackupEntityDao.kt b/app/core/backup-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/room/dao/TagBackupEntityDao.kt new file mode 100644 index 00000000..89ae56fc --- /dev/null +++ b/app/core/backup-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/room/dao/TagBackupEntityDao.kt @@ -0,0 +1,19 @@ +package io.github.taetae98coding.diary.core.backup.database.room.dao + +import androidx.room.Dao +import androidx.room.Query +import io.github.taetae98coding.diary.core.backup.database.room.entity.TagBackupEntity +import io.github.taetae98coding.diary.library.room.dao.EntityDao +import kotlinx.coroutines.flow.Flow + +@Dao +internal abstract class TagBackupEntityDao : EntityDao() { + @Query("DELETE FROM TagBackupEntity WHERE tagId IN (:tagIds)") + abstract suspend fun delete(tagIds: Set) + + @Query("SELECT COUNT(DISTINCT uid) FROM TagBackupEntity WHERE uid = :uid") + abstract fun countByUid(uid: String): Flow + + @Query("SELECT DISTINCT tagId FROM TagBackupEntity WHERE uid = :uid LIMIT 50") + abstract fun findByUid(uid: String): Flow> +} diff --git a/app/core/backup-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/room/dao/TagBackupRoomDao.kt b/app/core/backup-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/room/dao/TagBackupRoomDao.kt new file mode 100644 index 00000000..2e181a12 --- /dev/null +++ b/app/core/backup-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/room/dao/TagBackupRoomDao.kt @@ -0,0 +1,24 @@ +package io.github.taetae98coding.diary.core.backup.database.room.dao + +import io.github.taetae98coding.diary.core.backup.database.TagBackupDao +import io.github.taetae98coding.diary.core.backup.database.room.BackupDatabase +import io.github.taetae98coding.diary.core.backup.database.room.entity.TagBackupEntity +import kotlinx.coroutines.flow.Flow +import org.koin.core.annotation.Factory + +@Factory +internal class TagBackupRoomDao( + private val database: BackupDatabase, +) : TagBackupDao { + override suspend fun upsert(uid: String, id: String) { + database.tag().upsert(TagBackupEntity(tagId = id, uid = uid)) + } + + override suspend fun delete(ids: Set) { + database.tag().delete(ids) + } + + override fun findByUid(uid: String): Flow> { + return database.tag().findByUid(uid) + } +} diff --git a/app/core/backup-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/room/entity/MemoBackupEntity.kt b/app/core/backup-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/room/entity/MemoBackupEntity.kt new file mode 100644 index 00000000..6506a7d9 --- /dev/null +++ b/app/core/backup-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/room/entity/MemoBackupEntity.kt @@ -0,0 +1,11 @@ +package io.github.taetae98coding.diary.core.backup.database.room.entity + +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity +internal data class MemoBackupEntity( + @PrimaryKey + val memoId: String, + val uid: String, +) diff --git a/app/core/backup-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/room/entity/TagBackupEntity.kt b/app/core/backup-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/room/entity/TagBackupEntity.kt new file mode 100644 index 00000000..2bf76e1a --- /dev/null +++ b/app/core/backup-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/room/entity/TagBackupEntity.kt @@ -0,0 +1,11 @@ +package io.github.taetae98coding.diary.core.backup.database.room.entity + +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity +internal data class TagBackupEntity( + @PrimaryKey + val tagId: String, + val uid: String, +) diff --git a/app/core/backup-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/room/internal/BackupDatabaseConstructor.kt b/app/core/backup-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/room/internal/BackupDatabaseConstructor.kt new file mode 100644 index 00000000..2c884f9b --- /dev/null +++ b/app/core/backup-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/room/internal/BackupDatabaseConstructor.kt @@ -0,0 +1,6 @@ +package io.github.taetae98coding.diary.core.backup.database.room.internal + +import androidx.room.RoomDatabaseConstructor +import io.github.taetae98coding.diary.core.backup.database.room.BackupDatabase + +internal expect object BackupDatabaseConstructor : RoomDatabaseConstructor diff --git a/app/core/holiday-database-memory/README.md b/app/core/backup-database/README.md similarity index 52% rename from app/core/holiday-database-memory/README.md rename to app/core/backup-database/README.md index f03cf94f..b7696702 100644 --- a/app/core/holiday-database-memory/README.md +++ b/app/core/backup-database/README.md @@ -1,3 +1,3 @@ -# :app:core:holiday-database-memory module +# :app:core:backup-database module ## Dependency graph -![Dependency graph](../../../docs/images/graphs/dep_graph_app_core_holiday_database_memory.svg) +![Dependency graph](../../../docs/images/graphs/dep_graph_app_core_backup_database.svg) diff --git a/app/core/backup-database/build.gradle.kts b/app/core/backup-database/build.gradle.kts new file mode 100644 index 00000000..421e85b5 --- /dev/null +++ b/app/core/backup-database/build.gradle.kts @@ -0,0 +1,14 @@ +plugins { + id("diary.kotlin.multiplatform.common") +} + +kotlin { + sourceSets { + commonMain { + dependencies { + api(project(":app:core:model")) + api(libs.kotlinx.coroutines.core) + } + } + } +} diff --git a/app/core/backup-database/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/MemoBackupDao.kt b/app/core/backup-database/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/MemoBackupDao.kt new file mode 100644 index 00000000..f98edc98 --- /dev/null +++ b/app/core/backup-database/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/MemoBackupDao.kt @@ -0,0 +1,10 @@ +package io.github.taetae98coding.diary.core.backup.database + +import kotlinx.coroutines.flow.Flow + +public interface MemoBackupDao { + public suspend fun upsert(uid: String, id: String) + public suspend fun delete(ids: Set) + + public fun findMemoIdByUid(uid: String): Flow> +} diff --git a/app/core/backup-database/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/TagBackupDao.kt b/app/core/backup-database/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/TagBackupDao.kt new file mode 100644 index 00000000..14e86967 --- /dev/null +++ b/app/core/backup-database/src/commonMain/kotlin/io/github/taetae98coding/diary/core/backup/database/TagBackupDao.kt @@ -0,0 +1,10 @@ +package io.github.taetae98coding.diary.core.backup.database + +import kotlinx.coroutines.flow.Flow + +public interface TagBackupDao { + public suspend fun upsert(uid: String, id: String) + public suspend fun delete(ids: Set) + + public fun findByUid(uid: String): Flow> +} diff --git a/app/core/calendar-compose/build.gradle.kts b/app/core/calendar-compose/build.gradle.kts index 234b2eff..bace8853 100644 --- a/app/core/calendar-compose/build.gradle.kts +++ b/app/core/calendar-compose/build.gradle.kts @@ -11,7 +11,6 @@ kotlin { sourceSets { commonMain { dependencies { - implementation(project(":app:core:resources")) implementation(project(":app:core:design-system")) implementation(project(":library:color")) implementation(project(":library:datetime")) diff --git a/app/core/calendar-compose/src/commonMain/kotlin/io/github/taetae98coding/diary/core/calendar/compose/DayOfWeekRow.kt b/app/core/calendar-compose/src/commonMain/kotlin/io/github/taetae98coding/diary/core/calendar/compose/DayOfWeekRow.kt index 0d1c1846..1a3806d9 100644 --- a/app/core/calendar-compose/src/commonMain/kotlin/io/github/taetae98coding/diary/core/calendar/compose/DayOfWeekRow.kt +++ b/app/core/calendar-compose/src/commonMain/kotlin/io/github/taetae98coding/diary/core/calendar/compose/DayOfWeekRow.kt @@ -9,15 +9,6 @@ import androidx.compose.ui.graphics.takeOrElse import androidx.compose.ui.text.style.TextAlign import io.github.taetae98coding.diary.core.calendar.compose.color.CalendarColors import io.github.taetae98coding.diary.core.design.system.theme.DiaryTheme -import io.github.taetae98coding.diary.core.resources.Res -import io.github.taetae98coding.diary.core.resources.fri -import io.github.taetae98coding.diary.core.resources.mon -import io.github.taetae98coding.diary.core.resources.sat -import io.github.taetae98coding.diary.core.resources.sun -import io.github.taetae98coding.diary.core.resources.thu -import io.github.taetae98coding.diary.core.resources.tue -import io.github.taetae98coding.diary.core.resources.wed -import org.jetbrains.compose.resources.stringResource @Composable internal fun DayOfWeekRow( @@ -28,49 +19,49 @@ internal fun DayOfWeekRow( val dayOfWeekModifier = Modifier.weight(1F) Text( - text = stringResource(Res.string.sun), + text = "일", modifier = dayOfWeekModifier, color = colors.sundayColor, textAlign = TextAlign.Center, style = DiaryTheme.typography.bodySmall, ) Text( - text = stringResource(Res.string.mon), + text = "월", modifier = dayOfWeekModifier, color = colors.dayColor.takeOrElse { LocalContentColor.current }, textAlign = TextAlign.Center, style = DiaryTheme.typography.bodySmall, ) Text( - text = stringResource(Res.string.tue), + text = "화", modifier = dayOfWeekModifier, color = colors.dayColor.takeOrElse { LocalContentColor.current }, textAlign = TextAlign.Center, style = DiaryTheme.typography.bodySmall, ) Text( - text = stringResource(Res.string.wed), + text = "수", modifier = dayOfWeekModifier, color = colors.dayColor.takeOrElse { LocalContentColor.current }, textAlign = TextAlign.Center, style = DiaryTheme.typography.bodySmall, ) Text( - text = stringResource(Res.string.thu), + text = "목", modifier = dayOfWeekModifier, color = colors.dayColor.takeOrElse { LocalContentColor.current }, textAlign = TextAlign.Center, style = DiaryTheme.typography.bodySmall, ) Text( - text = stringResource(Res.string.fri), + text = "금", modifier = dayOfWeekModifier, color = colors.dayColor.takeOrElse { LocalContentColor.current }, textAlign = TextAlign.Center, style = DiaryTheme.typography.bodySmall, ) Text( - text = stringResource(Res.string.sat), + text = "토", modifier = dayOfWeekModifier, color = colors.saturdayColor, textAlign = TextAlign.Center, diff --git a/app/core/calendar-compose/src/commonMain/kotlin/io/github/taetae98coding/diary/core/calendar/compose/topbar/CalendarTopBar.kt b/app/core/calendar-compose/src/commonMain/kotlin/io/github/taetae98coding/diary/core/calendar/compose/topbar/CalendarTopBar.kt index cbfe80e6..7ef4e53c 100644 --- a/app/core/calendar-compose/src/commonMain/kotlin/io/github/taetae98coding/diary/core/calendar/compose/topbar/CalendarTopBar.kt +++ b/app/core/calendar-compose/src/commonMain/kotlin/io/github/taetae98coding/diary/core/calendar/compose/topbar/CalendarTopBar.kt @@ -28,17 +28,14 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp -import androidx.lifecycle.compose.LifecycleStartEffect +import androidx.lifecycle.compose.LifecycleResumeEffect import io.github.taetae98coding.diary.core.calendar.compose.state.CalendarState import io.github.taetae98coding.diary.core.design.system.date.DiaryDatePickerDialog -import io.github.taetae98coding.diary.core.resources.Res -import io.github.taetae98coding.diary.core.resources.icon.DropDownIcon -import io.github.taetae98coding.diary.core.resources.icon.DropUpIcon -import io.github.taetae98coding.diary.core.resources.year_and_month +import io.github.taetae98coding.diary.core.design.system.icon.DropDownIcon +import io.github.taetae98coding.diary.core.design.system.icon.DropUpIcon import io.github.taetae98coding.diary.library.datetime.todayIn import kotlinx.coroutines.launch import kotlinx.datetime.LocalDate -import org.jetbrains.compose.resources.stringResource @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -59,7 +56,7 @@ public fun CalendarTopBar( horizontalArrangement = Arrangement.spacedBy(4.dp), verticalAlignment = Alignment.CenterVertically, ) { - Text(text = stringResource(Res.string.year_and_month, state.localDate.year, state.localDate.monthNumber)) + Text(text = "${state.localDate.year}년 ${state.localDate.monthNumber}월") DropIcon(dropUpProvider = { isDialogVisible }) } @@ -80,9 +77,9 @@ public fun CalendarTopBar( TodayIcon(text = today.dayOfMonth.toString()) } - LifecycleStartEffect(Unit) { + LifecycleResumeEffect(Unit) { today = LocalDate.todayIn() - onStopOrDispose { } + onPauseOrDispose { } } }, ) diff --git a/app/core/resources/README.md b/app/core/compose/README.md similarity index 64% rename from app/core/resources/README.md rename to app/core/compose/README.md index a1ad0283..8fcc077b 100644 --- a/app/core/resources/README.md +++ b/app/core/compose/README.md @@ -1,3 +1,3 @@ -# :app:core:resources module +# :app:core:compose module ## Dependency graph -![Dependency graph](../../../docs/images/graphs/dep_graph_app_core_resources.svg) +![Dependency graph](../../../docs/images/graphs/dep_graph_app_core_compose.svg) diff --git a/app/core/compose/build.gradle.kts b/app/core/compose/build.gradle.kts new file mode 100644 index 00000000..6c145cfc --- /dev/null +++ b/app/core/compose/build.gradle.kts @@ -0,0 +1,28 @@ +plugins { + id("diary.android.library") + id("diary.kotlin.multiplatform.all") + id("diary.compose") +} + +kotlin { + sourceSets { + commonMain { + dependencies { + implementation(project(":app:core:design-system")) + + implementation(compose.material3) + implementation(libs.compose.material3.adaptive.navigation) + } + } + + androidMain { + dependencies { + implementation(libs.androidx.activity.compose) + } + } + } +} + +android { + namespace = "${Build.NAMESPACE}.core.compose" +} diff --git a/app/core/compose/src/androidMain/kotlin/io/github/taetae98coding/diary/core/compose/back/KBackHandler.android.kt b/app/core/compose/src/androidMain/kotlin/io/github/taetae98coding/diary/core/compose/back/KBackHandler.android.kt new file mode 100644 index 00000000..b013dd02 --- /dev/null +++ b/app/core/compose/src/androidMain/kotlin/io/github/taetae98coding/diary/core/compose/back/KBackHandler.android.kt @@ -0,0 +1,15 @@ +package io.github.taetae98coding.diary.core.compose.back + +import androidx.activity.compose.BackHandler +import androidx.compose.runtime.Composable + +@Composable +public actual fun KBackHandler( + isEnabled: Boolean, + onBack: () -> Unit, +) { + BackHandler( + enabled = isEnabled, + onBack = onBack, + ) +} diff --git a/app/core/compose/src/commonMain/kotlin/io/github/taetae98coding/diary/core/compose/adaptive/ThreePaneScaffoldNavigatorExt.kt b/app/core/compose/src/commonMain/kotlin/io/github/taetae98coding/diary/core/compose/adaptive/ThreePaneScaffoldNavigatorExt.kt new file mode 100644 index 00000000..0a5a5c4f --- /dev/null +++ b/app/core/compose/src/commonMain/kotlin/io/github/taetae98coding/diary/core/compose/adaptive/ThreePaneScaffoldNavigatorExt.kt @@ -0,0 +1,36 @@ +package io.github.taetae98coding.diary.core.compose.adaptive + +import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi +import androidx.compose.material3.adaptive.layout.PaneAdaptedValue +import androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue +import androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator + +@OptIn(ExperimentalMaterial3AdaptiveApi::class) +public fun ThreePaneScaffoldNavigator<*>.isListVisible(): Boolean { + return scaffoldValue.isListVisible() +} + +@OptIn(ExperimentalMaterial3AdaptiveApi::class) +public fun ThreePaneScaffoldNavigator<*>.isDetailVisible(): Boolean { + return scaffoldValue.isDetailVisible() +} + +@OptIn(ExperimentalMaterial3AdaptiveApi::class) +public fun ThreePaneScaffoldNavigator<*>.isTertiaryVisible(): Boolean { + return scaffoldValue.isTertiaryVisible() +} + +@OptIn(ExperimentalMaterial3AdaptiveApi::class) +public fun ThreePaneScaffoldValue.isListVisible(): Boolean { + return secondary == PaneAdaptedValue.Expanded +} + +@OptIn(ExperimentalMaterial3AdaptiveApi::class) +public fun ThreePaneScaffoldValue.isDetailVisible(): Boolean { + return primary == PaneAdaptedValue.Expanded +} + +@OptIn(ExperimentalMaterial3AdaptiveApi::class) +public fun ThreePaneScaffoldValue.isTertiaryVisible(): Boolean { + return tertiary == PaneAdaptedValue.Expanded +} diff --git a/app/core/compose/src/commonMain/kotlin/io/github/taetae98coding/diary/core/compose/back/KBackHandler.kt b/app/core/compose/src/commonMain/kotlin/io/github/taetae98coding/diary/core/compose/back/KBackHandler.kt new file mode 100644 index 00000000..8de0fe7e --- /dev/null +++ b/app/core/compose/src/commonMain/kotlin/io/github/taetae98coding/diary/core/compose/back/KBackHandler.kt @@ -0,0 +1,9 @@ +package io.github.taetae98coding.diary.core.compose.back + +import androidx.compose.runtime.Composable + +@Composable +public expect fun KBackHandler( + isEnabled: Boolean = true, + onBack: () -> Unit +) diff --git a/app/core/compose/src/commonMain/kotlin/io/github/taetae98coding/diary/core/compose/button/FloatingAddButton.kt b/app/core/compose/src/commonMain/kotlin/io/github/taetae98coding/diary/core/compose/button/FloatingAddButton.kt new file mode 100644 index 00000000..084e8997 --- /dev/null +++ b/app/core/compose/src/commonMain/kotlin/io/github/taetae98coding/diary/core/compose/button/FloatingAddButton.kt @@ -0,0 +1,30 @@ +package io.github.taetae98coding.diary.core.compose.button + +import androidx.compose.animation.Crossfade +import androidx.compose.foundation.layout.size +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.FloatingActionButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import io.github.taetae98coding.diary.core.design.system.icon.AddIcon + +@Composable +public fun FloatingAddButton( + onClick: () -> Unit, + modifier: Modifier = Modifier, + progressProvider: () -> Boolean = { false }, +) { + FloatingActionButton( + onClick = onClick, + modifier = modifier, + ) { + Crossfade(progressProvider()) { + if (it) { + CircularProgressIndicator(modifier = Modifier.size(24.dp)) + } else { + AddIcon() + } + } + } +} diff --git a/app/core/compose/src/commonMain/kotlin/io/github/taetae98coding/diary/core/compose/runtime/SkipProperty.kt b/app/core/compose/src/commonMain/kotlin/io/github/taetae98coding/diary/core/compose/runtime/SkipProperty.kt new file mode 100644 index 00000000..d502563e --- /dev/null +++ b/app/core/compose/src/commonMain/kotlin/io/github/taetae98coding/diary/core/compose/runtime/SkipProperty.kt @@ -0,0 +1,16 @@ +package io.github.taetae98coding.diary.core.compose.runtime + +import androidx.compose.runtime.Immutable + +@Immutable +public class SkipProperty( + public val value: T +) { + override fun equals(other: Any?): Boolean { + return true + } + + override fun hashCode(): Int { + return 0 + } +} diff --git a/app/core/compose/src/commonMain/kotlin/io/github/taetae98coding/diary/core/compose/swipe/FinishAndDeleteSwipeBox.kt b/app/core/compose/src/commonMain/kotlin/io/github/taetae98coding/diary/core/compose/swipe/FinishAndDeleteSwipeBox.kt new file mode 100644 index 00000000..9cbb8abe --- /dev/null +++ b/app/core/compose/src/commonMain/kotlin/io/github/taetae98coding/diary/core/compose/swipe/FinishAndDeleteSwipeBox.kt @@ -0,0 +1,91 @@ +package io.github.taetae98coding.diary.core.compose.swipe + +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.SwipeToDismissBox +import androidx.compose.material3.SwipeToDismissBoxValue +import androidx.compose.material3.rememberSwipeToDismissBoxState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.unit.dp +import io.github.taetae98coding.diary.core.design.system.icon.CircleIcon +import io.github.taetae98coding.diary.core.design.system.icon.DeleteIcon +import io.github.taetae98coding.diary.core.design.system.icon.FinishIcon +import io.github.taetae98coding.diary.core.design.system.theme.DiaryTheme + +@Composable +public fun FinishAndDeleteSwipeBox( + modifier: Modifier = Modifier, + onFinish: () -> Unit, + onDelete: () -> Unit, + content: @Composable () -> Unit, +) { + val state = rememberSwipeToDismissBoxState( + confirmValueChange = { + when(it) { + SwipeToDismissBoxValue.StartToEnd -> onFinish() + SwipeToDismissBoxValue.EndToStart -> onDelete() + else -> Unit + } + + true + }, + ) + + SwipeToDismissBox( + state = state, + backgroundContent = { + Box( + modifier = Modifier + .fillMaxSize(), + ) { + val scale by animateFloatAsState( + targetValue = if (state.targetValue == SwipeToDismissBoxValue.Settled) { + 0.5F + } else { + 1.4F + }, + ) + + Box( + modifier = Modifier.align(Alignment.CenterStart) + .padding(horizontal = 4.dp) + .graphicsLayer { + scaleX = scale + scaleY = scale + }, + ) { + if (state.targetValue == SwipeToDismissBoxValue.Settled) { + CircleIcon() + } else if (state.targetValue == SwipeToDismissBoxValue.StartToEnd) { + FinishIcon(tint = DiaryTheme.color.primary) + } + } + + Box( + modifier = Modifier.align(Alignment.CenterEnd) + .padding(horizontal = 4.dp) + .graphicsLayer { + scaleX = scale + scaleY = scale + }, + ) { + if (state.targetValue == SwipeToDismissBoxValue.Settled) { + CircleIcon() + } else if (state.targetValue == SwipeToDismissBoxValue.EndToStart) { + DeleteIcon(tint = DiaryTheme.color.secondary) + } + } + } + }, + modifier = modifier, + content = { + content() + }, + ) +} diff --git a/app/core/compose/src/iosMain/kotlin/io/github/taetae98coding/diary/core/compose/back/KBackHandler.ios.kt b/app/core/compose/src/iosMain/kotlin/io/github/taetae98coding/diary/core/compose/back/KBackHandler.ios.kt new file mode 100644 index 00000000..61078799 --- /dev/null +++ b/app/core/compose/src/iosMain/kotlin/io/github/taetae98coding/diary/core/compose/back/KBackHandler.ios.kt @@ -0,0 +1,6 @@ +package io.github.taetae98coding.diary.core.compose.back + +import androidx.compose.runtime.Composable + +@Composable +public actual fun KBackHandler(isEnabled: Boolean, onBack: () -> Unit): Unit = Unit diff --git a/app/core/compose/src/jvmMain/kotlin/io/github/taetae98coding/diary/core/compose/back/KBackHandler.jvm.kt b/app/core/compose/src/jvmMain/kotlin/io/github/taetae98coding/diary/core/compose/back/KBackHandler.jvm.kt new file mode 100644 index 00000000..61078799 --- /dev/null +++ b/app/core/compose/src/jvmMain/kotlin/io/github/taetae98coding/diary/core/compose/back/KBackHandler.jvm.kt @@ -0,0 +1,6 @@ +package io.github.taetae98coding.diary.core.compose.back + +import androidx.compose.runtime.Composable + +@Composable +public actual fun KBackHandler(isEnabled: Boolean, onBack: () -> Unit): Unit = Unit diff --git a/app/core/compose/src/wasmJsMain/kotlin/io/github/taetae98coding/diary/core/compose/back/KBackHandler.wasmJs.kt b/app/core/compose/src/wasmJsMain/kotlin/io/github/taetae98coding/diary/core/compose/back/KBackHandler.wasmJs.kt new file mode 100644 index 00000000..61078799 --- /dev/null +++ b/app/core/compose/src/wasmJsMain/kotlin/io/github/taetae98coding/diary/core/compose/back/KBackHandler.wasmJs.kt @@ -0,0 +1,6 @@ +package io.github.taetae98coding.diary.core.compose.back + +import androidx.compose.runtime.Composable + +@Composable +public actual fun KBackHandler(isEnabled: Boolean, onBack: () -> Unit): Unit = Unit diff --git a/app/core/coroutines/build.gradle.kts b/app/core/coroutines/build.gradle.kts index 41e5a40a..2154a7f0 100644 --- a/app/core/coroutines/build.gradle.kts +++ b/app/core/coroutines/build.gradle.kts @@ -27,7 +27,6 @@ kotlin { nonAndroidMain.dependsOn(commonMain.get()) jvmMain.get().dependsOn(nonAndroidMain) - wasmJsMain.get().dependsOn(nonAndroidMain) iosMain.get().dependsOn(nonAndroidMain) } } diff --git a/app/core/design-system/build.gradle.kts b/app/core/design-system/build.gradle.kts index 62984101..4d612de7 100644 --- a/app/core/design-system/build.gradle.kts +++ b/app/core/design-system/build.gradle.kts @@ -17,11 +17,11 @@ kotlin { sourceSets { commonMain { dependencies { - implementation(project(":app:core:resources")) implementation(project(":library:color")) implementation(project(":library:datetime")) implementation(compose.material3) + implementation(compose.materialIconsExtended) implementation(libs.compose.markdown) } } @@ -43,7 +43,6 @@ kotlin { nonAndroidMain.dependsOn(commonMain.get()) jvmMain.get().dependsOn(nonAndroidMain) - wasmJsMain.get().dependsOn(nonAndroidMain) iosMain.get().dependsOn(nonAndroidMain) } } diff --git a/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/chip/DiaryFilterChip.kt b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/chip/DiaryFilterChip.kt new file mode 100644 index 00000000..2c1f179c --- /dev/null +++ b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/chip/DiaryFilterChip.kt @@ -0,0 +1,49 @@ +package io.github.taetae98coding.diary.core.design.system.chip + +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.material3.FilterChip +import androidx.compose.material3.FilterChipDefaults +import androidx.compose.material3.LocalMinimumInteractiveComponentSize +import androidx.compose.material3.SelectableChipColors +import androidx.compose.material3.SelectableChipElevation +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.unit.Dp + +@Composable +public fun DiaryFilterChip( + selected: Boolean, + onClick: () -> Unit, + label: @Composable () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + leadingIcon: @Composable (() -> Unit)? = null, + trailingIcon: @Composable (() -> Unit)? = null, + shape: Shape = FilterChipDefaults.shape, + colors: SelectableChipColors = FilterChipDefaults.filterChipColors(), + elevation: SelectableChipElevation? = FilterChipDefaults.filterChipElevation(), + border: BorderStroke? = FilterChipDefaults.filterChipBorder(enabled, selected), + interactionSource: MutableInteractionSource? = null, +) { + CompositionLocalProvider( + LocalMinimumInteractiveComponentSize provides Dp.Unspecified, + ) { + FilterChip( + selected = selected, + onClick = onClick, + label = label, + modifier = modifier, + enabled = enabled, + leadingIcon = leadingIcon, + trailingIcon = trailingIcon, + shape = shape, + colors = colors, + elevation = elevation, + border = border, + interactionSource = interactionSource, + ) + } +} diff --git a/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/color/DiaryColor.kt b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/color/DiaryColor.kt index 6442e027..e3965648 100644 --- a/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/color/DiaryColor.kt +++ b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/color/DiaryColor.kt @@ -5,6 +5,7 @@ import androidx.compose.ui.graphics.Color public data class DiaryColor( val primary: Color, val onPrimary: Color, + val secondary: Color, val background: Color, val onSurface: Color, ) diff --git a/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/color/DiaryColorPicker.kt b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/color/DiaryColorPicker.kt index 1b76ea71..6ba46012 100644 --- a/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/color/DiaryColorPicker.kt +++ b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/color/DiaryColorPicker.kt @@ -21,8 +21,8 @@ import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.unit.dp +import io.github.taetae98coding.diary.core.design.system.icon.RefreshIcon import io.github.taetae98coding.diary.core.design.system.theme.DiaryTheme -import io.github.taetae98coding.diary.core.resources.icon.RefreshIcon import io.github.taetae98coding.diary.library.color.toContrastColor import io.github.taetae98coding.diary.library.color.toRgbString diff --git a/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/color/DiaryColorPickerDialog.kt b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/color/DiaryColorPickerDialog.kt index a896449a..6308f765 100644 --- a/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/color/DiaryColorPickerDialog.kt +++ b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/color/DiaryColorPickerDialog.kt @@ -16,10 +16,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp -import io.github.taetae98coding.diary.core.resources.Res -import io.github.taetae98coding.diary.core.resources.dismiss -import io.github.taetae98coding.diary.core.resources.select -import org.jetbrains.compose.resources.stringResource @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -48,7 +44,7 @@ public fun DiaryColorPickerDialog( horizontalArrangement = Arrangement.spacedBy(12.dp, Alignment.End), ) { TextButton(onClick = onDismissRequest) { - Text(text = stringResource(Res.string.dismiss)) + Text(text = "닫기") } TextButton( onClick = { @@ -56,7 +52,7 @@ public fun DiaryColorPickerDialog( onDismissRequest() }, ) { - Text(text = stringResource(Res.string.select)) + Text(text = "선택") } } } diff --git a/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/date/DiaryDatePickerDialog.kt b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/date/DiaryDatePickerDialog.kt index dd26d2a8..96e313f7 100644 --- a/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/date/DiaryDatePickerDialog.kt +++ b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/date/DiaryDatePickerDialog.kt @@ -11,13 +11,9 @@ import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Modifier -import io.github.taetae98coding.diary.core.resources.Res -import io.github.taetae98coding.diary.core.resources.dismiss -import io.github.taetae98coding.diary.core.resources.select import io.github.taetae98coding.diary.library.datetime.toLocalDate import io.github.taetae98coding.diary.library.datetime.toTimeInMillis import kotlinx.datetime.LocalDate -import org.jetbrains.compose.resources.stringResource @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -43,13 +39,13 @@ public fun DiaryDatePickerDialog( }, enabled = isConfirmButtonEnable, ) { - Text(text = stringResource(Res.string.select)) + Text(text = "선택") } }, modifier = modifier, dismissButton = { TextButton(onClick = onDismissRequest) { - Text(text = stringResource(Res.string.dismiss)) + Text(text = "닫기") } }, ) { diff --git a/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/diary/component/DiaryComponent.kt b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/diary/component/DiaryComponent.kt index 6684fee6..2eeccca4 100644 --- a/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/diary/component/DiaryComponent.kt +++ b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/diary/component/DiaryComponent.kt @@ -18,15 +18,11 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.unit.dp import com.mikepenz.markdown.m3.Markdown +import io.github.taetae98coding.diary.core.design.system.icon.MarkdownIcon +import io.github.taetae98coding.diary.core.design.system.icon.TextFieldIcon import io.github.taetae98coding.diary.core.design.system.text.ClearTextField import io.github.taetae98coding.diary.core.design.system.theme.DiaryTheme -import io.github.taetae98coding.diary.core.resources.Res -import io.github.taetae98coding.diary.core.resources.description -import io.github.taetae98coding.diary.core.resources.icon.MarkdownIcon -import io.github.taetae98coding.diary.core.resources.icon.TextFieldIcon -import io.github.taetae98coding.diary.core.resources.title import kotlinx.coroutines.launch -import org.jetbrains.compose.resources.stringResource @Composable public fun DiaryComponent( @@ -39,7 +35,7 @@ public fun DiaryComponent( onValueChange = state::onTitleChange, modifier = Modifier.fillMaxWidth() .focusRequester(state.titleFocusRequester), - label = { Text(text = stringResource(Res.string.title)) }, + label = { Text(text = "제목") }, errorProvider = { state.isTitleError }, singleLine = true ) @@ -93,7 +89,7 @@ private fun DescriptionPager( valueProvider = { state.description }, onValueChange = state::onDescriptionChange, modifier = Modifier.fillMaxSize(), - label = { Text(text = stringResource(Res.string.description)) }, + label = { Text(text = "설명") }, ) } diff --git a/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/diary/date/DiaryDate.kt b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/diary/date/DiaryDate.kt index 588eabe7..27a1b045 100644 --- a/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/diary/date/DiaryDate.kt +++ b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/diary/date/DiaryDate.kt @@ -27,11 +27,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextAlign import io.github.taetae98coding.diary.core.design.system.date.DiaryDatePickerDialog import io.github.taetae98coding.diary.core.design.system.theme.DiaryTheme -import io.github.taetae98coding.diary.core.resources.Res -import io.github.taetae98coding.diary.core.resources.date -import io.github.taetae98coding.diary.core.resources.month_and_day import kotlinx.datetime.LocalDate -import org.jetbrains.compose.resources.stringResource @Composable public fun DiaryDate( @@ -65,7 +61,7 @@ private fun Title( horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, ) { - Text(text = stringResource(Res.string.date)) + Text(text = "날짜") Switch( checked = state.hasDate, onCheckedChange = null, @@ -127,7 +123,7 @@ private fun DateButton( style = DiaryTheme.typography.labelSmall, ) Text( - text = stringResource(Res.string.month_and_day, target.monthNumber, target.dayOfMonth), + text = "${target.monthNumber}월 ${target.dayOfMonth}일", modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.Center, style = DiaryTheme.typography.labelLarge, diff --git a/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/emoji/Emoji.kt b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/emoji/Emoji.kt index 48c1009c..fe67ea93 100644 --- a/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/emoji/Emoji.kt +++ b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/emoji/Emoji.kt @@ -1,6 +1,9 @@ package io.github.taetae98coding.diary.core.design.system.emoji public data object Emoji { - public val congratulate: List = listOf("🥳", "🎉", "🎊") + public val congratulate: List = listOf("🥳", "🎉", "🎊", "👍") public val error: List = listOf("😵‍💫", "🫠") + public val fail: List = listOf("🧐", "🤔") + public val check: List = listOf("😅", "🥺") + public val invalid: List = listOf("😢", "🥲") } diff --git a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/AccountIcon.kt b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/AccountIcon.kt similarity index 86% rename from app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/AccountIcon.kt rename to app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/AccountIcon.kt index f5a82dfa..fabd884d 100644 --- a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/AccountIcon.kt +++ b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/AccountIcon.kt @@ -1,4 +1,4 @@ -package io.github.taetae98coding.diary.core.resources.icon +package io.github.taetae98coding.diary.core.design.system.icon import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.Person diff --git a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/AddIcon.kt b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/AddIcon.kt similarity index 86% rename from app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/AddIcon.kt rename to app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/AddIcon.kt index 9e412762..ef064d09 100644 --- a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/AddIcon.kt +++ b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/AddIcon.kt @@ -1,4 +1,4 @@ -package io.github.taetae98coding.diary.core.resources.icon +package io.github.taetae98coding.diary.core.design.system.icon import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.Add diff --git a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/CalendarIcon.kt b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/CalendarIcon.kt similarity index 87% rename from app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/CalendarIcon.kt rename to app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/CalendarIcon.kt index 4dd2e641..a55d3039 100644 --- a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/CalendarIcon.kt +++ b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/CalendarIcon.kt @@ -1,4 +1,4 @@ -package io.github.taetae98coding.diary.core.resources.icon +package io.github.taetae98coding.diary.core.design.system.icon import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.CalendarToday diff --git a/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/ChevronRightIcon.kt b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/ChevronRightIcon.kt new file mode 100644 index 00000000..f4b93df8 --- /dev/null +++ b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/ChevronRightIcon.kt @@ -0,0 +1,18 @@ +package io.github.taetae98coding.diary.core.design.system.icon + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.ChevronRight +import androidx.compose.material3.Icon +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier + +@Composable +public fun ChevronRightIcon( + modifier: Modifier = Modifier, +) { + Icon( + imageVector = Icons.Rounded.ChevronRight, + contentDescription = null, + modifier = modifier, + ) +} diff --git a/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/CircleIcon.kt b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/CircleIcon.kt new file mode 100644 index 00000000..e0b9fb37 --- /dev/null +++ b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/CircleIcon.kt @@ -0,0 +1,18 @@ +package io.github.taetae98coding.diary.core.design.system.icon + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.Circle +import androidx.compose.material3.Icon +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier + +@Composable +public fun CircleIcon( + modifier: Modifier = Modifier, +) { + Icon( + imageVector = Icons.Rounded.Circle, + contentDescription = null, + modifier = modifier, + ) +} diff --git a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/ClearIcon.kt b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/ClearIcon.kt similarity index 86% rename from app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/ClearIcon.kt rename to app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/ClearIcon.kt index c89f3c18..ed9bb142 100644 --- a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/ClearIcon.kt +++ b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/ClearIcon.kt @@ -1,4 +1,4 @@ -package io.github.taetae98coding.diary.core.resources.icon +package io.github.taetae98coding.diary.core.design.system.icon import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.Clear diff --git a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/DeleteIcon.kt b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/DeleteIcon.kt similarity index 65% rename from app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/DeleteIcon.kt rename to app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/DeleteIcon.kt index 262936b4..c8e64a3e 100644 --- a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/DeleteIcon.kt +++ b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/DeleteIcon.kt @@ -1,18 +1,22 @@ -package io.github.taetae98coding.diary.core.resources.icon +package io.github.taetae98coding.diary.core.design.system.icon import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.Delete import androidx.compose.material3.Icon +import androidx.compose.material3.LocalContentColor import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color @Composable public fun DeleteIcon( modifier: Modifier = Modifier, + tint: Color = LocalContentColor.current, ) { Icon( modifier = modifier, imageVector = Icons.Rounded.Delete, contentDescription = null, + tint = tint, ) } diff --git a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/DropDownIcon.kt b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/DropDownIcon.kt similarity index 87% rename from app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/DropDownIcon.kt rename to app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/DropDownIcon.kt index 0cb225f2..61b905de 100644 --- a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/DropDownIcon.kt +++ b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/DropDownIcon.kt @@ -1,4 +1,4 @@ -package io.github.taetae98coding.diary.core.resources.icon +package io.github.taetae98coding.diary.core.design.system.icon import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.ArrowDropDown diff --git a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/DropUpIcon.kt b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/DropUpIcon.kt similarity index 87% rename from app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/DropUpIcon.kt rename to app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/DropUpIcon.kt index 002ab7eb..34473b3a 100644 --- a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/DropUpIcon.kt +++ b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/DropUpIcon.kt @@ -1,4 +1,4 @@ -package io.github.taetae98coding.diary.core.resources.icon +package io.github.taetae98coding.diary.core.design.system.icon import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.ArrowDropUp diff --git a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/EmailIcon.kt b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/EmailIcon.kt similarity index 87% rename from app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/EmailIcon.kt rename to app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/EmailIcon.kt index ecec91ff..86ca03f8 100644 --- a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/EmailIcon.kt +++ b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/EmailIcon.kt @@ -1,4 +1,4 @@ -package io.github.taetae98coding.diary.core.resources.icon +package io.github.taetae98coding.diary.core.design.system.icon import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.AlternateEmail diff --git a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/FinishIcon.kt b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/FinishIcon.kt similarity index 65% rename from app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/FinishIcon.kt rename to app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/FinishIcon.kt index 55124c7d..6c8cc5b2 100644 --- a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/FinishIcon.kt +++ b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/FinishIcon.kt @@ -1,18 +1,22 @@ -package io.github.taetae98coding.diary.core.resources.icon +package io.github.taetae98coding.diary.core.design.system.icon import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.Verified import androidx.compose.material3.Icon +import androidx.compose.material3.LocalContentColor import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color @Composable public fun FinishIcon( modifier: Modifier = Modifier, + tint: Color = LocalContentColor.current, ) { Icon( modifier = modifier, imageVector = Icons.Rounded.Verified, contentDescription = null, + tint = tint, ) } diff --git a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/KeyIcon.kt b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/KeyIcon.kt similarity index 86% rename from app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/KeyIcon.kt rename to app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/KeyIcon.kt index 0ce22284..e7321df3 100644 --- a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/KeyIcon.kt +++ b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/KeyIcon.kt @@ -1,4 +1,4 @@ -package io.github.taetae98coding.diary.core.resources.icon +package io.github.taetae98coding.diary.core.design.system.icon import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.Key diff --git a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/LoginIcon.kt b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/LoginIcon.kt similarity index 87% rename from app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/LoginIcon.kt rename to app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/LoginIcon.kt index 963a553c..5807f9bc 100644 --- a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/LoginIcon.kt +++ b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/LoginIcon.kt @@ -1,4 +1,4 @@ -package io.github.taetae98coding.diary.core.resources.icon +package io.github.taetae98coding.diary.core.design.system.icon import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.rounded.Login diff --git a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/LogoutIcon.kt b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/LogoutIcon.kt similarity index 87% rename from app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/LogoutIcon.kt rename to app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/LogoutIcon.kt index f3042801..38529b99 100644 --- a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/LogoutIcon.kt +++ b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/LogoutIcon.kt @@ -1,4 +1,4 @@ -package io.github.taetae98coding.diary.core.resources.icon +package io.github.taetae98coding.diary.core.design.system.icon import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.rounded.Logout diff --git a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/MarkdownIcon.kt b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/MarkdownIcon.kt similarity index 86% rename from app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/MarkdownIcon.kt rename to app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/MarkdownIcon.kt index 55ebfa9f..40e06b1f 100644 --- a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/MarkdownIcon.kt +++ b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/MarkdownIcon.kt @@ -1,4 +1,4 @@ -package io.github.taetae98coding.diary.core.resources.icon +package io.github.taetae98coding.diary.core.design.system.icon import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.Code diff --git a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/MemoIcon.kt b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/MemoIcon.kt similarity index 87% rename from app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/MemoIcon.kt rename to app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/MemoIcon.kt index 84d4006d..e0a8a1ab 100644 --- a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/MemoIcon.kt +++ b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/MemoIcon.kt @@ -1,4 +1,4 @@ -package io.github.taetae98coding.diary.core.resources.icon +package io.github.taetae98coding.diary.core.design.system.icon import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.rounded.Article diff --git a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/MoreIcon.kt b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/MoreIcon.kt similarity index 87% rename from app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/MoreIcon.kt rename to app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/MoreIcon.kt index 288485ba..0e8f18b4 100644 --- a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/MoreIcon.kt +++ b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/MoreIcon.kt @@ -1,4 +1,4 @@ -package io.github.taetae98coding.diary.core.resources.icon +package io.github.taetae98coding.diary.core.design.system.icon import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.MoreHoriz diff --git a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/NavigateUpIcon.kt b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/NavigateUpIcon.kt similarity index 87% rename from app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/NavigateUpIcon.kt rename to app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/NavigateUpIcon.kt index a72e745d..a7365f8d 100644 --- a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/NavigateUpIcon.kt +++ b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/NavigateUpIcon.kt @@ -1,4 +1,4 @@ -package io.github.taetae98coding.diary.core.resources.icon +package io.github.taetae98coding.diary.core.design.system.icon import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.rounded.ArrowBack diff --git a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/RefreshIcon.kt b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/RefreshIcon.kt similarity index 87% rename from app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/RefreshIcon.kt rename to app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/RefreshIcon.kt index b23f4471..5fc17fa4 100644 --- a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/RefreshIcon.kt +++ b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/RefreshIcon.kt @@ -1,4 +1,4 @@ -package io.github.taetae98coding.diary.core.resources.icon +package io.github.taetae98coding.diary.core.design.system.icon import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.Refresh diff --git a/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/TagIcon.kt b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/TagIcon.kt new file mode 100644 index 00000000..37d629f3 --- /dev/null +++ b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/TagIcon.kt @@ -0,0 +1,18 @@ +package io.github.taetae98coding.diary.core.design.system.icon + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.Tag +import androidx.compose.material3.Icon +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier + +@Composable +public fun TagIcon( + modifier: Modifier = Modifier, +) { + Icon( + imageVector = Icons.Rounded.Tag, + contentDescription = null, + modifier = modifier, + ) +} diff --git a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/TextFieldIcon.kt b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/TextFieldIcon.kt similarity index 87% rename from app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/TextFieldIcon.kt rename to app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/TextFieldIcon.kt index 19f6b4b5..33c4d1b9 100644 --- a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/TextFieldIcon.kt +++ b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/TextFieldIcon.kt @@ -1,4 +1,4 @@ -package io.github.taetae98coding.diary.core.resources.icon +package io.github.taetae98coding.diary.core.design.system.icon import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.TextFields diff --git a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/VisibilityOffIcon.kt b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/VisibilityOffIcon.kt similarity index 100% rename from app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/VisibilityOffIcon.kt rename to app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/VisibilityOffIcon.kt diff --git a/app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/VisibilityOnIcon.kt b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/VisibilityOnIcon.kt similarity index 100% rename from app/core/resources/src/commonMain/kotlin/io/github/taetae98coding/diary/core/resources/icon/VisibilityOnIcon.kt rename to app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/icon/VisibilityOnIcon.kt diff --git a/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/text/ClearTextField.kt b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/text/ClearTextField.kt index 8a7acacb..19b8f16a 100644 --- a/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/text/ClearTextField.kt +++ b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/text/ClearTextField.kt @@ -11,7 +11,7 @@ import androidx.compose.runtime.NonRestartableComposable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.input.VisualTransformation -import io.github.taetae98coding.diary.core.resources.icon.ClearIcon +import io.github.taetae98coding.diary.core.design.system.icon.ClearIcon @Composable @NonRestartableComposable diff --git a/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/theme/DiaryTheme.kt b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/theme/DiaryTheme.kt index a6d2bfd4..4aeeda26 100644 --- a/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/theme/DiaryTheme.kt +++ b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/theme/DiaryTheme.kt @@ -37,11 +37,13 @@ public fun DiaryTheme( CompositionLocalProvider( LocalDiaryColor provides DiaryColor( primary = colorScheme.primary, + secondary = colorScheme.secondary, onPrimary = colorScheme.onPrimary, background = colorScheme.background, onSurface = colorScheme.onSurface, ), LocalDiaryTypography provides DiaryTypography( + headlineMedium = MaterialTheme.typography.headlineMedium, labelSmall = MaterialTheme.typography.labelSmall, labelMedium = MaterialTheme.typography.labelMedium, labelLarge = MaterialTheme.typography.labelLarge, diff --git a/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/typography/DiaryTypography.kt b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/typography/DiaryTypography.kt index 6b426e8c..9283849a 100644 --- a/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/typography/DiaryTypography.kt +++ b/app/core/design-system/src/commonMain/kotlin/io/github/taetae98coding/diary/core/design/system/typography/DiaryTypography.kt @@ -3,6 +3,7 @@ package io.github.taetae98coding.diary.core.design.system.typography import androidx.compose.ui.text.TextStyle public data class DiaryTypography( + val headlineMedium: TextStyle, val labelSmall: TextStyle, val labelMedium: TextStyle, val labelLarge: TextStyle, diff --git a/app/core/diary-database-memory/build.gradle.kts b/app/core/diary-database-memory/build.gradle.kts deleted file mode 100644 index 7e0160a7..00000000 --- a/app/core/diary-database-memory/build.gradle.kts +++ /dev/null @@ -1,21 +0,0 @@ -plugins { - id("diary.android.library") - id("diary.kotlin.multiplatform.all") - id("diary.koin.all") -} - -kotlin { - sourceSets { - commonMain { - dependencies { - implementation(project(":app:core:diary-database")) - implementation(project(":library:coroutines")) - implementation(project(":library:datetime")) - } - } - } -} - -android { - namespace = "${Build.NAMESPACE}.core.diary.database.memory" -} diff --git a/app/core/diary-database-memory/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/memory/MemoBackupMemoryDao.kt b/app/core/diary-database-memory/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/memory/MemoBackupMemoryDao.kt deleted file mode 100644 index b9757a62..00000000 --- a/app/core/diary-database-memory/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/memory/MemoBackupMemoryDao.kt +++ /dev/null @@ -1,54 +0,0 @@ -package io.github.taetae98coding.diary.core.diary.database.memory - -import io.github.taetae98coding.diary.core.diary.database.MemoBackupDao -import io.github.taetae98coding.diary.core.model.memo.MemoDto -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.mapLatest -import org.koin.core.annotation.Singleton - -@OptIn(ExperimentalCoroutinesApi::class) -@Singleton -internal class MemoBackupMemoryDao( - private val memoMemoryDao: MemoMemoryDao, -) : MemoBackupDao { - private val flow = MutableStateFlow>>(emptyMap()) - - override suspend fun upsert(uid: String, memoId: String) { - val set = buildSet { - flow.value[uid]?.let { addAll(it) } - add(memoId) - } - val map = buildMap { - putAll(flow.value) - put(uid, set) - } - - flow.emit(map) - } - - override suspend fun deleteByMemoIds(memoIds: List) { - val idSet = memoIds.toSet() - val map = buildMap { - flow.value.forEach { entry -> - val set = entry.value.filterNot { it in idSet } - .toSet() - - put(entry.key, set) - } - } - - flow.emit(map) - } - - override fun countByUid(uid: String): Flow { - return flow.mapLatest { it[uid]?.size ?: 0 } - } - - override fun findByUid(uid: String): Flow> { - return memoMemoryDao.flow.mapLatest { map -> - map.values.filter { it.owner == uid } - } - } -} diff --git a/app/core/diary-database-memory/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/memory/MemoMemoryDao.kt b/app/core/diary-database-memory/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/memory/MemoMemoryDao.kt deleted file mode 100644 index 36f1e230..00000000 --- a/app/core/diary-database-memory/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/memory/MemoMemoryDao.kt +++ /dev/null @@ -1,103 +0,0 @@ -package io.github.taetae98coding.diary.core.diary.database.memory - -import io.github.taetae98coding.diary.core.diary.database.MemoDao -import io.github.taetae98coding.diary.core.model.memo.MemoDetail -import io.github.taetae98coding.diary.core.model.memo.MemoDto -import io.github.taetae98coding.diary.library.coroutines.filterCollectionLatest -import io.github.taetae98coding.diary.library.datetime.isOverlap -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.mapLatest -import kotlinx.datetime.Clock -import kotlinx.datetime.Instant -import kotlinx.datetime.LocalDate -import org.koin.core.annotation.Singleton - -@OptIn(ExperimentalCoroutinesApi::class) -@Singleton -internal class MemoMemoryDao( - private val clock: Clock, -) : MemoDao { - val flow = MutableStateFlow>(emptyMap()) - - override suspend fun upsert(memo: MemoDto) { - val map = buildMap { - putAll(flow.value) - put(memo.id, memo) - } - - flow.emit(map) - } - - override suspend fun upsert(memoList: List) { - val map = buildMap { - putAll(flow.value) - memoList.forEach { put(it.id, it) } - } - - flow.emit(map) - } - - override suspend fun update(memoId: String, detail: MemoDetail) { - val dto = flow.value[memoId]?.copy( - detail = detail, - updateAt = clock.now(), - ) ?: return - val map = buildMap { - putAll(flow.value) - put(memoId, dto) - } - - flow.emit(map) - } - - override suspend fun updateFinish(memoId: String, isFinish: Boolean) { - val dto = flow.value[memoId]?.copy( - isFinish = isFinish, - updateAt = clock.now(), - ) ?: return - val map = buildMap { - putAll(flow.value) - put(memoId, dto) - } - - flow.emit(map) - } - - override suspend fun updateDelete(memoId: String, isDelete: Boolean) { - val dto = flow.value[memoId]?.copy( - isDelete = isDelete, - updateAt = clock.now(), - ) ?: return - val map = buildMap { - putAll(flow.value) - put(memoId, dto) - } - - flow.emit(map) - } - - override fun find(memoId: String): Flow { - return flow.mapLatest { it[memoId] } - } - - override fun findByDateRange(owner: String?, dateRange: ClosedRange): Flow> { - return flow.mapLatest { it.values } - .filterCollectionLatest { it.owner == owner } - .filterCollectionLatest { !it.isDelete } - .filterCollectionLatest { - val start = it.detail.start ?: return@filterCollectionLatest false - val endInclusive = it.detail.endInclusive ?: return@filterCollectionLatest false - - dateRange.isOverlap(start..endInclusive) - } - } - - override fun getLastServerUpdateAt(owner: String?): Flow { - return flow.mapLatest { it.values } - .filterCollectionLatest { it.owner == owner } - .filterCollectionLatest { it.serverUpdateAt != null } - .mapLatest { list -> list.maxOfOrNull { requireNotNull(it.serverUpdateAt) } } - } -} diff --git a/app/core/diary-database-room/schemas/io.github.taetae98coding.diary.core.diary.database.room.DiaryDatabase/2.json b/app/core/diary-database-room/schemas/io.github.taetae98coding.diary.core.diary.database.room.DiaryDatabase/2.json new file mode 100644 index 00000000..618813cf --- /dev/null +++ b/app/core/diary-database-room/schemas/io.github.taetae98coding.diary.core.diary.database.room.DiaryDatabase/2.json @@ -0,0 +1,272 @@ +{ + "formatVersion": 1, + "database": { + "version": 2, + "identityHash": "b29b7ff71883e0b0b16db7008a3d21f9", + "entities": [ + { + "tableName": "MemoEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL DEFAULT '', `title` TEXT NOT NULL DEFAULT '', `description` TEXT NOT NULL DEFAULT '', `start` TEXT DEFAULT null, `endInclusive` TEXT DEFAULT null, `color` INTEGER NOT NULL DEFAULT -16777216, `isFinish` INTEGER NOT NULL DEFAULT 0, `isDelete` INTEGER NOT NULL DEFAULT 0, `owner` TEXT DEFAULT null, `primaryTag` TEXT DEFAULT null, `updateAt` INTEGER NOT NULL DEFAULT 0, `serverUpdateAt` INTEGER DEFAULT null, PRIMARY KEY(`id`), FOREIGN KEY(`primaryTag`) REFERENCES `TagEntity`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "''" + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "''" + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "''" + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "TEXT", + "defaultValue": "null" + }, + { + "fieldPath": "endInclusive", + "columnName": "endInclusive", + "affinity": "TEXT", + "defaultValue": "null" + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-16777216" + }, + { + "fieldPath": "isFinish", + "columnName": "isFinish", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "isDelete", + "columnName": "isDelete", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "owner", + "columnName": "owner", + "affinity": "TEXT", + "defaultValue": "null" + }, + { + "fieldPath": "primaryTag", + "columnName": "primaryTag", + "affinity": "TEXT", + "defaultValue": "null" + }, + { + "fieldPath": "updateAt", + "columnName": "updateAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "serverUpdateAt", + "columnName": "serverUpdateAt", + "affinity": "INTEGER", + "defaultValue": "null" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_MemoEntity_primaryTag", + "unique": false, + "columnNames": [ + "primaryTag" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_MemoEntity_primaryTag` ON `${TABLE_NAME}` (`primaryTag`)" + } + ], + "foreignKeys": [ + { + "table": "TagEntity", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "primaryTag" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "TagEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL DEFAULT '', `title` TEXT NOT NULL DEFAULT '', `description` TEXT NOT NULL DEFAULT '', `color` INTEGER NOT NULL DEFAULT -16777216, `isFinish` INTEGER NOT NULL DEFAULT 0, `isDelete` INTEGER NOT NULL DEFAULT 0, `owner` TEXT DEFAULT null, `updateAt` INTEGER NOT NULL DEFAULT 0, `serverUpdateAt` INTEGER DEFAULT null, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "''" + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "''" + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "''" + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-16777216" + }, + { + "fieldPath": "isFinish", + "columnName": "isFinish", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "isDelete", + "columnName": "isDelete", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "owner", + "columnName": "owner", + "affinity": "TEXT", + "defaultValue": "null" + }, + { + "fieldPath": "updateAt", + "columnName": "updateAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "serverUpdateAt", + "columnName": "serverUpdateAt", + "affinity": "INTEGER", + "defaultValue": "null" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + } + }, + { + "tableName": "MemoTagEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`memoId` TEXT NOT NULL DEFAULT '', `tagId` TEXT NOT NULL DEFAULT '', PRIMARY KEY(`memoId`, `tagId`), FOREIGN KEY(`memoId`) REFERENCES `MemoEntity`(`id`) ON UPDATE CASCADE ON DELETE CASCADE , FOREIGN KEY(`tagId`) REFERENCES `TagEntity`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "memoId", + "columnName": "memoId", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "''" + }, + { + "fieldPath": "tagId", + "columnName": "tagId", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "''" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "memoId", + "tagId" + ] + }, + "indices": [ + { + "name": "index_MemoTagEntity_memoId", + "unique": false, + "columnNames": [ + "memoId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_MemoTagEntity_memoId` ON `${TABLE_NAME}` (`memoId`)" + }, + { + "name": "index_MemoTagEntity_tagId", + "unique": false, + "columnNames": [ + "tagId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_MemoTagEntity_tagId` ON `${TABLE_NAME}` (`tagId`)" + } + ], + "foreignKeys": [ + { + "table": "MemoEntity", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "memoId" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "TagEntity", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "tagId" + ], + "referencedColumns": [ + "id" + ] + } + ] + } + ], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'b29b7ff71883e0b0b16db7008a3d21f9')" + ] + } +} \ No newline at end of file diff --git a/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/DiaryDatabase.kt b/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/DiaryDatabase.kt index 29441afc..62cf5a82 100644 --- a/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/DiaryDatabase.kt +++ b/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/DiaryDatabase.kt @@ -1,13 +1,18 @@ package io.github.taetae98coding.diary.core.diary.database.room +import androidx.room.AutoMigration import androidx.room.ConstructedBy import androidx.room.Database +import androidx.room.DeleteTable import androidx.room.RoomDatabase import androidx.room.TypeConverters -import io.github.taetae98coding.diary.core.diary.database.room.dao.MemoBackupEntityDao +import androidx.room.migration.AutoMigrationSpec import io.github.taetae98coding.diary.core.diary.database.room.dao.MemoEntityDao -import io.github.taetae98coding.diary.core.diary.database.room.entity.MemoBackupEntity +import io.github.taetae98coding.diary.core.diary.database.room.dao.MemoTagEntityDao +import io.github.taetae98coding.diary.core.diary.database.room.dao.TagEntityDao import io.github.taetae98coding.diary.core.diary.database.room.entity.MemoEntity +import io.github.taetae98coding.diary.core.diary.database.room.entity.MemoTagEntity +import io.github.taetae98coding.diary.core.diary.database.room.entity.TagEntity import io.github.taetae98coding.diary.core.diary.database.room.internal.DiaryDatabaseConstructor import io.github.taetae98coding.diary.library.room.InstantConverter import io.github.taetae98coding.diary.library.room.LocalDataConverter @@ -15,9 +20,13 @@ import io.github.taetae98coding.diary.library.room.LocalDataConverter @Database( entities = [ MemoEntity::class, - MemoBackupEntity::class, + TagEntity::class, + MemoTagEntity::class, ], - version = 1 + version = 2, + autoMigrations = [ + AutoMigration(from = 1, to = 2, DiaryDatabase.DeleteMemoBackupEntity1To2::class) + ] ) @ConstructedBy(DiaryDatabaseConstructor::class) @TypeConverters( @@ -25,6 +34,10 @@ import io.github.taetae98coding.diary.library.room.LocalDataConverter InstantConverter::class, ) internal abstract class DiaryDatabase : RoomDatabase() { + @DeleteTable("MemoBackupEntity") + class DeleteMemoBackupEntity1To2 : AutoMigrationSpec + abstract fun memo(): MemoEntityDao - abstract fun memoBackup(): MemoBackupEntityDao + abstract fun tag(): TagEntityDao + abstract fun memoTag(): MemoTagEntityDao } diff --git a/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/DiaryRoomDatabaseModule.kt b/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/DiaryRoomDatabaseModule.kt index e719edb8..528ec710 100644 --- a/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/DiaryRoomDatabaseModule.kt +++ b/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/DiaryRoomDatabaseModule.kt @@ -12,6 +12,7 @@ public class DiaryRoomDatabaseModule : KoinComponent { @Singleton internal fun providesDiaryDatabase(): DiaryDatabase { return getDatabaseBuilder("diary.db") + .addMigrations() .build() } } diff --git a/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/dao/EntityDao.kt b/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/dao/EntityDao.kt deleted file mode 100644 index 97be1391..00000000 --- a/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/dao/EntityDao.kt +++ /dev/null @@ -1,19 +0,0 @@ -package io.github.taetae98coding.diary.core.diary.database.room.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Transaction -import androidx.room.Upsert - -@Dao -internal abstract class EntityDao { - @Upsert - abstract suspend fun upsert(entity: T) - - @Transaction - @Upsert - abstract suspend fun upsert(entityList: List) - - @Delete - abstract suspend fun delete(entity: T) -} diff --git a/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/dao/MemoBackupEntityDao.kt b/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/dao/MemoBackupEntityDao.kt deleted file mode 100644 index cfd1dcf2..00000000 --- a/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/dao/MemoBackupEntityDao.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.taetae98coding.diary.core.diary.database.room.dao - -import androidx.room.Dao -import androidx.room.Query -import io.github.taetae98coding.diary.core.diary.database.room.entity.MemoBackupEntity -import io.github.taetae98coding.diary.core.diary.database.room.entity.MemoEntity -import kotlinx.coroutines.flow.Flow - -@Dao -internal abstract class MemoBackupEntityDao : EntityDao() { - @Query("DELETE FROM MemoBackupEntity WHERE memoId IN (:memoIds)") - abstract suspend fun deleteByMemoIds(memoIds: List) - - @Query("SELECT COUNT(memoId) FROM MemoBackupEntity WHERE uid = :uid") - abstract fun countByUid(uid: String): Flow - - @Query(""" - SELECT * - FROM MemoEntity - WHERE id IN (SELECT memoId FROM MemoBackupEntity WHERE uid = :uid) - LIMIT 50 - """) - abstract fun findByUid(uid: String): Flow> -} diff --git a/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/dao/MemoBackupRoomDao.kt b/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/dao/MemoBackupRoomDao.kt deleted file mode 100644 index a196431e..00000000 --- a/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/dao/MemoBackupRoomDao.kt +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.taetae98coding.diary.core.diary.database.room.dao - -import io.github.taetae98coding.diary.core.diary.database.MemoBackupDao -import io.github.taetae98coding.diary.core.diary.database.room.DiaryDatabase -import io.github.taetae98coding.diary.core.diary.database.room.entity.MemoBackupEntity -import io.github.taetae98coding.diary.core.diary.database.room.entity.MemoEntity -import io.github.taetae98coding.diary.core.diary.database.room.mapper.toDto -import io.github.taetae98coding.diary.core.model.memo.MemoDto -import io.github.taetae98coding.diary.library.coroutines.mapCollectionLatest -import kotlinx.coroutines.flow.Flow -import org.koin.core.annotation.Factory - -@Factory -internal class MemoBackupRoomDao( - private val database: DiaryDatabase, -) : MemoBackupDao { - override suspend fun upsert(uid: String, memoId: String) { - database.memoBackup().upsert(MemoBackupEntity(uid, memoId)) - } - - override suspend fun deleteByMemoIds(memoIds: List) { - database.memoBackup().deleteByMemoIds(memoIds) - } - - override fun countByUid(uid: String): Flow { - return database.memoBackup().countByUid(uid) - } - - override fun findByUid(uid: String): Flow> { - return database.memoBackup().findByUid(uid) - .mapCollectionLatest(MemoEntity::toDto) - } -} diff --git a/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/dao/MemoEntityDao.kt b/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/dao/MemoEntityDao.kt index 74289394..69492d1e 100644 --- a/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/dao/MemoEntityDao.kt +++ b/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/dao/MemoEntityDao.kt @@ -2,13 +2,39 @@ package io.github.taetae98coding.diary.core.diary.database.room.dao import androidx.room.Dao import androidx.room.Query +import androidx.room.Transaction +import androidx.room.Upsert import io.github.taetae98coding.diary.core.diary.database.room.entity.MemoEntity +import io.github.taetae98coding.diary.core.diary.database.room.entity.MemoTagEntity +import io.github.taetae98coding.diary.core.diary.database.room.mapper.toEntity +import io.github.taetae98coding.diary.core.model.memo.MemoAndTagIds +import io.github.taetae98coding.diary.library.room.dao.EntityDao import kotlinx.coroutines.flow.Flow import kotlinx.datetime.Instant import kotlinx.datetime.LocalDate @Dao internal abstract class MemoEntityDao : EntityDao() { + @Transaction + open suspend fun upsertMemoAndTagIds(memo: MemoAndTagIds) { + upsert(memo.memo.toEntity()) + deleteMemoTag(memo.memo.id) + memo.tagIds.forEach { + upsertMemoTag(MemoTagEntity(memoId = memo.memo.id, tagId = it)) + } + } + + @Transaction + open suspend fun upsertMemoAndTagIds(memoList: List) { + memoList.forEach { upsertMemoAndTagIds(it) } + } + + @Upsert + abstract suspend fun upsertMemoTag(entity: MemoTagEntity) + + @Query("DELETE FROM MemoTagEntity WHERE memoId = :memoId") + abstract suspend fun deleteMemoTag(memoId: String) + @Query( """ UPDATE MemoEntity @@ -32,6 +58,17 @@ internal abstract class MemoEntityDao : EntityDao() { updateAt: Instant, ) + @Query( + """ + UPDATE MemoEntity + SET + primaryTag = :primaryTag, + updateAt = :updateAt + WHERE id = :memoId + """, + ) + abstract suspend fun updatePrimaryTag(memoId: String, primaryTag: String?, updateAt: Instant) + @Query( """ UPDATE MemoEntity @@ -63,6 +100,17 @@ internal abstract class MemoEntityDao : EntityDao() { ) abstract fun find(memoId: String): Flow + @Query( + """ + SELECT * + FROM MemoEntity + LEFT OUTER JOIN MemoTagEntity + ON MemoEntity.id = MemoTagEntity.memoId + WHERE MemoEntity.id IN (:memoIds) + """, + ) + abstract fun findMemoAndTagIdsByIds(memoIds: Set): Flow>> + @Query( """ SELECT * diff --git a/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/dao/MemoRoomDao.kt b/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/dao/MemoRoomDao.kt index cbf97aa5..2721b79f 100644 --- a/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/dao/MemoRoomDao.kt +++ b/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/dao/MemoRoomDao.kt @@ -4,13 +4,14 @@ import io.github.taetae98coding.diary.core.diary.database.MemoDao import io.github.taetae98coding.diary.core.diary.database.room.DiaryDatabase import io.github.taetae98coding.diary.core.diary.database.room.entity.MemoEntity import io.github.taetae98coding.diary.core.diary.database.room.mapper.toDto -import io.github.taetae98coding.diary.core.diary.database.room.mapper.toEntity +import io.github.taetae98coding.diary.core.model.memo.MemoAndTagIds import io.github.taetae98coding.diary.core.model.memo.MemoDetail import io.github.taetae98coding.diary.core.model.memo.MemoDto import io.github.taetae98coding.diary.library.coroutines.mapCollectionLatest import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.mapLatest +import kotlinx.coroutines.flow.onEach import kotlinx.datetime.Clock import kotlinx.datetime.Instant import kotlinx.datetime.LocalDate @@ -22,12 +23,8 @@ internal class MemoRoomDao( private val clock: Clock, private val database: DiaryDatabase, ) : MemoDao { - override suspend fun upsert(memo: MemoDto) { - database.memo().upsert(memo.toEntity()) - } - - override suspend fun upsert(memoList: List) { - database.memo().upsert(memoList.map(MemoDto::toEntity)) + override suspend fun upsert(dto: MemoAndTagIds) { + database.memo().upsertMemoAndTagIds(dto) } override suspend fun update(memoId: String, detail: MemoDetail) { @@ -43,6 +40,10 @@ internal class MemoRoomDao( ) } + override suspend fun updatePrimaryTag(memoId: String, tagId: String?) { + database.memo().updatePrimaryTag(memoId, tagId, clock.now()) + } + override suspend fun updateFinish(memoId: String, isFinish: Boolean) { database.memo().updateFinish(memoId, isFinish, clock.now()) } @@ -61,6 +62,22 @@ internal class MemoRoomDao( .mapCollectionLatest(MemoEntity::toDto) } + override suspend fun upsert(memoList: List) { + database.memo().upsertMemoAndTagIds(memoList) + } + + override fun findMemoAndTagIdsByIds(memoIds: Set): Flow> { + return database.memo().findMemoAndTagIdsByIds(memoIds) + .mapLatest { map -> + map.map { entry -> + MemoAndTagIds( + memo = entry.key.toDto(), + tagIds = entry.value.map { it.tagId }.toSet() + ) + } + } + } + override fun getLastServerUpdateAt(owner: String?): Flow { return database.memo().getLastUpdateAt(owner) } diff --git a/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/dao/MemoTagEntityDao.kt b/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/dao/MemoTagEntityDao.kt new file mode 100644 index 00000000..f84879ea --- /dev/null +++ b/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/dao/MemoTagEntityDao.kt @@ -0,0 +1,24 @@ +package io.github.taetae98coding.diary.core.diary.database.room.dao + +import androidx.room.Dao +import androidx.room.Query +import io.github.taetae98coding.diary.core.diary.database.room.entity.MemoTagEntity +import io.github.taetae98coding.diary.core.diary.database.room.entity.TagEntity +import io.github.taetae98coding.diary.library.room.dao.EntityDao +import kotlinx.coroutines.flow.Flow + +@Dao +internal abstract class MemoTagEntityDao : EntityDao() { + @Query(""" + SELECT * + FROM TagEntity + WHERE id IN ( + SELECT tagId + FROM MemoTagEntity + WHERE memoId = :memoId + ) + AND isDelete = 0 + ORDER BY title + """) + abstract fun findTagByMemoId(memoId: String): Flow> +} diff --git a/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/dao/MemoTagRoomDao.kt b/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/dao/MemoTagRoomDao.kt new file mode 100644 index 00000000..69c323b6 --- /dev/null +++ b/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/dao/MemoTagRoomDao.kt @@ -0,0 +1,29 @@ +package io.github.taetae98coding.diary.core.diary.database.room.dao + +import io.github.taetae98coding.diary.core.diary.database.MemoTagDao +import io.github.taetae98coding.diary.core.diary.database.room.DiaryDatabase +import io.github.taetae98coding.diary.core.diary.database.room.entity.MemoTagEntity +import io.github.taetae98coding.diary.core.diary.database.room.entity.TagEntity +import io.github.taetae98coding.diary.core.diary.database.room.mapper.toDto +import io.github.taetae98coding.diary.core.model.tag.TagDto +import io.github.taetae98coding.diary.library.coroutines.mapCollectionLatest +import kotlinx.coroutines.flow.Flow +import org.koin.core.annotation.Factory + +@Factory +internal class MemoTagRoomDao( + private val database: DiaryDatabase, +) : MemoTagDao { + override fun findByMemoId(memoId: String): Flow> { + return database.memoTag().findTagByMemoId(memoId) + .mapCollectionLatest(TagEntity::toDto) + } + + override suspend fun upsert(memoId: String, tagId: String) { + database.memoTag().upsert(MemoTagEntity(memoId, tagId)) + } + + override suspend fun delete(memoId: String, tagId: String) { + database.memoTag().delete(MemoTagEntity(memoId, tagId)) + } +} diff --git a/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/dao/TagEntityDao.kt b/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/dao/TagEntityDao.kt new file mode 100644 index 00000000..a9844297 --- /dev/null +++ b/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/dao/TagEntityDao.kt @@ -0,0 +1,86 @@ +package io.github.taetae98coding.diary.core.diary.database.room.dao + +import androidx.room.Dao +import androidx.room.Query +import io.github.taetae98coding.diary.core.diary.database.room.entity.TagEntity +import io.github.taetae98coding.diary.library.room.dao.EntityDao +import kotlinx.coroutines.flow.Flow +import kotlinx.datetime.Instant + +@Dao +internal abstract class TagEntityDao : EntityDao() { + @Query( + """ + UPDATE TagEntity + SET + title = :title, + description = :description, + color = :color, + updateAt = :updateAt + WHERE id = :tagId + """, + ) + abstract suspend fun update( + tagId: String, + title: String, + description: String, + color: Int, + updateAt: Instant, + ) + + @Query( + """ + UPDATE TagEntity + SET + isFinish = :isFinish, + updateAt = :updateAt + WHERE id = :tagId + """, + ) + abstract suspend fun updateFinish(tagId: String, isFinish: Boolean, updateAt: Instant) + + @Query( + """ + UPDATE TagEntity + SET + isDelete = :isDelete, + updateAt = :updateAt + WHERE id = :tagId + """, + ) + abstract suspend fun updateDelete(tagId: String, isDelete: Boolean, updateAt: Instant) + + @Query( + """ + SELECT * + FROM TagEntity + WHERE id = :tagId + """, + ) + abstract fun find(tagId: String): Flow + + @Query( + """ + SELECT * + FROM TagEntity + WHERE id IN (:tagIds) + AND isDelete = 0 + """, + ) + abstract fun findByIds(tagIds: Set): Flow> + + @Query( + """ + SELECT * + FROM TagEntity + WHERE isDelete = 0 + AND isFinish = 0 + AND (owner = :owner OR (owner IS NULL AND :owner IS NULL)) + ORDER BY title + """, + ) + abstract fun page(owner: String?): Flow> + + @Query("SELECT MAX(serverUpdateAt) FROM TagEntity WHERE owner = :owner") + abstract fun getLastUpdateAt(owner: String?): Flow +} diff --git a/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/dao/TagRoomDao.kt b/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/dao/TagRoomDao.kt new file mode 100644 index 00000000..123e8f3d --- /dev/null +++ b/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/dao/TagRoomDao.kt @@ -0,0 +1,68 @@ +package io.github.taetae98coding.diary.core.diary.database.room.dao + +import io.github.taetae98coding.diary.core.diary.database.TagDao +import io.github.taetae98coding.diary.core.diary.database.room.DiaryDatabase +import io.github.taetae98coding.diary.core.diary.database.room.entity.TagEntity +import io.github.taetae98coding.diary.core.diary.database.room.mapper.toDto +import io.github.taetae98coding.diary.core.diary.database.room.mapper.toEntity +import io.github.taetae98coding.diary.core.model.tag.TagDetail +import io.github.taetae98coding.diary.core.model.tag.TagDto +import io.github.taetae98coding.diary.library.coroutines.mapCollectionLatest +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.mapLatest +import kotlinx.datetime.Clock +import kotlinx.datetime.Instant +import org.koin.core.annotation.Factory + +@OptIn(ExperimentalCoroutinesApi::class) +@Factory +internal class TagRoomDao( + private val clock: Clock, + private val database: DiaryDatabase, +) : TagDao { + override suspend fun upsert(tag: TagDto) { + database.tag().upsert(tag.toEntity()) + } + + override suspend fun upsert(tagList: List) { + database.tag().upsert(tagList.map(TagDto::toEntity)) + } + + override suspend fun update(tagId: String, detail: TagDetail) { + database.tag().update( + tagId = tagId, + title = detail.title, + description = detail.description, + color = detail.color, + updateAt = clock.now(), + ) + } + + override suspend fun updateFinish(tagId: String, isFinish: Boolean) { + database.tag().updateFinish(tagId, isFinish, clock.now()) + } + + override suspend fun updateDelete(tagId: String, isDelete: Boolean) { + database.tag().updateDelete(tagId, isDelete, clock.now()) + } + + override fun find(tagId: String): Flow { + return database.tag().find(tagId).mapLatest { it?.toDto() } + } + + override fun findByIds(tagIds: Set): Flow> { + return database.tag().findByIds(tagIds) + .mapCollectionLatest(TagEntity::toDto) + } + + override fun page(owner: String?): Flow> { + return database.tag().page(owner) + .mapCollectionLatest(TagEntity::toDto) + } + + + override fun getLastServerUpdateAt(owner: String?): Flow { + return database.tag().getLastUpdateAt(owner) + } +} diff --git a/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/entity/MemoBackupEntity.kt b/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/entity/MemoBackupEntity.kt deleted file mode 100644 index 38a9242c..00000000 --- a/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/entity/MemoBackupEntity.kt +++ /dev/null @@ -1,21 +0,0 @@ -package io.github.taetae98coding.diary.core.diary.database.room.entity - -import androidx.room.Entity -import androidx.room.ForeignKey - -@Entity( - primaryKeys = ["uid", "memoId"], - foreignKeys = [ - ForeignKey( - entity = MemoEntity::class, - parentColumns = ["id"], - childColumns = ["memoId"], - onDelete = ForeignKey.CASCADE, - onUpdate = ForeignKey.CASCADE, - ), - ], -) -internal data class MemoBackupEntity( - val uid: String, - val memoId: String, -) diff --git a/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/entity/MemoEntity.kt b/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/entity/MemoEntity.kt index 16b9eda4..b1501029 100644 --- a/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/entity/MemoEntity.kt +++ b/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/entity/MemoEntity.kt @@ -2,11 +2,26 @@ package io.github.taetae98coding.diary.core.diary.database.room.entity import androidx.room.ColumnInfo import androidx.room.Entity +import androidx.room.ForeignKey +import androidx.room.Index import androidx.room.PrimaryKey import kotlinx.datetime.Instant import kotlinx.datetime.LocalDate -@Entity +@Entity( + indices = [ + Index("primaryTag"), + ], + foreignKeys = [ + ForeignKey( + entity = TagEntity::class, + parentColumns = ["id"], + childColumns = ["primaryTag"], + onDelete = ForeignKey.CASCADE, + onUpdate = ForeignKey.CASCADE, + ), + ], +) internal data class MemoEntity( @PrimaryKey @ColumnInfo(defaultValue = "") @@ -27,6 +42,8 @@ internal data class MemoEntity( val isDelete: Boolean, @ColumnInfo(defaultValue = "null") val owner: String?, + @ColumnInfo(defaultValue = "null") + val primaryTag: String?, @ColumnInfo(defaultValue = "0") val updateAt: Instant, @ColumnInfo(defaultValue = "null") diff --git a/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/entity/MemoTagEntity.kt b/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/entity/MemoTagEntity.kt new file mode 100644 index 00000000..eca383e9 --- /dev/null +++ b/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/entity/MemoTagEntity.kt @@ -0,0 +1,35 @@ +package io.github.taetae98coding.diary.core.diary.database.room.entity + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.ForeignKey +import androidx.room.Index + +@Entity( + indices = [ + Index("memoId"), Index("tagId"), + ], + primaryKeys = ["memoId", "tagId"], + foreignKeys = [ + ForeignKey( + entity = MemoEntity::class, + parentColumns = ["id"], + childColumns = ["memoId"], + onDelete = ForeignKey.CASCADE, + onUpdate = ForeignKey.CASCADE, + ), + ForeignKey( + entity = TagEntity::class, + parentColumns = ["id"], + childColumns = ["tagId"], + onDelete = ForeignKey.CASCADE, + onUpdate = ForeignKey.CASCADE, + ), + ], +) +internal data class MemoTagEntity( + @ColumnInfo(defaultValue = "") + val memoId: String, + @ColumnInfo(defaultValue = "") + val tagId: String, +) diff --git a/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/entity/TagEntity.kt b/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/entity/TagEntity.kt new file mode 100644 index 00000000..a7fce016 --- /dev/null +++ b/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/entity/TagEntity.kt @@ -0,0 +1,29 @@ +package io.github.taetae98coding.diary.core.diary.database.room.entity + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import kotlinx.datetime.Instant + +@Entity +internal data class TagEntity( + @PrimaryKey + @ColumnInfo(defaultValue = "") + val id: String, + @ColumnInfo(defaultValue = "") + val title: String, + @ColumnInfo(defaultValue = "") + val description: String, + @ColumnInfo(defaultValue = "-16777216") + val color: Int, + @ColumnInfo(defaultValue = "0") + val isFinish: Boolean, + @ColumnInfo(defaultValue = "0") + val isDelete: Boolean, + @ColumnInfo(defaultValue = "null") + val owner: String?, + @ColumnInfo(defaultValue = "0") + val updateAt: Instant, + @ColumnInfo(defaultValue = "null") + val serverUpdateAt: Instant?, +) diff --git a/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/mapper/MemoMapper.kt b/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/mapper/MemoMapper.kt index 19d88281..12686688 100644 --- a/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/mapper/MemoMapper.kt +++ b/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/mapper/MemoMapper.kt @@ -1,8 +1,8 @@ package io.github.taetae98coding.diary.core.diary.database.room.mapper -import io.github.taetae98coding.diary.core.model.memo.MemoDto import io.github.taetae98coding.diary.core.diary.database.room.entity.MemoEntity import io.github.taetae98coding.diary.core.model.memo.MemoDetail +import io.github.taetae98coding.diary.core.model.memo.MemoDto internal fun MemoDto.toEntity(): MemoEntity { return MemoEntity( @@ -15,6 +15,7 @@ internal fun MemoDto.toEntity(): MemoEntity { isFinish = isFinish, isDelete = isDelete, owner = owner, + primaryTag = primaryTag, updateAt = updateAt, serverUpdateAt = serverUpdateAt, ) @@ -31,6 +32,7 @@ internal fun MemoEntity.toDto(): MemoDto { color = color, ), owner = owner, + primaryTag = primaryTag, isFinish = isFinish, isDelete = isDelete, updateAt = updateAt, diff --git a/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/mapper/MemoTagMapper.kt b/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/mapper/MemoTagMapper.kt new file mode 100644 index 00000000..84256355 --- /dev/null +++ b/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/mapper/MemoTagMapper.kt @@ -0,0 +1,11 @@ +package io.github.taetae98coding.diary.core.diary.database.room.mapper + +import io.github.taetae98coding.diary.core.diary.database.room.entity.MemoTagEntity +import io.github.taetae98coding.diary.core.model.memo.MemoTag + +internal fun MemoTag.toEntity(): MemoTagEntity { + return MemoTagEntity( + memoId = memoId, + tagId = tagId + ) +} diff --git a/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/mapper/TagMapper.kt b/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/mapper/TagMapper.kt new file mode 100644 index 00000000..5c8fb596 --- /dev/null +++ b/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/mapper/TagMapper.kt @@ -0,0 +1,35 @@ +package io.github.taetae98coding.diary.core.diary.database.room.mapper + +import io.github.taetae98coding.diary.core.diary.database.room.entity.TagEntity +import io.github.taetae98coding.diary.core.model.tag.TagDetail +import io.github.taetae98coding.diary.core.model.tag.TagDto + +internal fun TagDto.toEntity(): TagEntity { + return TagEntity( + id = id, + title = detail.title, + description = detail.description, + color = detail.color, + isFinish = isFinish, + isDelete = isDelete, + owner = owner, + updateAt = updateAt, + serverUpdateAt = serverUpdateAt, + ) +} + +internal fun TagEntity.toDto(): TagDto { + return TagDto( + id = id, + detail = TagDetail( + title = title, + description = description, + color = color, + ), + owner = owner, + isFinish = isFinish, + isDelete = isDelete, + updateAt = updateAt, + serverUpdateAt = serverUpdateAt, + ) +} diff --git a/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/migration/Migration.kt b/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/migration/Migration.kt new file mode 100644 index 00000000..5686edc0 --- /dev/null +++ b/app/core/diary-database-room/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/room/migration/Migration.kt @@ -0,0 +1,13 @@ +package io.github.taetae98coding.diary.core.diary.database.room.migration + +import androidx.room.migration.Migration +import androidx.sqlite.SQLiteConnection +import androidx.sqlite.execSQL + +internal fun migration1To2(): Migration { + return object : Migration(1, 2) { + override fun migrate(connection: SQLiteConnection) { + connection.execSQL("DROP TABLE MemoBackupEntity") + } + } +} diff --git a/app/core/diary-database/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/MemoBackupDao.kt b/app/core/diary-database/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/MemoBackupDao.kt deleted file mode 100644 index a5d3cd34..00000000 --- a/app/core/diary-database/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/MemoBackupDao.kt +++ /dev/null @@ -1,12 +0,0 @@ -package io.github.taetae98coding.diary.core.diary.database - -import io.github.taetae98coding.diary.core.model.memo.MemoDto -import kotlinx.coroutines.flow.Flow - -public interface MemoBackupDao { - public suspend fun upsert(uid: String, memoId: String) - public suspend fun deleteByMemoIds(memoIds: List) - - public fun countByUid(uid: String): Flow - public fun findByUid(uid: String): Flow> -} diff --git a/app/core/diary-database/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/MemoDao.kt b/app/core/diary-database/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/MemoDao.kt index 42af30bc..65712fd4 100644 --- a/app/core/diary-database/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/MemoDao.kt +++ b/app/core/diary-database/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/MemoDao.kt @@ -1,5 +1,6 @@ package io.github.taetae98coding.diary.core.diary.database +import io.github.taetae98coding.diary.core.model.memo.MemoAndTagIds import io.github.taetae98coding.diary.core.model.memo.MemoDetail import io.github.taetae98coding.diary.core.model.memo.MemoDto import kotlinx.coroutines.flow.Flow @@ -7,14 +8,18 @@ import kotlinx.datetime.Instant import kotlinx.datetime.LocalDate public interface MemoDao { - public suspend fun upsert(memo: MemoDto) - public suspend fun upsert(memoList: List) + public suspend fun upsert(dto: MemoAndTagIds) + public suspend fun upsert(memoList: List) public suspend fun update(memoId: String, detail: MemoDetail) + public suspend fun updatePrimaryTag(memoId: String, tagId: String?) public suspend fun updateFinish(memoId: String, isFinish: Boolean) public suspend fun updateDelete(memoId: String, isDelete: Boolean) public fun find(memoId: String): Flow public fun findByDateRange(owner: String?, dateRange: ClosedRange): Flow> + + public fun findMemoAndTagIdsByIds(memoIds: Set): Flow> + public fun getLastServerUpdateAt(owner: String?): Flow } diff --git a/app/core/diary-database/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/MemoTagDao.kt b/app/core/diary-database/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/MemoTagDao.kt new file mode 100644 index 00000000..839a8624 --- /dev/null +++ b/app/core/diary-database/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/MemoTagDao.kt @@ -0,0 +1,11 @@ +package io.github.taetae98coding.diary.core.diary.database + +import io.github.taetae98coding.diary.core.model.tag.TagDto +import kotlinx.coroutines.flow.Flow + +public interface MemoTagDao { + public suspend fun upsert(memoId: String, tagId: String) + public suspend fun delete(memoId: String, tagId: String) + + public fun findByMemoId(memoId: String): Flow> +} diff --git a/app/core/diary-database/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/TagDao.kt b/app/core/diary-database/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/TagDao.kt new file mode 100644 index 00000000..d49e6935 --- /dev/null +++ b/app/core/diary-database/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/TagDao.kt @@ -0,0 +1,23 @@ +package io.github.taetae98coding.diary.core.diary.database + +import io.github.taetae98coding.diary.core.model.tag.TagDetail +import io.github.taetae98coding.diary.core.model.tag.TagDto +import kotlinx.coroutines.flow.Flow +import kotlinx.datetime.Instant + +public interface TagDao { + public suspend fun upsert(tag: TagDto) + public suspend fun upsert(tagList: List) + + public suspend fun update(tagId: String, detail: TagDetail) + public suspend fun updateFinish(tagId: String, isFinish: Boolean) + public suspend fun updateDelete(tagId: String, isDelete: Boolean) + + public fun find(tagId: String):Flow + public fun findByIds(tagIds: Set): Flow> + + public fun page(owner: String?): Flow> + + public fun getLastServerUpdateAt(owner: String?): Flow + +} diff --git a/app/core/diary-service/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/service/memo/MemoService.kt b/app/core/diary-service/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/service/memo/MemoService.kt index 503415c1..b6241cca 100644 --- a/app/core/diary-service/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/service/memo/MemoService.kt +++ b/app/core/diary-service/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/service/memo/MemoService.kt @@ -3,7 +3,7 @@ package io.github.taetae98coding.diary.core.diary.service.memo import io.github.taetae98coding.diary.common.model.memo.MemoEntity import io.github.taetae98coding.diary.core.diary.service.DiaryServiceModule import io.github.taetae98coding.diary.core.diary.service.ext.getOrThrow -import io.github.taetae98coding.diary.core.model.memo.Memo +import io.github.taetae98coding.diary.core.model.memo.MemoAndTagIds import io.github.taetae98coding.diary.core.model.memo.MemoDetail import io.github.taetae98coding.diary.core.model.memo.MemoDto import io.ktor.client.HttpClient @@ -22,20 +22,22 @@ public class MemoService internal constructor( @Named(DiaryServiceModule.DIARY_CLIENT) private val client: HttpClient, ) { - public suspend fun upsert(list: List) { + public suspend fun upsert(list: List) { return client.post("/memo/upsert") { val body = list.map { MemoEntity( - id = it.id, - title = it.detail.title, - description = it.detail.description, - start = it.detail.start, - endInclusive = it.detail.endInclusive, - color = it.detail.color, - owner = requireNotNull(it.owner), - isFinish = it.isFinish, - isDelete = it.isDelete, - updateAt = it.updateAt, + id = it.memo.id, + title = it.memo.detail.title, + description = it.memo.detail.description, + start = it.memo.detail.start, + endInclusive = it.memo.detail.endInclusive, + color = it.memo.detail.color, + owner = requireNotNull(it.memo.owner), + primaryTag = it.memo.primaryTag, + tagIds = it.tagIds, + isFinish = it.memo.isFinish, + isDelete = it.memo.isDelete, + updateAt = it.memo.updateAt, ) } @@ -44,13 +46,13 @@ public class MemoService internal constructor( }.getOrThrow() } - public suspend fun fetch(updateAt: Instant): List { + public suspend fun fetch(updateAt: Instant): List { val response = client.get("/memo/fetch") { parameter("updateAt", updateAt) }.getOrThrow>() return response.map { - MemoDto( + val dto = MemoDto( id = it.id, detail = MemoDetail( title = it.title, @@ -60,11 +62,14 @@ public class MemoService internal constructor( color = it.color, ), owner = it.owner, + primaryTag = it.primaryTag, isFinish = it.isFinish, isDelete = it.isDelete, updateAt = it.updateAt, serverUpdateAt = it.updateAt, ) + + MemoAndTagIds(dto, it.tagIds) } } } diff --git a/app/core/diary-service/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/service/memo/MemoTagService.kt b/app/core/diary-service/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/service/memo/MemoTagService.kt new file mode 100644 index 00000000..dd24e913 --- /dev/null +++ b/app/core/diary-service/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/service/memo/MemoTagService.kt @@ -0,0 +1,35 @@ +package io.github.taetae98coding.diary.core.diary.service.memo + +import io.github.taetae98coding.diary.common.model.memo.MemoTagEntity +import io.github.taetae98coding.diary.core.diary.service.DiaryServiceModule +import io.github.taetae98coding.diary.core.diary.service.ext.getOrThrow +import io.github.taetae98coding.diary.core.model.memo.MemoTagDto +import io.ktor.client.HttpClient +import io.ktor.client.request.post +import io.ktor.client.request.setBody +import io.ktor.http.ContentType +import io.ktor.http.contentType +import org.koin.core.annotation.Factory +import org.koin.core.annotation.Named + +@Factory +public class MemoTagService internal constructor( + @Named(DiaryServiceModule.DIARY_CLIENT) + private val client: HttpClient, +) { + public suspend fun upsert(list: List) { + return client.post("/memoTag/upsert") { + val body = list.map { + MemoTagEntity( + memoId = it.memoId, + tagId = it.tagId, + isSelected = it.isSelected, + updateAt = it.updateAt, + ) + } + + contentType(ContentType.Application.Json) + setBody(body) + }.getOrThrow() + } +} diff --git a/app/core/diary-service/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/service/memo/TagService.kt b/app/core/diary-service/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/service/memo/TagService.kt new file mode 100644 index 00000000..b27de421 --- /dev/null +++ b/app/core/diary-service/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/service/memo/TagService.kt @@ -0,0 +1,65 @@ +package io.github.taetae98coding.diary.core.diary.service.memo + +import io.github.taetae98coding.diary.common.model.memo.TagEntity +import io.github.taetae98coding.diary.core.diary.service.DiaryServiceModule +import io.github.taetae98coding.diary.core.diary.service.ext.getOrThrow +import io.github.taetae98coding.diary.core.model.tag.TagDetail +import io.github.taetae98coding.diary.core.model.tag.TagDto +import io.ktor.client.HttpClient +import io.ktor.client.request.get +import io.ktor.client.request.parameter +import io.ktor.client.request.post +import io.ktor.client.request.setBody +import io.ktor.http.ContentType +import io.ktor.http.contentType +import kotlinx.datetime.Instant +import org.koin.core.annotation.Factory +import org.koin.core.annotation.Named + +@Factory +public class TagService internal constructor( + @Named(DiaryServiceModule.DIARY_CLIENT) + private val client: HttpClient, +) { + public suspend fun upsert(list: List) { + return client.post("/tag/upsert") { + val body = list.map { + TagEntity( + id = it.id, + title = it.detail.title, + description = it.detail.description, + color = it.detail.color, + owner = requireNotNull(it.owner), + isFinish = it.isFinish, + isDelete = it.isDelete, + updateAt = it.updateAt, + ) + } + + contentType(ContentType.Application.Json) + setBody(body) + }.getOrThrow() + } + + public suspend fun fetch(updateAt: Instant): List { + val response = client.get("/tag/fetch") { + parameter("updateAt", updateAt) + }.getOrThrow>() + + return response.map { + TagDto( + id = it.id, + detail = TagDetail( + title = it.title, + description = it.description, + color = it.color, + ), + owner = it.owner, + isFinish = it.isFinish, + isDelete = it.isDelete, + updateAt = it.updateAt, + serverUpdateAt = it.updateAt, + ) + } + } +} diff --git a/app/core/holiday-database-memory/build.gradle.kts b/app/core/holiday-database-memory/build.gradle.kts deleted file mode 100644 index f63d5df3..00000000 --- a/app/core/holiday-database-memory/build.gradle.kts +++ /dev/null @@ -1,19 +0,0 @@ -plugins { - id("diary.android.library") - id("diary.kotlin.multiplatform.all") - id("diary.koin.all") -} - -kotlin { - sourceSets { - commonMain { - dependencies { - implementation(project(":app:core:holiday-database")) - } - } - } -} - -android { - namespace = "${Build.NAMESPACE}.core.holiday.database.memory" -} diff --git a/app/core/holiday-database-memory/src/commonMain/kotlin/io/github/taetae98coding/diary/core/holiday/database/memory/HolidayMemoryDao.kt b/app/core/holiday-database-memory/src/commonMain/kotlin/io/github/taetae98coding/diary/core/holiday/database/memory/HolidayMemoryDao.kt deleted file mode 100644 index c4a537ab..00000000 --- a/app/core/holiday-database-memory/src/commonMain/kotlin/io/github/taetae98coding/diary/core/holiday/database/memory/HolidayMemoryDao.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.taetae98coding.diary.core.holiday.database.memory - -import io.github.taetae98coding.diary.core.holiday.database.HolidayDao -import io.github.taetae98coding.diary.core.model.holiday.Holiday -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.datetime.Month - -internal data object HolidayMemoryDao : HolidayDao { - private val flow = mutableMapOf, MutableStateFlow>>() - - override fun findHoliday(year: Int, month: Month): Flow> { - return getFlow(year, month).asStateFlow() - } - - override suspend fun upsert(year: Int, month: Month, holidayList: List) { - getFlow(year, month).emit(holidayList) - } - - private fun getFlow(year: Int, month: Month): MutableStateFlow> { - return flow.getOrPut(year to month) { MutableStateFlow(emptyList()) } - } -} diff --git a/app/core/holiday-database-memory/src/commonMain/kotlin/io/github/taetae98coding/diary/core/holiday/database/memory/HolidayMemoryDatabaseModule.kt b/app/core/holiday-database-memory/src/commonMain/kotlin/io/github/taetae98coding/diary/core/holiday/database/memory/HolidayMemoryDatabaseModule.kt deleted file mode 100644 index c266c176..00000000 --- a/app/core/holiday-database-memory/src/commonMain/kotlin/io/github/taetae98coding/diary/core/holiday/database/memory/HolidayMemoryDatabaseModule.kt +++ /dev/null @@ -1,15 +0,0 @@ -package io.github.taetae98coding.diary.core.holiday.database.memory - -import io.github.taetae98coding.diary.core.holiday.database.HolidayDao -import org.koin.core.annotation.ComponentScan -import org.koin.core.annotation.Module -import org.koin.core.annotation.Singleton - -@Module -@ComponentScan -public class HolidayMemoryDatabaseModule { - @Singleton - internal fun providesHolidayDao(): HolidayDao { - return HolidayMemoryDao - } -} diff --git a/app/core/holiday-preferences-memory/README.md b/app/core/holiday-preferences-memory/README.md deleted file mode 100644 index cb35719b..00000000 --- a/app/core/holiday-preferences-memory/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# :app:core:holiday-preferences-memory module -## Dependency graph -![Dependency graph](../../../docs/images/graphs/dep_graph_app_core_holiday_preferences_memory.svg) diff --git a/app/core/holiday-preferences-memory/build.gradle.kts b/app/core/holiday-preferences-memory/build.gradle.kts deleted file mode 100644 index d52d2421..00000000 --- a/app/core/holiday-preferences-memory/build.gradle.kts +++ /dev/null @@ -1,19 +0,0 @@ -plugins { - id("diary.android.library") - id("diary.kotlin.multiplatform.all") - id("diary.koin.all") -} - -kotlin { - sourceSets { - commonMain { - dependencies { - implementation(project(":app:core:holiday-preferences")) - } - } - } -} - -android { - namespace = "${Build.NAMESPACE}.core.holiday.preferences.memory" -} diff --git a/app/core/holiday-preferences-memory/src/commonMain/kotlin/io/github/taetae98coding/diary/core/holiday/preferences/memory/HolidayMemoryPreferences.kt b/app/core/holiday-preferences-memory/src/commonMain/kotlin/io/github/taetae98coding/diary/core/holiday/preferences/memory/HolidayMemoryPreferences.kt deleted file mode 100644 index 3a433583..00000000 --- a/app/core/holiday-preferences-memory/src/commonMain/kotlin/io/github/taetae98coding/diary/core/holiday/preferences/memory/HolidayMemoryPreferences.kt +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.taetae98coding.diary.core.holiday.preferences.memory - -import io.github.taetae98coding.diary.core.holiday.preferences.HolidayPreferences -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.datetime.Month - -internal data object HolidayMemoryPreferences : HolidayPreferences{ - private val map = mutableMapOf, MutableStateFlow>() - - override fun isDirty(year: Int, month: Month): Flow { - return getFlow(year, month).asStateFlow() - } - - override suspend fun setDirty(year: Int, month: Month) { - getFlow(year, month).emit(true) - } - - private fun getFlow(year: Int, month: Month): MutableStateFlow { - return map.getOrPut(year to month) { MutableStateFlow(false) } - } -} diff --git a/app/core/holiday-preferences-memory/src/commonMain/kotlin/io/github/taetae98coding/diary/core/holiday/preferences/memory/HolidayPreferencesMemoryModule.kt b/app/core/holiday-preferences-memory/src/commonMain/kotlin/io/github/taetae98coding/diary/core/holiday/preferences/memory/HolidayPreferencesMemoryModule.kt deleted file mode 100644 index 7be84278..00000000 --- a/app/core/holiday-preferences-memory/src/commonMain/kotlin/io/github/taetae98coding/diary/core/holiday/preferences/memory/HolidayPreferencesMemoryModule.kt +++ /dev/null @@ -1,15 +0,0 @@ -package io.github.taetae98coding.diary.core.holiday.preferences.memory - -import io.github.taetae98coding.diary.core.holiday.preferences.HolidayPreferences -import org.koin.core.annotation.ComponentScan -import org.koin.core.annotation.Module -import org.koin.core.annotation.Singleton - -@Module -@ComponentScan -public class HolidayPreferencesMemoryModule { - @Singleton - internal fun providesHolidayPreferences(): HolidayPreferences { - return HolidayMemoryPreferences - } -} diff --git a/app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/mapper/MemoMapper.kt b/app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/mapper/MemoMapper.kt index 1dae2193..24e89282 100644 --- a/app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/mapper/MemoMapper.kt +++ b/app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/mapper/MemoMapper.kt @@ -3,11 +3,25 @@ package io.github.taetae98coding.diary.core.model.mapper import io.github.taetae98coding.diary.core.model.memo.Memo import io.github.taetae98coding.diary.core.model.memo.MemoDto +public fun Memo.toDto(): MemoDto { + return MemoDto( + id = id, + detail = detail, + owner = owner, + primaryTag = primaryTag, + isFinish = isFinish, + isDelete = isDelete, + updateAt = updateAt, + serverUpdateAt = null, + ) +} + public fun MemoDto.toMemo(): Memo { return Memo( id = id, detail = detail, owner = owner, + primaryTag = primaryTag, isFinish = isFinish, isDelete = isDelete, updateAt = updateAt, diff --git a/app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/mapper/TagMapper.kt b/app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/mapper/TagMapper.kt new file mode 100644 index 00000000..cae225b4 --- /dev/null +++ b/app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/mapper/TagMapper.kt @@ -0,0 +1,27 @@ +package io.github.taetae98coding.diary.core.model.mapper + +import io.github.taetae98coding.diary.core.model.tag.Tag +import io.github.taetae98coding.diary.core.model.tag.TagDto + +public fun Tag.toDto(): TagDto { + return TagDto( + id = id, + detail = detail, + owner = owner, + isFinish = isFinish, + isDelete = isDelete, + updateAt = updateAt, + serverUpdateAt = null, + ) +} + +public fun TagDto.toTag(): Tag { + return Tag( + id = id, + detail = detail, + owner = owner, + isFinish = isFinish, + isDelete = isDelete, + updateAt = updateAt, + ) +} diff --git a/app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/memo/Memo.kt b/app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/memo/Memo.kt index a13794e6..ca9fdaac 100644 --- a/app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/memo/Memo.kt +++ b/app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/memo/Memo.kt @@ -5,6 +5,7 @@ import kotlinx.datetime.Instant public data class Memo( val id: String, val detail: MemoDetail, + val primaryTag: String?, val owner: String?, val isFinish: Boolean, val isDelete: Boolean, diff --git a/app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/memo/MemoAndTagIds.kt b/app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/memo/MemoAndTagIds.kt new file mode 100644 index 00000000..d2508360 --- /dev/null +++ b/app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/memo/MemoAndTagIds.kt @@ -0,0 +1,6 @@ +package io.github.taetae98coding.diary.core.model.memo + +public data class MemoAndTagIds( + val memo: MemoDto, + val tagIds: Set +) diff --git a/app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/memo/MemoDto.kt b/app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/memo/MemoDto.kt index 1b53f6a4..40469428 100644 --- a/app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/memo/MemoDto.kt +++ b/app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/memo/MemoDto.kt @@ -6,6 +6,7 @@ public data class MemoDto( val id: String, val detail: MemoDetail, val owner: String?, + val primaryTag: String?, val isFinish: Boolean, val isDelete: Boolean, val updateAt: Instant, diff --git a/app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/memo/MemoTag.kt b/app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/memo/MemoTag.kt new file mode 100644 index 00000000..9960eab5 --- /dev/null +++ b/app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/memo/MemoTag.kt @@ -0,0 +1,10 @@ +package io.github.taetae98coding.diary.core.model.memo + +import kotlinx.datetime.Instant + +public data class MemoTag( + val memoId: String, + val tagId: String, + val isSelected: Boolean, + val updateAt: Instant, +) diff --git a/app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/memo/MemoTagDto.kt b/app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/memo/MemoTagDto.kt new file mode 100644 index 00000000..e5ba9fcc --- /dev/null +++ b/app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/memo/MemoTagDto.kt @@ -0,0 +1,10 @@ +package io.github.taetae98coding.diary.core.model.memo + +import kotlinx.datetime.Instant + +public data class MemoTagDto( + val memoId: String, + val tagId: String, + val isSelected: Boolean, + val updateAt: Instant, +) diff --git a/app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/tag/Tag.kt b/app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/tag/Tag.kt new file mode 100644 index 00000000..95b2bcbf --- /dev/null +++ b/app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/tag/Tag.kt @@ -0,0 +1,12 @@ +package io.github.taetae98coding.diary.core.model.tag + +import kotlinx.datetime.Instant + +public data class Tag( + val id: String, + val detail: TagDetail, + val owner: String?, + val isFinish: Boolean, + val isDelete: Boolean, + val updateAt: Instant, +) diff --git a/app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/tag/TagDetail.kt b/app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/tag/TagDetail.kt new file mode 100644 index 00000000..bc03abb2 --- /dev/null +++ b/app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/tag/TagDetail.kt @@ -0,0 +1,7 @@ +package io.github.taetae98coding.diary.core.model.tag + +public data class TagDetail( + val title: String, + val description: String, + val color: Int, +) diff --git a/app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/tag/TagDto.kt b/app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/tag/TagDto.kt new file mode 100644 index 00000000..4d1b23af --- /dev/null +++ b/app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/tag/TagDto.kt @@ -0,0 +1,13 @@ +package io.github.taetae98coding.diary.core.model.tag + +import kotlinx.datetime.Instant + +public data class TagDto( + val id: String, + val detail: TagDetail, + val owner: String?, + val isFinish: Boolean, + val isDelete: Boolean, + val updateAt: Instant, + val serverUpdateAt: Instant?, +) diff --git a/app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/tag/TagWithPrimary.kt b/app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/tag/TagWithPrimary.kt new file mode 100644 index 00000000..5831594e --- /dev/null +++ b/app/core/model/src/commonMain/kotlin/io/github/taetae98coding/diary/core/model/tag/TagWithPrimary.kt @@ -0,0 +1,6 @@ +package io.github.taetae98coding.diary.core.model.tag + +public data class TagWithPrimary( + val tag: Tag, + val isPrimary: Boolean, +) diff --git a/app/core/navigation/src/commonMain/kotlin/io/github/taetae98coding/diary/core/navigation/memo/MemoDetailDestination.kt b/app/core/navigation/src/commonMain/kotlin/io/github/taetae98coding/diary/core/navigation/memo/MemoDetailDestination.kt index fc6b6b1d..cb4367bb 100644 --- a/app/core/navigation/src/commonMain/kotlin/io/github/taetae98coding/diary/core/navigation/memo/MemoDetailDestination.kt +++ b/app/core/navigation/src/commonMain/kotlin/io/github/taetae98coding/diary/core/navigation/memo/MemoDetailDestination.kt @@ -5,6 +5,10 @@ import kotlinx.serialization.Serializable @Serializable public data class MemoDetailDestination( - @SerialName("memoId") + @SerialName(MEMO_ID) val memoId: String, -) +) { + public companion object { + public const val MEMO_ID: String = "memoId" + } +} diff --git a/app/core/navigation/src/commonMain/kotlin/io/github/taetae98coding/diary/core/navigation/tag/TagAddDestination.kt b/app/core/navigation/src/commonMain/kotlin/io/github/taetae98coding/diary/core/navigation/tag/TagAddDestination.kt new file mode 100644 index 00000000..8d91bacf --- /dev/null +++ b/app/core/navigation/src/commonMain/kotlin/io/github/taetae98coding/diary/core/navigation/tag/TagAddDestination.kt @@ -0,0 +1,6 @@ +package io.github.taetae98coding.diary.core.navigation.tag + +import kotlinx.serialization.Serializable + +@Serializable +public data object TagAddDestination diff --git a/app/core/navigation/src/commonMain/kotlin/io/github/taetae98coding/diary/core/navigation/tag/TagDestination.kt b/app/core/navigation/src/commonMain/kotlin/io/github/taetae98coding/diary/core/navigation/tag/TagDestination.kt new file mode 100644 index 00000000..4f7be878 --- /dev/null +++ b/app/core/navigation/src/commonMain/kotlin/io/github/taetae98coding/diary/core/navigation/tag/TagDestination.kt @@ -0,0 +1,6 @@ +package io.github.taetae98coding.diary.core.navigation.tag + +import kotlinx.serialization.Serializable + +@Serializable +public data object TagDestination diff --git a/app/core/navigation/src/commonMain/kotlin/io/github/taetae98coding/diary/core/navigation/tag/TagDetailDestination.kt b/app/core/navigation/src/commonMain/kotlin/io/github/taetae98coding/diary/core/navigation/tag/TagDetailDestination.kt new file mode 100644 index 00000000..54b1cce3 --- /dev/null +++ b/app/core/navigation/src/commonMain/kotlin/io/github/taetae98coding/diary/core/navigation/tag/TagDetailDestination.kt @@ -0,0 +1,14 @@ +package io.github.taetae98coding.diary.core.navigation.tag + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +public data class TagDetailDestination( + @SerialName(TAG_ID) + val tagId: String, +) { + public companion object { + public const val TAG_ID: String = "tagId" + } +} diff --git a/app/core/resources/build.gradle.kts b/app/core/resources/build.gradle.kts deleted file mode 100644 index 91e19841..00000000 --- a/app/core/resources/build.gradle.kts +++ /dev/null @@ -1,28 +0,0 @@ -plugins { - id("diary.android.library") - id("diary.kotlin.multiplatform.all") - id("diary.compose") -} - -kotlin { - sourceSets { - commonMain { - dependencies { - implementation(compose.material3) - implementation(compose.materialIconsExtended) - api(compose.components.resources) - } - } - } -} - -compose { - resources { - publicResClass = true - packageOfResClass = "${Build.NAMESPACE}.core.resources" - } -} - -android { - namespace = "${Build.NAMESPACE}.core.resouces" -} diff --git a/app/core/resources/src/commonMain/composeResources/values-ko/strings.xml b/app/core/resources/src/commonMain/composeResources/values-ko/strings.xml deleted file mode 100644 index 67d216a0..00000000 --- a/app/core/resources/src/commonMain/composeResources/values-ko/strings.xml +++ /dev/null @@ -1,47 +0,0 @@ - - 메모 - 메모 추가 - 캘린더 - 더보기 - 게스트 - 회원가입 - 로그인 - 로그아웃 - 이메일 - 비밀번호 - 선택 - 닫기 - 제목 - 설명 - 날짜 - - %1$d년 %2$d월 - %1$d월 %2$d일 - - 로그인 - 회원가입 - - 이메일을 입력해 주세요 - 비밀번호를 입력해 주세요 - 이메일 형식을 지켜주세요 🫠 - 입력된 패스워드가 달라요 😵‍💫 - - 이미 존재하는 이메일입니다 🥲 - - 비밀번호 확인 - - 메모 추가 - - 제목을 입력해 주세요 🥲 - 계정을 찾을 수 없습니다 🧐 - 네트워크 연결 상태를 확인해 주세요 🤔 - 알 수 없는 에러가 발생했습니다 잠시 후 시도해 주세요 🙀 - - - - - - - - - diff --git a/app/core/resources/src/commonMain/composeResources/values/strings.xml b/app/core/resources/src/commonMain/composeResources/values/strings.xml deleted file mode 100644 index 837fae89..00000000 --- a/app/core/resources/src/commonMain/composeResources/values/strings.xml +++ /dev/null @@ -1,47 +0,0 @@ - - Memo - Memo Add - Calendar - More - Guest - Join - Login - Logout - Email - Password - Select - Dismiss - Title - Description - Date - - %1$d. %2$d. - %1$d. %2$d. - - Login - Join - - The email is blank - The password is blank - Please keep the email format - The password is different - - Exist email - - Check Password - - Memo add - - Title is blank - Account Not Found - Network Error - Unknown Error - - Sun - Mon - Tue - Wed - Thu - Fri - Sat - diff --git a/app/data/backup/build.gradle.kts b/app/data/backup/build.gradle.kts index f096bfbd..3a40ed48 100644 --- a/app/data/backup/build.gradle.kts +++ b/app/data/backup/build.gradle.kts @@ -6,6 +6,7 @@ kotlin { sourceSets { commonMain { dependencies { + implementation(project(":app:core:backup-database")) implementation(project(":app:core:diary-database")) implementation(project(":app:core:diary-service")) implementation(project(":app:domain:backup")) diff --git a/app/data/backup/src/commonMain/kotlin/io/github/taetae98coding/diary/data/backup/repository/BackupRepositoryImpl.kt b/app/data/backup/src/commonMain/kotlin/io/github/taetae98coding/diary/data/backup/repository/BackupRepositoryImpl.kt deleted file mode 100644 index 0e6cb91b..00000000 --- a/app/data/backup/src/commonMain/kotlin/io/github/taetae98coding/diary/data/backup/repository/BackupRepositoryImpl.kt +++ /dev/null @@ -1,29 +0,0 @@ -package io.github.taetae98coding.diary.data.backup.repository - -import io.github.taetae98coding.diary.core.diary.database.MemoBackupDao -import io.github.taetae98coding.diary.core.diary.service.memo.MemoService -import io.github.taetae98coding.diary.core.model.mapper.toMemo -import io.github.taetae98coding.diary.core.model.memo.MemoDto -import io.github.taetae98coding.diary.domain.backup.repository.BackupRepository -import kotlinx.coroutines.flow.first -import org.koin.core.annotation.Factory - -@Factory -internal class BackupRepositoryImpl( - private val memoBackupDao: MemoBackupDao, - private val memoService: MemoService, -) : BackupRepository { - override suspend fun backup(uid: String) { - while (memoBackupDao.countByUid(uid).first() > 0) { - val memoList = memoBackupDao.findByUid(uid).first() - .map(MemoDto::toMemo) - - memoService.upsert(memoList) - memoBackupDao.deleteByMemoIds(memoList.map { it.id }) - } - } - - override suspend fun upsertMemoBackupQueue(uid: String, memoId: String) { - memoBackupDao.upsert(uid, memoId) - } -} diff --git a/app/data/backup/src/commonMain/kotlin/io/github/taetae98coding/diary/data/backup/repository/MemoBackupRepositoryImpl.kt b/app/data/backup/src/commonMain/kotlin/io/github/taetae98coding/diary/data/backup/repository/MemoBackupRepositoryImpl.kt new file mode 100644 index 00000000..3787e95c --- /dev/null +++ b/app/data/backup/src/commonMain/kotlin/io/github/taetae98coding/diary/data/backup/repository/MemoBackupRepositoryImpl.kt @@ -0,0 +1,39 @@ +package io.github.taetae98coding.diary.data.backup.repository + +import io.github.taetae98coding.diary.core.backup.database.MemoBackupDao +import io.github.taetae98coding.diary.core.diary.database.MemoDao +import io.github.taetae98coding.diary.core.diary.service.memo.MemoService +import io.github.taetae98coding.diary.domain.backup.repository.MemoBackupRepository +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import org.koin.core.annotation.Factory + +@Factory +internal class MemoBackupRepositoryImpl( + private val memoDao: MemoDao, + private val memoBackupDao: MemoBackupDao, + private val memoService: MemoService, +) : MemoBackupRepository { + override suspend fun backup(uid: String) { + mutex.withLock { + while (true) { + val ids = memoBackupDao.findMemoIdByUid(uid).first() + if (ids.isEmpty()) { + break + } + + memoService.upsert(memoDao.findMemoAndTagIdsByIds(ids.toSet()).first()) + memoBackupDao.delete(ids.toSet()) + } + } + } + + override suspend fun upsertBackupQueue(uid: String, id: String) { + memoBackupDao.upsert(uid, id) + } + + companion object { + private val mutex = Mutex() + } +} diff --git a/app/data/backup/src/commonMain/kotlin/io/github/taetae98coding/diary/data/backup/repository/TagBackupRepositoryImpl.kt b/app/data/backup/src/commonMain/kotlin/io/github/taetae98coding/diary/data/backup/repository/TagBackupRepositoryImpl.kt new file mode 100644 index 00000000..dfaafa78 --- /dev/null +++ b/app/data/backup/src/commonMain/kotlin/io/github/taetae98coding/diary/data/backup/repository/TagBackupRepositoryImpl.kt @@ -0,0 +1,39 @@ +package io.github.taetae98coding.diary.data.backup.repository + +import io.github.taetae98coding.diary.core.backup.database.TagBackupDao +import io.github.taetae98coding.diary.core.diary.database.TagDao +import io.github.taetae98coding.diary.core.diary.service.memo.TagService +import io.github.taetae98coding.diary.domain.backup.repository.TagBackupRepository +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import org.koin.core.annotation.Factory + +@Factory +internal class TagBackupRepositoryImpl( + private val tagDao: TagDao, + private val tagBackupDao: TagBackupDao, + private val tagService: TagService, +) : TagBackupRepository { + override suspend fun backup(uid: String) { + mutex.withLock { + while (true) { + val ids = tagBackupDao.findByUid(uid).first() + if (ids.isEmpty()) { + break + } + + tagService.upsert(tagDao.findByIds(ids.toSet()).first()) + tagBackupDao.delete(ids.toSet()) + } + } + } + + override suspend fun upsertBackupQueue(uid: String, id: String) { + tagBackupDao.upsert(uid, id) + } + + companion object { + private val mutex = Mutex() + } +} diff --git a/app/data/fetch/src/commonMain/kotlin/io/github/taetae98coding/diary/data/fetch/repository/FetchRepositoryImpl.kt b/app/data/fetch/src/commonMain/kotlin/io/github/taetae98coding/diary/data/fetch/repository/FetchRepositoryImpl.kt deleted file mode 100644 index 33228ece..00000000 --- a/app/data/fetch/src/commonMain/kotlin/io/github/taetae98coding/diary/data/fetch/repository/FetchRepositoryImpl.kt +++ /dev/null @@ -1,29 +0,0 @@ -package io.github.taetae98coding.diary.data.fetch.repository - -import io.github.taetae98coding.diary.core.diary.database.MemoDao -import io.github.taetae98coding.diary.core.diary.service.memo.MemoService -import io.github.taetae98coding.diary.domain.fetch.repository.FetchRepository -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.first -import kotlinx.datetime.Instant -import org.koin.core.annotation.Factory - -@Factory -internal class FetchRepositoryImpl( - private val memoDao: MemoDao, - private val memoService: MemoService, -) : FetchRepository { - override suspend fun fetchMemo(uid: String) { - while (true) { - val updateAt = memoDao.getLastServerUpdateAt(uid).first() ?: Instant.fromEpochMilliseconds(0L) - val memoList = memoService.fetch(updateAt) - - if (memoList.isEmpty()) { - break - } - - memoDao.upsert(memoList) - delay(2000L) - } - } -} diff --git a/app/data/fetch/src/commonMain/kotlin/io/github/taetae98coding/diary/data/fetch/repository/MemoFetchRepositoryImpl.kt b/app/data/fetch/src/commonMain/kotlin/io/github/taetae98coding/diary/data/fetch/repository/MemoFetchRepositoryImpl.kt new file mode 100644 index 00000000..888dc3d7 --- /dev/null +++ b/app/data/fetch/src/commonMain/kotlin/io/github/taetae98coding/diary/data/fetch/repository/MemoFetchRepositoryImpl.kt @@ -0,0 +1,35 @@ +package io.github.taetae98coding.diary.data.fetch.repository + +import io.github.taetae98coding.diary.core.diary.database.MemoDao +import io.github.taetae98coding.diary.core.diary.service.memo.MemoService +import io.github.taetae98coding.diary.domain.fetch.repository.MemoFetchRepository +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import kotlinx.datetime.Instant +import org.koin.core.annotation.Factory + +@Factory +internal class MemoFetchRepositoryImpl( + private val localDataSource: MemoDao, + private val remoteDataSource: MemoService, +) : MemoFetchRepository { + override suspend fun fetch(uid: String) { + mutex.withLock { + while (true) { + val updateAt = localDataSource.getLastServerUpdateAt(uid).first() ?: Instant.fromEpochMilliseconds(0L) + val list = remoteDataSource.fetch(updateAt) + + if (list.isEmpty()) { + break + } + + localDataSource.upsert(list) + } + } + } + + companion object { + private val mutex = Mutex() + } +} diff --git a/app/data/fetch/src/commonMain/kotlin/io/github/taetae98coding/diary/data/fetch/repository/TagFetchRepositoryImpl.kt b/app/data/fetch/src/commonMain/kotlin/io/github/taetae98coding/diary/data/fetch/repository/TagFetchRepositoryImpl.kt new file mode 100644 index 00000000..e49d4e39 --- /dev/null +++ b/app/data/fetch/src/commonMain/kotlin/io/github/taetae98coding/diary/data/fetch/repository/TagFetchRepositoryImpl.kt @@ -0,0 +1,35 @@ +package io.github.taetae98coding.diary.data.fetch.repository + +import io.github.taetae98coding.diary.core.diary.database.TagDao +import io.github.taetae98coding.diary.core.diary.service.memo.TagService +import io.github.taetae98coding.diary.domain.fetch.repository.TagFetchRepository +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import kotlinx.datetime.Instant +import org.koin.core.annotation.Factory + +@Factory +internal class TagFetchRepositoryImpl( + private val localDataSource: TagDao, + private val remoteDataSource: TagService, +) : TagFetchRepository { + override suspend fun fetch(uid: String) { + mutex.withLock { + while (true) { + val updateAt = localDataSource.getLastServerUpdateAt(uid).first() ?: Instant.fromEpochMilliseconds(0L) + val list = remoteDataSource.fetch(updateAt) + + if (list.isEmpty()) { + break + } + + localDataSource.upsert(list) + } + } + } + + companion object { + private val mutex = Mutex() + } +} diff --git a/app/data/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/data/memo/repository/MemoRepositoryImpl.kt b/app/data/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/data/memo/repository/MemoRepositoryImpl.kt index 334c4141..2b3856cf 100644 --- a/app/data/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/data/memo/repository/MemoRepositoryImpl.kt +++ b/app/data/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/data/memo/repository/MemoRepositoryImpl.kt @@ -1,9 +1,10 @@ package io.github.taetae98coding.diary.data.memo.repository -import io.github.taetae98coding.diary.core.diary.database.MemoBackupDao import io.github.taetae98coding.diary.core.diary.database.MemoDao +import io.github.taetae98coding.diary.core.model.mapper.toDto import io.github.taetae98coding.diary.core.model.mapper.toMemo import io.github.taetae98coding.diary.core.model.memo.Memo +import io.github.taetae98coding.diary.core.model.memo.MemoAndTagIds import io.github.taetae98coding.diary.core.model.memo.MemoDetail import io.github.taetae98coding.diary.core.model.memo.MemoDto import io.github.taetae98coding.diary.domain.memo.repository.MemoRepository @@ -18,26 +19,19 @@ import org.koin.core.annotation.Factory @Factory internal class MemoRepositoryImpl( private val localDataSource: MemoDao, - private val backupDataSource: MemoBackupDao, ) : MemoRepository { - override suspend fun upsert(memo: Memo) { - val dto = MemoDto( - id = memo.id, - detail = memo.detail, - owner = memo.owner, - isFinish = memo.isFinish, - isDelete = memo.isDelete, - updateAt = memo.updateAt, - serverUpdateAt = null, - ) - - localDataSource.upsert(dto) + override suspend fun upsert(memo: Memo, tagIds: Set) { + localDataSource.upsert(MemoAndTagIds(memo.toDto(), tagIds)) } override suspend fun update(memoId: String, detail: MemoDetail) { localDataSource.update(memoId, detail) } + override suspend fun updatePrimaryTag(memoId: String, tagId: String?) { + localDataSource.updatePrimaryTag(memoId, tagId) + } + override suspend fun updateFinish(memoId: String, isFinish: Boolean) { localDataSource.updateFinish(memoId, isFinish) } diff --git a/app/data/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/data/memo/repository/MemoTagRepositoryImpl.kt b/app/data/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/data/memo/repository/MemoTagRepositoryImpl.kt new file mode 100644 index 00000000..84017470 --- /dev/null +++ b/app/data/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/data/memo/repository/MemoTagRepositoryImpl.kt @@ -0,0 +1,28 @@ +package io.github.taetae98coding.diary.data.memo.repository + +import io.github.taetae98coding.diary.core.diary.database.MemoTagDao +import io.github.taetae98coding.diary.core.model.mapper.toTag +import io.github.taetae98coding.diary.core.model.tag.Tag +import io.github.taetae98coding.diary.core.model.tag.TagDto +import io.github.taetae98coding.diary.domain.memo.repository.MemoTagRepository +import io.github.taetae98coding.diary.library.coroutines.mapCollectionLatest +import kotlinx.coroutines.flow.Flow +import org.koin.core.annotation.Factory + +@Factory +internal class MemoTagRepositoryImpl( + private val localDataSource: MemoTagDao, +) : MemoTagRepository { + override fun findByMemoId(memoId: String): Flow> { + return localDataSource.findByMemoId(memoId) + .mapCollectionLatest(TagDto::toTag) + } + + override suspend fun upsert(memoId: String, tagId: String) { + localDataSource.upsert(memoId, tagId) + } + + override suspend fun delete(memoId: String, tagId: String) { + localDataSource.delete(memoId, tagId) + } +} diff --git a/app/data/tag/README.md b/app/data/tag/README.md new file mode 100644 index 00000000..03ba97f3 --- /dev/null +++ b/app/data/tag/README.md @@ -0,0 +1,3 @@ +# :app:data:tag module +## Dependency graph +![Dependency graph](../../../docs/images/graphs/dep_graph_app_data_tag.svg) diff --git a/app/data/tag/build.gradle.kts b/app/data/tag/build.gradle.kts new file mode 100644 index 00000000..14641c87 --- /dev/null +++ b/app/data/tag/build.gradle.kts @@ -0,0 +1,14 @@ +plugins { + id("diary.app.data") +} + +kotlin { + sourceSets { + commonMain { + dependencies { + implementation(project(":app:core:diary-database")) + implementation(project(":app:domain:tag")) + } + } + } +} diff --git a/app/core/diary-database-memory/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/memory/DiaryMemoryDatabaseModule.kt b/app/data/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/data/tag/TagDataModule.kt similarity index 51% rename from app/core/diary-database-memory/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/memory/DiaryMemoryDatabaseModule.kt rename to app/data/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/data/tag/TagDataModule.kt index 5e832edf..e7d81c0a 100644 --- a/app/core/diary-database-memory/src/commonMain/kotlin/io/github/taetae98coding/diary/core/diary/database/memory/DiaryMemoryDatabaseModule.kt +++ b/app/data/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/data/tag/TagDataModule.kt @@ -1,8 +1,8 @@ -package io.github.taetae98coding.diary.core.diary.database.memory +package io.github.taetae98coding.diary.data.tag import org.koin.core.annotation.ComponentScan import org.koin.core.annotation.Module @Module @ComponentScan -public class DiaryMemoryDatabaseModule +public class TagDataModule diff --git a/app/data/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/data/tag/repository/TagRepositoryImpl.kt b/app/data/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/data/tag/repository/TagRepositoryImpl.kt new file mode 100644 index 00000000..6690b7c5 --- /dev/null +++ b/app/data/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/data/tag/repository/TagRepositoryImpl.kt @@ -0,0 +1,49 @@ +package io.github.taetae98coding.diary.data.tag.repository + +import io.github.taetae98coding.diary.core.diary.database.TagDao +import io.github.taetae98coding.diary.core.model.mapper.toDto +import io.github.taetae98coding.diary.core.model.mapper.toTag +import io.github.taetae98coding.diary.core.model.tag.Tag +import io.github.taetae98coding.diary.core.model.tag.TagDetail +import io.github.taetae98coding.diary.core.model.tag.TagDto +import io.github.taetae98coding.diary.domain.tag.repository.TagRepository +import io.github.taetae98coding.diary.library.coroutines.mapCollectionLatest +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.mapLatest +import org.koin.core.annotation.Factory + +@OptIn(ExperimentalCoroutinesApi::class) +@Factory +internal class TagRepositoryImpl( + private val localDataSource: TagDao, +) : TagRepository { + override suspend fun upsert(tag: Tag) { + localDataSource.upsert(tag.toDto()) + } + + override suspend fun update(tagId: String, detail: TagDetail) { + localDataSource.update(tagId, detail) + } + + override suspend fun updateDelete(tagId: String, isDelete: Boolean) { + localDataSource.updateDelete(tagId, isDelete) + } + + override suspend fun updateFinish(tagId: String, isFinish: Boolean) { + localDataSource.updateFinish(tagId, isFinish) + } + + override fun find(tagId: String): Flow { + return localDataSource.find(tagId).mapLatest { it?.toTag() } + } + + override fun findByIds(tagIds: Set): Flow> { + return localDataSource.findByIds(tagIds) + } + + override fun page(owner: String?): Flow> { + return localDataSource.page(owner) + .mapCollectionLatest(TagDto::toTag) + } +} diff --git a/app/domain/backup/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/backup/repository/BackupRepository.kt b/app/domain/backup/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/backup/repository/MemoBackupRepository.kt similarity index 50% rename from app/domain/backup/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/backup/repository/BackupRepository.kt rename to app/domain/backup/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/backup/repository/MemoBackupRepository.kt index 1bdd1c52..62b5fa61 100644 --- a/app/domain/backup/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/backup/repository/BackupRepository.kt +++ b/app/domain/backup/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/backup/repository/MemoBackupRepository.kt @@ -1,6 +1,6 @@ package io.github.taetae98coding.diary.domain.backup.repository -public interface BackupRepository { +public interface MemoBackupRepository { public suspend fun backup(uid: String) - public suspend fun upsertMemoBackupQueue(uid: String, memoId: String) + public suspend fun upsertBackupQueue(uid: String, id: String) } diff --git a/app/domain/backup/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/backup/repository/TagBackupRepository.kt b/app/domain/backup/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/backup/repository/TagBackupRepository.kt new file mode 100644 index 00000000..e46cab96 --- /dev/null +++ b/app/domain/backup/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/backup/repository/TagBackupRepository.kt @@ -0,0 +1,6 @@ +package io.github.taetae98coding.diary.domain.backup.repository + +public interface TagBackupRepository { + public suspend fun backup(uid: String) + public suspend fun upsertBackupQueue(uid: String, id: String) +} diff --git a/app/domain/backup/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/backup/usecase/BackupUseCase.kt b/app/domain/backup/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/backup/usecase/BackupUseCase.kt index 3b18bedb..a4422d1d 100644 --- a/app/domain/backup/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/backup/usecase/BackupUseCase.kt +++ b/app/domain/backup/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/backup/usecase/BackupUseCase.kt @@ -2,20 +2,23 @@ package io.github.taetae98coding.diary.domain.backup.usecase import io.github.taetae98coding.diary.core.model.account.Account import io.github.taetae98coding.diary.domain.account.usecase.GetAccountUseCase -import io.github.taetae98coding.diary.domain.backup.repository.BackupRepository +import io.github.taetae98coding.diary.domain.backup.repository.MemoBackupRepository +import io.github.taetae98coding.diary.domain.backup.repository.TagBackupRepository import kotlinx.coroutines.flow.first import org.koin.core.annotation.Factory @Factory public class BackupUseCase internal constructor( private val getAccountUseCase: GetAccountUseCase, - private val repository: BackupRepository, + private val tagBackupRepository: TagBackupRepository, + private val memoBackupRepository: MemoBackupRepository, ) { public suspend operator fun invoke(): Result { return runCatching { val account = getAccountUseCase().first().getOrThrow() if (account is Account.Member) { - repository.backup(account.uid) + tagBackupRepository.backup(account.uid) + memoBackupRepository.backup(account.uid) } } } diff --git a/app/domain/backup/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/backup/usecase/PushMemoBackupQueueUseCase.kt b/app/domain/backup/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/backup/usecase/PushMemoBackupQueueUseCase.kt new file mode 100644 index 00000000..bcb584d0 --- /dev/null +++ b/app/domain/backup/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/backup/usecase/PushMemoBackupQueueUseCase.kt @@ -0,0 +1,31 @@ +package io.github.taetae98coding.diary.domain.backup.usecase + +import io.github.taetae98coding.diary.core.model.account.Account +import io.github.taetae98coding.diary.domain.account.usecase.GetAccountUseCase +import io.github.taetae98coding.diary.domain.backup.repository.MemoBackupRepository +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch +import org.koin.core.annotation.Factory + +@Factory +public class PushMemoBackupQueueUseCase internal constructor( + private val getAccountUseCase: GetAccountUseCase, + private val backupUseCase: BackupUseCase, + private val coroutineScope: CoroutineScope, + private val repository: MemoBackupRepository, +) { + public suspend operator fun invoke(memoId: String?): Result { + return runCatching { + if (memoId.isNullOrBlank()) return@runCatching + + val account = getAccountUseCase().first().getOrThrow() + if (account is Account.Member) { + coroutineScope.launch { + repository.upsertBackupQueue(account.uid, memoId) + backupUseCase() + } + } + } + } +} diff --git a/app/domain/backup/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/backup/usecase/PushTagBackupQueueUseCase.kt b/app/domain/backup/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/backup/usecase/PushTagBackupQueueUseCase.kt new file mode 100644 index 00000000..48a78526 --- /dev/null +++ b/app/domain/backup/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/backup/usecase/PushTagBackupQueueUseCase.kt @@ -0,0 +1,31 @@ +package io.github.taetae98coding.diary.domain.backup.usecase + +import io.github.taetae98coding.diary.core.model.account.Account +import io.github.taetae98coding.diary.domain.account.usecase.GetAccountUseCase +import io.github.taetae98coding.diary.domain.backup.repository.TagBackupRepository +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch +import org.koin.core.annotation.Factory + +@Factory +public class PushTagBackupQueueUseCase internal constructor( + private val getAccountUseCase: GetAccountUseCase, + private val backupUseCase: BackupUseCase, + private val coroutineScope: CoroutineScope, + private val repository: TagBackupRepository, +) { + public suspend operator fun invoke(tagId: String?): Result { + return runCatching { + if (tagId.isNullOrBlank()) return@runCatching + + val account = getAccountUseCase().first().getOrThrow() + if (account is Account.Member) { + coroutineScope.launch { + repository.upsertBackupQueue(account.uid, tagId) + backupUseCase() + } + } + } + } +} diff --git a/app/domain/calendar/README.md b/app/domain/calendar/README.md new file mode 100644 index 00000000..d10d0af0 --- /dev/null +++ b/app/domain/calendar/README.md @@ -0,0 +1,3 @@ +# :app:domain:calendar module +## Dependency graph +![Dependency graph](../../../docs/images/graphs/dep_graph_app_domain_calendar.svg) diff --git a/app/domain/calendar/build.gradle.kts b/app/domain/calendar/build.gradle.kts new file mode 100644 index 00000000..34e96bf4 --- /dev/null +++ b/app/domain/calendar/build.gradle.kts @@ -0,0 +1,15 @@ +plugins { + id("diary.app.domain") +} + +kotlin { + sourceSets { + commonMain { + dependencies { + implementation(project(":app:domain:account")) + implementation(project(":app:domain:memo")) + implementation(project(":app:domain:tag")) + } + } + } +} diff --git a/app/domain/calendar/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/calendar/CalendarDomainModule.kt b/app/domain/calendar/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/calendar/CalendarDomainModule.kt new file mode 100644 index 00000000..a666ff93 --- /dev/null +++ b/app/domain/calendar/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/calendar/CalendarDomainModule.kt @@ -0,0 +1,8 @@ +package io.github.taetae98coding.diary.domain.calendar + +import org.koin.core.annotation.ComponentScan +import org.koin.core.annotation.Module + +@Module +@ComponentScan +public class CalendarDomainModule diff --git a/app/domain/calendar/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/calendar/usecase/FindCalendarMemoUseCase.kt b/app/domain/calendar/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/calendar/usecase/FindCalendarMemoUseCase.kt new file mode 100644 index 00000000..925e5977 --- /dev/null +++ b/app/domain/calendar/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/calendar/usecase/FindCalendarMemoUseCase.kt @@ -0,0 +1,50 @@ +package io.github.taetae98coding.diary.domain.calendar.usecase + +import io.github.taetae98coding.diary.core.model.memo.Memo +import io.github.taetae98coding.diary.domain.account.usecase.GetAccountUseCase +import io.github.taetae98coding.diary.domain.memo.repository.MemoRepository +import io.github.taetae98coding.diary.domain.tag.repository.TagRepository +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.emitAll +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.mapLatest +import kotlinx.datetime.LocalDate +import org.koin.core.annotation.Factory + +@OptIn(ExperimentalCoroutinesApi::class) +@Factory +public class FindCalendarMemoUseCase internal constructor( + private val getAccountUseCase: GetAccountUseCase, + private val memoRepository: MemoRepository, + private val tagRepository: TagRepository, +) { + public operator fun invoke(dateRange: ClosedRange): Flow>> { + return flow { + getAccountUseCase().mapLatest { it.getOrThrow() } + .flatMapLatest { memoRepository.findByDateRange(it.uid, dateRange) } + .flatMapLatest { memoList -> + val tagIdSet = memoList.mapNotNull { it.primaryTag }.toSet() + + tagRepository.findByIds(tagIdSet).mapLatest { tagList -> + memoList.map { memo -> + val color = tagList.find { it.id == memo.primaryTag } + ?.detail + ?.color + ?: memo.detail.color + + memo.copy(detail = memo.detail.copy(color = color)) + } + } + } + .also { emitAll(it) } + }.mapLatest { + Result.success(it) + }.catch { + it.printStackTrace() + emit(Result.failure(it)) + } + } +} diff --git a/app/domain/fetch/build.gradle.kts b/app/domain/fetch/build.gradle.kts index 84e3b1c2..7385bf5b 100644 --- a/app/domain/fetch/build.gradle.kts +++ b/app/domain/fetch/build.gradle.kts @@ -7,6 +7,7 @@ kotlin { commonMain { dependencies { implementation(project(":app:domain:account")) + implementation(project(":app:domain:backup")) } } } diff --git a/app/domain/fetch/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/fetch/repository/FetchRepository.kt b/app/domain/fetch/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/fetch/repository/FetchRepository.kt deleted file mode 100644 index 8d8b243c..00000000 --- a/app/domain/fetch/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/fetch/repository/FetchRepository.kt +++ /dev/null @@ -1,5 +0,0 @@ -package io.github.taetae98coding.diary.domain.fetch.repository - -public interface FetchRepository { - public suspend fun fetchMemo(uid: String) -} diff --git a/app/domain/fetch/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/fetch/repository/MemoFetchRepository.kt b/app/domain/fetch/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/fetch/repository/MemoFetchRepository.kt new file mode 100644 index 00000000..e7e63a7e --- /dev/null +++ b/app/domain/fetch/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/fetch/repository/MemoFetchRepository.kt @@ -0,0 +1,5 @@ +package io.github.taetae98coding.diary.domain.fetch.repository + +public interface MemoFetchRepository { + public suspend fun fetch(uid: String) +} diff --git a/app/domain/fetch/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/fetch/repository/TagFetchRepository.kt b/app/domain/fetch/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/fetch/repository/TagFetchRepository.kt new file mode 100644 index 00000000..003328a2 --- /dev/null +++ b/app/domain/fetch/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/fetch/repository/TagFetchRepository.kt @@ -0,0 +1,5 @@ +package io.github.taetae98coding.diary.domain.fetch.repository + +public interface TagFetchRepository { + public suspend fun fetch(uid: String) +} diff --git a/app/domain/fetch/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/fetch/usecase/FetchUseCase.kt b/app/domain/fetch/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/fetch/usecase/FetchUseCase.kt index 55acb067..0bb4557e 100644 --- a/app/domain/fetch/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/fetch/usecase/FetchUseCase.kt +++ b/app/domain/fetch/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/fetch/usecase/FetchUseCase.kt @@ -2,20 +2,26 @@ package io.github.taetae98coding.diary.domain.fetch.usecase import io.github.taetae98coding.diary.core.model.account.Account import io.github.taetae98coding.diary.domain.account.usecase.GetAccountUseCase -import io.github.taetae98coding.diary.domain.fetch.repository.FetchRepository +import io.github.taetae98coding.diary.domain.backup.usecase.BackupUseCase +import io.github.taetae98coding.diary.domain.fetch.repository.MemoFetchRepository +import io.github.taetae98coding.diary.domain.fetch.repository.TagFetchRepository import kotlinx.coroutines.flow.first import org.koin.core.annotation.Factory @Factory public class FetchUseCase internal constructor( private val getAccountUseCase: GetAccountUseCase, - private val repository: FetchRepository, + private val backupUseCase: BackupUseCase, + private val memoFetchRepository: MemoFetchRepository, + private val tagFetchRepository: TagFetchRepository ) { public suspend operator fun invoke(): Result { return runCatching { val account = getAccountUseCase().first().getOrThrow() if (account is Account.Member) { - repository.fetchMemo(account.uid) + backupUseCase() + tagFetchRepository.fetch(account.uid) + memoFetchRepository.fetch(account.uid) } } } diff --git a/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/repository/MemoRepository.kt b/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/repository/MemoRepository.kt index 240a8b0b..f88f1c03 100644 --- a/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/repository/MemoRepository.kt +++ b/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/repository/MemoRepository.kt @@ -6,8 +6,9 @@ import kotlinx.coroutines.flow.Flow import kotlinx.datetime.LocalDate public interface MemoRepository { - public suspend fun upsert(memo: Memo) + public suspend fun upsert(memo: Memo, tagIds: Set) public suspend fun update(memoId: String, detail: MemoDetail) + public suspend fun updatePrimaryTag(memoId: String, tagId: String?) public suspend fun updateFinish(memoId: String, isFinish: Boolean) public suspend fun updateDelete(memoId: String, isDelete: Boolean) diff --git a/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/repository/MemoTagRepository.kt b/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/repository/MemoTagRepository.kt new file mode 100644 index 00000000..06819d2f --- /dev/null +++ b/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/repository/MemoTagRepository.kt @@ -0,0 +1,11 @@ +package io.github.taetae98coding.diary.domain.memo.repository + +import io.github.taetae98coding.diary.core.model.tag.Tag +import kotlinx.coroutines.flow.Flow + +public interface MemoTagRepository { + public suspend fun upsert(memoId: String, tagId: String) + public suspend fun delete(memoId: String, tagId: String) + + public fun findByMemoId(memoId: String): Flow> +} diff --git a/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/AddMemoUseCase.kt b/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/AddMemoUseCase.kt index 8917b46b..bb445a94 100644 --- a/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/AddMemoUseCase.kt +++ b/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/AddMemoUseCase.kt @@ -1,55 +1,47 @@ package io.github.taetae98coding.diary.domain.memo.usecase import io.github.taetae98coding.diary.common.exception.memo.MemoTitleBlankException -import io.github.taetae98coding.diary.core.model.account.Account import io.github.taetae98coding.diary.core.model.memo.Memo import io.github.taetae98coding.diary.core.model.memo.MemoDetail import io.github.taetae98coding.diary.domain.account.usecase.GetAccountUseCase -import io.github.taetae98coding.diary.domain.backup.repository.BackupRepository -import io.github.taetae98coding.diary.domain.backup.usecase.BackupUseCase +import io.github.taetae98coding.diary.domain.backup.usecase.PushMemoBackupQueueUseCase import io.github.taetae98coding.diary.domain.memo.repository.MemoRepository import kotlin.uuid.ExperimentalUuidApi import kotlin.uuid.Uuid -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.first -import kotlinx.coroutines.launch import kotlinx.datetime.Clock import org.koin.core.annotation.Factory @OptIn(ExperimentalUuidApi::class) @Factory public class AddMemoUseCase internal constructor( - private val clock: Clock, private val getAccountUseCase: GetAccountUseCase, + private val pushMemoBackupQueueUseCase: PushMemoBackupQueueUseCase, + private val clock: Clock, private val repository: MemoRepository, - private val coroutineScope: CoroutineScope, - private val backupRepository: BackupRepository, - private val backupUseCase: BackupUseCase, ) { - public suspend operator fun invoke(detail: MemoDetail): Result { + public suspend operator fun invoke( + detail: MemoDetail, + primaryTag: String?, + tagIds: Set, + ): Result { return runCatching { if (detail.title.isBlank()) throw MemoTitleBlankException() + val memoId = Uuid.random().toString() val account = getAccountUseCase().first().getOrThrow() - val id = Uuid.random().toString() - val memo = Memo( - id = id, + id = memoId, detail = detail, + primaryTag = primaryTag, owner = account.uid, isFinish = false, isDelete = false, updateAt = clock.now(), ) - repository.upsert(memo) - - if (account is Account.Member) { - coroutineScope.launch { - backupRepository.upsertMemoBackupQueue(account.uid, id) - backupUseCase() - } - } + repository.upsert(memo, tagIds) + pushMemoBackupQueueUseCase(memoId) } } } diff --git a/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/DeleteMemoPrimaryTagUseCase.kt b/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/DeleteMemoPrimaryTagUseCase.kt new file mode 100644 index 00000000..e996c09c --- /dev/null +++ b/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/DeleteMemoPrimaryTagUseCase.kt @@ -0,0 +1,20 @@ +package io.github.taetae98coding.diary.domain.memo.usecase + +import io.github.taetae98coding.diary.domain.backup.usecase.PushMemoBackupQueueUseCase +import io.github.taetae98coding.diary.domain.memo.repository.MemoRepository +import org.koin.core.annotation.Factory + +@Factory +public class DeleteMemoPrimaryTagUseCase internal constructor( + private val pushMemoBackupQueueUseCase: PushMemoBackupQueueUseCase, + private val repository: MemoRepository, +) { + public suspend operator fun invoke(memoId: String?): Result { + return runCatching { + if (memoId.isNullOrBlank()) return@runCatching + + repository.updatePrimaryTag(memoId, null) + pushMemoBackupQueueUseCase(memoId) + } + } +} diff --git a/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/DeleteMemoUseCase.kt b/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/DeleteMemoUseCase.kt index 3b8cd293..33203003 100644 --- a/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/DeleteMemoUseCase.kt +++ b/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/DeleteMemoUseCase.kt @@ -1,35 +1,20 @@ package io.github.taetae98coding.diary.domain.memo.usecase -import io.github.taetae98coding.diary.core.model.account.Account -import io.github.taetae98coding.diary.domain.account.usecase.GetAccountUseCase -import io.github.taetae98coding.diary.domain.backup.repository.BackupRepository -import io.github.taetae98coding.diary.domain.backup.usecase.BackupUseCase +import io.github.taetae98coding.diary.domain.backup.usecase.PushMemoBackupQueueUseCase import io.github.taetae98coding.diary.domain.memo.repository.MemoRepository -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.launch import org.koin.core.annotation.Factory @Factory public class DeleteMemoUseCase internal constructor( - private val getAccountUseCase: GetAccountUseCase, + private val pushMemoBackupQueueUseCase: PushMemoBackupQueueUseCase, private val repository: MemoRepository, - private val coroutineScope: CoroutineScope, - private val backupRepository: BackupRepository, - private val backupUseCase: BackupUseCase, ) { public suspend operator fun invoke(memoId: String?): Result { return runCatching { if (memoId.isNullOrBlank()) return@runCatching - val account = getAccountUseCase().first().getOrThrow() repository.updateDelete(memoId, true) - if (account is Account.Member) { - coroutineScope.launch { - backupRepository.upsertMemoBackupQueue(account.uid, memoId) - backupUseCase() - } - } + pushMemoBackupQueueUseCase(memoId) } } } diff --git a/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/FindMemoTagUseCase.kt b/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/FindMemoTagUseCase.kt new file mode 100644 index 00000000..15b66f73 --- /dev/null +++ b/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/FindMemoTagUseCase.kt @@ -0,0 +1,46 @@ +package io.github.taetae98coding.diary.domain.memo.usecase + +import io.github.taetae98coding.diary.core.model.tag.TagWithPrimary +import io.github.taetae98coding.diary.domain.memo.repository.MemoTagRepository +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.emitAll +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.mapLatest +import org.koin.core.annotation.Factory + +@OptIn(ExperimentalCoroutinesApi::class) +@Factory +public class FindMemoTagUseCase internal constructor( + private val findMemoUseCase: FindMemoUseCase, + private val repository: MemoTagRepository, +) { + public operator fun invoke(memoId: String?): Flow>> { + if (memoId.isNullOrBlank()) return flowOf(Result.success(emptyList())) + + return flow { + val memoFlow = findMemoUseCase(memoId).mapLatest { it.getOrThrow() } + val tagListFlow = repository.findByMemoId(memoId) + + combine(memoFlow, tagListFlow) { memo, tagList -> + if (memo == null) return@combine emptyList() + + tagList.map { + TagWithPrimary( + tag = it, + isPrimary = memo.primaryTag == it.id + ) + } + }.also { + emitAll(it) + } + }.mapLatest { + Result.success(it) + }.catch { + emit(Result.failure(it)) + } + } +} diff --git a/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/FinishMemoUseCase.kt b/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/FinishMemoUseCase.kt index dcd3d87b..0175b866 100644 --- a/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/FinishMemoUseCase.kt +++ b/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/FinishMemoUseCase.kt @@ -1,35 +1,21 @@ package io.github.taetae98coding.diary.domain.memo.usecase -import io.github.taetae98coding.diary.core.model.account.Account import io.github.taetae98coding.diary.domain.account.usecase.GetAccountUseCase -import io.github.taetae98coding.diary.domain.backup.repository.BackupRepository -import io.github.taetae98coding.diary.domain.backup.usecase.BackupUseCase +import io.github.taetae98coding.diary.domain.backup.usecase.PushMemoBackupQueueUseCase import io.github.taetae98coding.diary.domain.memo.repository.MemoRepository -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.launch import org.koin.core.annotation.Factory @Factory public class FinishMemoUseCase internal constructor( - private val getAccountUseCase: GetAccountUseCase, private val repository: MemoRepository, - private val coroutineScope: CoroutineScope, - private val backupRepository: BackupRepository, - private val backupUseCase: BackupUseCase, + private val pushMemoBackupQueueUseCase: PushMemoBackupQueueUseCase, ) { public suspend operator fun invoke(memoId: String?): Result { return runCatching { if (memoId.isNullOrBlank()) return@runCatching - val account = getAccountUseCase().first().getOrThrow() repository.updateFinish(memoId, true) - if (account is Account.Member) { - coroutineScope.launch { - backupRepository.upsertMemoBackupQueue(account.uid, memoId) - backupUseCase() - } - } + pushMemoBackupQueueUseCase(memoId) } } } diff --git a/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/RestartMemoUseCase.kt b/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/RestartMemoUseCase.kt index 4bcdca0c..ff1d2193 100644 --- a/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/RestartMemoUseCase.kt +++ b/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/RestartMemoUseCase.kt @@ -1,36 +1,20 @@ package io.github.taetae98coding.diary.domain.memo.usecase -import io.github.taetae98coding.diary.core.model.account.Account -import io.github.taetae98coding.diary.domain.account.usecase.GetAccountUseCase -import io.github.taetae98coding.diary.domain.backup.repository.BackupRepository -import io.github.taetae98coding.diary.domain.backup.usecase.BackupUseCase +import io.github.taetae98coding.diary.domain.backup.usecase.PushMemoBackupQueueUseCase import io.github.taetae98coding.diary.domain.memo.repository.MemoRepository -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.launch import org.koin.core.annotation.Factory @Factory public class RestartMemoUseCase internal constructor( - private val getAccountUseCase: GetAccountUseCase, + private val pushMemoBackupQueueUseCase: PushMemoBackupQueueUseCase, private val repository: MemoRepository, - private val coroutineScope: CoroutineScope, - private val backupRepository: BackupRepository, - private val backupUseCase: BackupUseCase, ) { public suspend operator fun invoke(memoId: String?): Result { return runCatching { if (memoId.isNullOrBlank()) return@runCatching - val account = getAccountUseCase().first().getOrThrow() - repository.updateFinish(memoId, false) - if (account is Account.Member) { - coroutineScope.launch { - backupRepository.upsertMemoBackupQueue(account.uid, memoId) - backupUseCase() - } - } + pushMemoBackupQueueUseCase(memoId) } } } diff --git a/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/SelectMemoTagUseCase.kt b/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/SelectMemoTagUseCase.kt new file mode 100644 index 00000000..d3e60eb8 --- /dev/null +++ b/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/SelectMemoTagUseCase.kt @@ -0,0 +1,20 @@ +package io.github.taetae98coding.diary.domain.memo.usecase + +import io.github.taetae98coding.diary.domain.backup.usecase.PushMemoBackupQueueUseCase +import io.github.taetae98coding.diary.domain.memo.repository.MemoTagRepository +import org.koin.core.annotation.Factory + +@Factory +public class SelectMemoTagUseCase internal constructor( + private val pushMemoBackupQueueUseCase: PushMemoBackupQueueUseCase, + private val repository: MemoTagRepository, +) { + public suspend operator fun invoke(memoId: String?, tagId: String?): Result { + return runCatching { + if (memoId.isNullOrBlank() || tagId.isNullOrBlank()) return@runCatching + + repository.upsert(memoId, tagId) + pushMemoBackupQueueUseCase(memoId) + } + } +} diff --git a/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/UnselectMemoTagUseCase.kt b/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/UnselectMemoTagUseCase.kt new file mode 100644 index 00000000..c556db2c --- /dev/null +++ b/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/UnselectMemoTagUseCase.kt @@ -0,0 +1,29 @@ +package io.github.taetae98coding.diary.domain.memo.usecase + +import io.github.taetae98coding.diary.domain.backup.usecase.PushMemoBackupQueueUseCase +import io.github.taetae98coding.diary.domain.memo.repository.MemoRepository +import io.github.taetae98coding.diary.domain.memo.repository.MemoTagRepository +import kotlinx.coroutines.flow.first +import org.koin.core.annotation.Factory + +@Factory +public class UnselectMemoTagUseCase internal constructor( + private val findMemoUseCase: FindMemoUseCase, + private val pushMemoBackupQueueUseCase: PushMemoBackupQueueUseCase, + private val memoRepository: MemoRepository, + private val memoTagRepository: MemoTagRepository, +) { + public suspend operator fun invoke(memoId: String?, tagId: String?): Result { + return runCatching { + if (memoId.isNullOrBlank() || tagId.isNullOrBlank()) return@runCatching + + val memo = findMemoUseCase(memoId).first().getOrThrow() ?: return@runCatching + + memoTagRepository.delete(memoId, tagId) + if (memo.primaryTag == tagId) { + memoRepository.updatePrimaryTag(memoId, null) + } + pushMemoBackupQueueUseCase(memoId) + } + } +} diff --git a/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/UpdateMemoPrimaryTagUseCase.kt b/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/UpdateMemoPrimaryTagUseCase.kt new file mode 100644 index 00000000..246b6f09 --- /dev/null +++ b/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/UpdateMemoPrimaryTagUseCase.kt @@ -0,0 +1,20 @@ +package io.github.taetae98coding.diary.domain.memo.usecase + +import io.github.taetae98coding.diary.domain.backup.usecase.PushMemoBackupQueueUseCase +import io.github.taetae98coding.diary.domain.memo.repository.MemoRepository +import org.koin.core.annotation.Factory + +@Factory +public class UpdateMemoPrimaryTagUseCase internal constructor( + private val pushMemoBackupQueueUseCase: PushMemoBackupQueueUseCase, + private val repository: MemoRepository, +) { + public suspend operator fun invoke(memoId: String?, tagId: String?): Result { + return runCatching { + if (memoId.isNullOrBlank() || tagId.isNullOrBlank()) return@runCatching + + repository.updatePrimaryTag(memoId, tagId) + pushMemoBackupQueueUseCase(memoId) + } + } +} diff --git a/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/UpdateMemoUseCase.kt b/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/UpdateMemoUseCase.kt index de589368..6a7f32dc 100644 --- a/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/UpdateMemoUseCase.kt +++ b/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/UpdateMemoUseCase.kt @@ -1,41 +1,27 @@ package io.github.taetae98coding.diary.domain.memo.usecase -import io.github.taetae98coding.diary.core.model.account.Account import io.github.taetae98coding.diary.core.model.memo.MemoDetail -import io.github.taetae98coding.diary.domain.account.usecase.GetAccountUseCase -import io.github.taetae98coding.diary.domain.backup.repository.BackupRepository -import io.github.taetae98coding.diary.domain.backup.usecase.BackupUseCase +import io.github.taetae98coding.diary.domain.backup.usecase.PushMemoBackupQueueUseCase import io.github.taetae98coding.diary.domain.memo.repository.MemoRepository -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.first -import kotlinx.coroutines.launch import org.koin.core.annotation.Factory @Factory public class UpdateMemoUseCase internal constructor( - private val getAccountUseCase: GetAccountUseCase, + private val pushMemoBackupQueueUseCase: PushMemoBackupQueueUseCase, private val repository: MemoRepository, - private val coroutineScope: CoroutineScope, - private val backupRepository: BackupRepository, - private val backupUseCase: BackupUseCase, ) { public suspend operator fun invoke(memoId: String?, detail: MemoDetail): Result { return runCatching { if (memoId.isNullOrBlank()) return@runCatching val memo = repository.find(memoId).first() ?: return@runCatching - val account = getAccountUseCase().first().getOrThrow() val validDetail = detail.copy(title = detail.title.ifBlank { memo.detail.title }) if (memo.detail == validDetail) return@runCatching repository.update(memoId, validDetail) - if (account is Account.Member) { - coroutineScope.launch { - backupRepository.upsertMemoBackupQueue(account.uid, memoId) - backupUseCase() - } - } + pushMemoBackupQueueUseCase(memoId) } } } diff --git a/app/domain/tag/README.md b/app/domain/tag/README.md new file mode 100644 index 00000000..42a2c071 --- /dev/null +++ b/app/domain/tag/README.md @@ -0,0 +1,3 @@ +# :app:domain:tag module +## Dependency graph +![Dependency graph](../../../docs/images/graphs/dep_graph_app_domain_tag.svg) diff --git a/app/domain/tag/build.gradle.kts b/app/domain/tag/build.gradle.kts new file mode 100644 index 00000000..7385bf5b --- /dev/null +++ b/app/domain/tag/build.gradle.kts @@ -0,0 +1,14 @@ +plugins { + id("diary.app.domain") +} + +kotlin { + sourceSets { + commonMain { + dependencies { + implementation(project(":app:domain:account")) + implementation(project(":app:domain:backup")) + } + } + } +} diff --git a/app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/TagDomainModule.kt b/app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/TagDomainModule.kt new file mode 100644 index 00000000..9e7c32f6 --- /dev/null +++ b/app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/TagDomainModule.kt @@ -0,0 +1,8 @@ +package io.github.taetae98coding.diary.domain.tag + +import org.koin.core.annotation.ComponentScan +import org.koin.core.annotation.Module + +@Module +@ComponentScan +public class TagDomainModule diff --git a/app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/repository/TagRepository.kt b/app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/repository/TagRepository.kt new file mode 100644 index 00000000..c5b3b418 --- /dev/null +++ b/app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/repository/TagRepository.kt @@ -0,0 +1,17 @@ +package io.github.taetae98coding.diary.domain.tag.repository + +import io.github.taetae98coding.diary.core.model.tag.Tag +import io.github.taetae98coding.diary.core.model.tag.TagDetail +import io.github.taetae98coding.diary.core.model.tag.TagDto +import kotlinx.coroutines.flow.Flow + +public interface TagRepository { + public suspend fun upsert(tag: Tag) + public suspend fun update(tagId: String, detail: TagDetail) + public suspend fun updateDelete(tagId: String, isDelete: Boolean) + public suspend fun updateFinish(tagId: String, isFinish: Boolean) + + public fun find(tagId: String): Flow + public fun findByIds(tagIds: Set): Flow> + public fun page(owner: String?): Flow> +} diff --git a/app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/AddTagUseCase.kt b/app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/AddTagUseCase.kt new file mode 100644 index 00000000..8e13a3dd --- /dev/null +++ b/app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/AddTagUseCase.kt @@ -0,0 +1,43 @@ +package io.github.taetae98coding.diary.domain.tag.usecase + +import io.github.taetae98coding.diary.common.exception.tag.TagTitleBlankException +import io.github.taetae98coding.diary.core.model.tag.Tag +import io.github.taetae98coding.diary.core.model.tag.TagDetail +import io.github.taetae98coding.diary.domain.account.usecase.GetAccountUseCase +import io.github.taetae98coding.diary.domain.backup.usecase.PushTagBackupQueueUseCase +import io.github.taetae98coding.diary.domain.tag.repository.TagRepository +import kotlin.uuid.ExperimentalUuidApi +import kotlin.uuid.Uuid +import kotlinx.coroutines.flow.first +import kotlinx.datetime.Clock +import org.koin.core.annotation.Factory + +@OptIn(ExperimentalUuidApi::class) +@Factory +public class AddTagUseCase internal constructor( + private val getAccountUseCase: GetAccountUseCase, + private val pushTagBackupQueueUseCase: PushTagBackupQueueUseCase, + private val clock: Clock, + private val repository: TagRepository, +) { + public suspend operator fun invoke(detail: TagDetail): Result { + return runCatching { + if (detail.title.isBlank()) throw TagTitleBlankException() + + val account = getAccountUseCase().first().getOrThrow() + val tagId = Uuid.random().toString() + + val tag = Tag( + id = tagId, + detail = detail, + owner = account.uid, + isFinish = false, + isDelete = false, + updateAt = clock.now(), + ) + + repository.upsert(tag) + pushTagBackupQueueUseCase(tagId) + } + } +} diff --git a/app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/DeleteTagUseCase.kt b/app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/DeleteTagUseCase.kt new file mode 100644 index 00000000..43a0c1d6 --- /dev/null +++ b/app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/DeleteTagUseCase.kt @@ -0,0 +1,20 @@ +package io.github.taetae98coding.diary.domain.tag.usecase + +import io.github.taetae98coding.diary.domain.backup.usecase.PushTagBackupQueueUseCase +import io.github.taetae98coding.diary.domain.tag.repository.TagRepository +import org.koin.core.annotation.Factory + +@Factory +public class DeleteTagUseCase internal constructor( + private val repository: TagRepository, + private val pushTagBackupQueueUseCase: PushTagBackupQueueUseCase, +) { + public suspend operator fun invoke(tagId: String?): Result { + return runCatching { + if (tagId.isNullOrBlank()) return@runCatching + + repository.updateDelete(tagId, true) + pushTagBackupQueueUseCase(tagId) + } + } +} diff --git a/app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/FindTagUseCase.kt b/app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/FindTagUseCase.kt new file mode 100644 index 00000000..8340a742 --- /dev/null +++ b/app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/FindTagUseCase.kt @@ -0,0 +1,26 @@ +package io.github.taetae98coding.diary.domain.tag.usecase + +import io.github.taetae98coding.diary.core.model.tag.Tag +import io.github.taetae98coding.diary.domain.tag.repository.TagRepository +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.emitAll +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.mapLatest +import org.koin.core.annotation.Factory + +@OptIn(ExperimentalCoroutinesApi::class) +@Factory +public class FindTagUseCase internal constructor( + private val repository: TagRepository, +) { + public operator fun invoke(tagId: String?): Flow> { + if (tagId.isNullOrBlank()) return flowOf(Result.success(null)) + + return flow { emitAll(repository.find(tagId)) } + .mapLatest { Result.success(it) } + .catch { emit(Result.failure(it)) } + } +} diff --git a/app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/FinishTagUseCase.kt b/app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/FinishTagUseCase.kt new file mode 100644 index 00000000..580f70f8 --- /dev/null +++ b/app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/FinishTagUseCase.kt @@ -0,0 +1,20 @@ +package io.github.taetae98coding.diary.domain.tag.usecase + +import io.github.taetae98coding.diary.domain.backup.usecase.PushTagBackupQueueUseCase +import io.github.taetae98coding.diary.domain.tag.repository.TagRepository +import org.koin.core.annotation.Factory + +@Factory +public class FinishTagUseCase internal constructor( + private val pushTagBackupQueueUseCase: PushTagBackupQueueUseCase, + private val repository: TagRepository +){ + public suspend operator fun invoke(tagId: String?): Result { + return runCatching { + if (tagId.isNullOrBlank()) return@runCatching + + repository.updateFinish(tagId, true) + pushTagBackupQueueUseCase(tagId) + } + } +} diff --git a/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/FindCalendarMemoUseCase.kt b/app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/PageTagUseCase.kt similarity index 60% rename from app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/FindCalendarMemoUseCase.kt rename to app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/PageTagUseCase.kt index 0cf3d8b4..7411dd08 100644 --- a/app/domain/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/FindCalendarMemoUseCase.kt +++ b/app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/PageTagUseCase.kt @@ -1,8 +1,8 @@ -package io.github.taetae98coding.diary.domain.memo.usecase +package io.github.taetae98coding.diary.domain.tag.usecase -import io.github.taetae98coding.diary.core.model.memo.Memo +import io.github.taetae98coding.diary.core.model.tag.Tag import io.github.taetae98coding.diary.domain.account.usecase.GetAccountUseCase -import io.github.taetae98coding.diary.domain.memo.repository.MemoRepository +import io.github.taetae98coding.diary.domain.tag.repository.TagRepository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.catch @@ -10,19 +10,18 @@ import kotlinx.coroutines.flow.emitAll import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.mapLatest -import kotlinx.datetime.LocalDate import org.koin.core.annotation.Factory @OptIn(ExperimentalCoroutinesApi::class) @Factory -public class FindCalendarMemoUseCase internal constructor( +public class PageTagUseCase internal constructor( private val getAccountUseCase: GetAccountUseCase, - private val repository: MemoRepository, + private val repository: TagRepository, ) { - public operator fun invoke(dateRange: ClosedRange): Flow>> { + public operator fun invoke(): Flow>> { return flow { getAccountUseCase().mapLatest { it.getOrThrow() } - .flatMapLatest { repository.findByDateRange(it.uid, dateRange) } + .flatMapLatest { repository.page(it.uid) } .also { emitAll(it) } }.mapLatest { Result.success(it) diff --git a/app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/RestartTagUseCase.kt b/app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/RestartTagUseCase.kt new file mode 100644 index 00000000..b3440141 --- /dev/null +++ b/app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/RestartTagUseCase.kt @@ -0,0 +1,20 @@ +package io.github.taetae98coding.diary.domain.tag.usecase + +import io.github.taetae98coding.diary.domain.backup.usecase.PushTagBackupQueueUseCase +import io.github.taetae98coding.diary.domain.tag.repository.TagRepository +import org.koin.core.annotation.Factory + +@Factory +public class RestartTagUseCase internal constructor( + private val pushTagBackupQueueUseCase: PushTagBackupQueueUseCase, + private val repository: TagRepository, +){ + public suspend operator fun invoke(tagId: String?): Result { + return runCatching { + if (tagId.isNullOrBlank()) return@runCatching + + repository.updateFinish(tagId, false) + pushTagBackupQueueUseCase(tagId) + } + } +} diff --git a/app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/RestoreTagUseCase.kt b/app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/RestoreTagUseCase.kt new file mode 100644 index 00000000..34fd16fc --- /dev/null +++ b/app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/RestoreTagUseCase.kt @@ -0,0 +1,20 @@ +package io.github.taetae98coding.diary.domain.tag.usecase + +import io.github.taetae98coding.diary.domain.backup.usecase.PushTagBackupQueueUseCase +import io.github.taetae98coding.diary.domain.tag.repository.TagRepository +import org.koin.core.annotation.Factory + +@Factory +public class RestoreTagUseCase internal constructor( + private val pushTagBackupQueueUseCase: PushTagBackupQueueUseCase, + private val repository: TagRepository, +) { + public suspend operator fun invoke(tagId: String?): Result { + return runCatching { + if (tagId.isNullOrBlank()) return@runCatching + + repository.updateDelete(tagId, false) + pushTagBackupQueueUseCase(tagId) + } + } +} diff --git a/app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/UpdateTagUseCase.kt b/app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/UpdateTagUseCase.kt new file mode 100644 index 00000000..2dca032e --- /dev/null +++ b/app/domain/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/UpdateTagUseCase.kt @@ -0,0 +1,27 @@ +package io.github.taetae98coding.diary.domain.tag.usecase + +import io.github.taetae98coding.diary.core.model.tag.TagDetail +import io.github.taetae98coding.diary.domain.backup.usecase.PushTagBackupQueueUseCase +import io.github.taetae98coding.diary.domain.tag.repository.TagRepository +import kotlinx.coroutines.flow.first +import org.koin.core.annotation.Factory + +@Factory +public class UpdateTagUseCase internal constructor( + private val pushTagBackupQueueUseCase: PushTagBackupQueueUseCase, + private val repository: TagRepository, +) { + public suspend operator fun invoke(tagId: String?, detail: TagDetail): Result { + return runCatching { + if (tagId.isNullOrBlank()) return@runCatching + + val tag = repository.find(tagId).first() ?: return@runCatching + val validDetail = detail.copy(title = detail.title.ifBlank { tag.detail.title }) + + if (tag.detail == validDetail) return@runCatching + + repository.update(tagId, validDetail) + pushTagBackupQueueUseCase(tagId) + } + } +} diff --git a/app/feature/account/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/account/common/BasePasswordTextField.kt b/app/feature/account/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/account/common/BasePasswordTextField.kt index 8197641d..1c6592a8 100644 --- a/app/feature/account/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/account/common/BasePasswordTextField.kt +++ b/app/feature/account/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/account/common/BasePasswordTextField.kt @@ -8,10 +8,10 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.VisualTransformation +import io.github.taetae98coding.diary.core.design.system.icon.KeyIcon import io.github.taetae98coding.diary.core.design.system.icon.VisibilityOffIcon import io.github.taetae98coding.diary.core.design.system.icon.VisibilityOnIcon import io.github.taetae98coding.diary.core.design.system.text.ClearTextField -import io.github.taetae98coding.diary.core.resources.icon.KeyIcon @Composable internal fun BasePasswordTextField( diff --git a/app/feature/account/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/account/common/EmailTextField.kt b/app/feature/account/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/account/common/EmailTextField.kt index 8db3d2a1..bcbe818e 100644 --- a/app/feature/account/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/account/common/EmailTextField.kt +++ b/app/feature/account/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/account/common/EmailTextField.kt @@ -11,11 +11,8 @@ import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.text.input.KeyboardType +import io.github.taetae98coding.diary.core.design.system.icon.EmailIcon import io.github.taetae98coding.diary.core.design.system.text.ClearTextField -import io.github.taetae98coding.diary.core.resources.Res -import io.github.taetae98coding.diary.core.resources.email -import io.github.taetae98coding.diary.core.resources.icon.EmailIcon -import org.jetbrains.compose.resources.stringResource @Composable internal fun EmailTextField( @@ -29,7 +26,7 @@ internal fun EmailTextField( valueProvider = valueProvider, onValueChange = onValueChange, modifier = modifier.focusRequester(focusRequester), - placeholder = { Text(text = stringResource(Res.string.email)) }, + placeholder = { Text(text = "이메일") }, leadingIcon = { EmailIcon() }, keyboardOptions = KeyboardOptions( capitalization = KeyboardCapitalization.None, diff --git a/app/feature/account/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/account/join/JoinScreen.kt b/app/feature/account/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/account/join/JoinScreen.kt index c35f3599..d3fd6748 100644 --- a/app/feature/account/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/account/join/JoinScreen.kt +++ b/app/feature/account/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/account/join/JoinScreen.kt @@ -22,20 +22,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.text.input.KeyboardType +import io.github.taetae98coding.diary.core.design.system.emoji.Emoji +import io.github.taetae98coding.diary.core.design.system.icon.NavigateUpIcon import io.github.taetae98coding.diary.core.design.system.theme.DiaryTheme -import io.github.taetae98coding.diary.core.resources.Res -import io.github.taetae98coding.diary.core.resources.bottom_button_email_blank -import io.github.taetae98coding.diary.core.resources.bottom_button_password_blank -import io.github.taetae98coding.diary.core.resources.check_password -import io.github.taetae98coding.diary.core.resources.icon.NavigateUpIcon -import io.github.taetae98coding.diary.core.resources.join -import io.github.taetae98coding.diary.core.resources.join_button_invalid_email_message -import io.github.taetae98coding.diary.core.resources.join_button_message -import io.github.taetae98coding.diary.core.resources.join_button_password_different_message -import io.github.taetae98coding.diary.core.resources.join_exist_email_message -import io.github.taetae98coding.diary.core.resources.network_error -import io.github.taetae98coding.diary.core.resources.password -import io.github.taetae98coding.diary.core.resources.unknown_error import io.github.taetae98coding.diary.feature.account.common.BasePasswordTextField import io.github.taetae98coding.diary.feature.account.common.BottomBarButton import io.github.taetae98coding.diary.feature.account.common.BottomBarButtonContent @@ -43,7 +32,6 @@ import io.github.taetae98coding.diary.feature.account.common.EmailTextField import io.github.taetae98coding.diary.feature.account.join.state.JoinScreenButtonUiState import io.github.taetae98coding.diary.feature.account.join.state.JoinScreenState import io.github.taetae98coding.diary.feature.account.join.state.JoinUiState -import org.jetbrains.compose.resources.stringResource @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -59,7 +47,7 @@ internal fun JoinScreen( modifier = modifier, topBar = { TopAppBar( - title = { Text(text = stringResource(Res.string.join)) }, + title = { Text(text = "회원가입") }, navigationIcon = { IconButton(onClick = onNavigateUp) { NavigateUpIcon() @@ -114,23 +102,23 @@ private fun JoinButtonContent( BottomBarButtonContent(modifier = modifier) { when (uiState) { JoinScreenButtonUiState.JoinEnable -> { - Text(text = stringResource(Res.string.join_button_message)) + Text(text = "회원가입") } JoinScreenButtonUiState.EmailBlank -> { - Text(text = stringResource(Res.string.bottom_button_email_blank)) + Text(text = "이메일을 입력해 주세요 🐮") } JoinScreenButtonUiState.PasswordBlank -> { - Text(text = stringResource(Res.string.bottom_button_password_blank)) + Text(text = "비밀번호를 입력해 주세요 🦁") } JoinScreenButtonUiState.InvalidEmail -> { - Text(text = stringResource(Res.string.join_button_invalid_email_message)) + Text(text = "이메일 형식을 지켜주세요 🐯") } JoinScreenButtonUiState.PasswordDifferent -> { - Text(text = stringResource(Res.string.join_button_password_different_message)) + Text(text = "입력된 패스워드가 달라요 🐨") } JoinScreenButtonUiState.Progress -> { @@ -177,7 +165,7 @@ private fun PasswordTextField( valueProvider = { state.password }, onValueChange = state::onPasswordChange, modifier = modifier, - placeholder = { Text(text = stringResource(Res.string.password)) }, + placeholder = { Text(text = "패스워드") }, passwordVisibleProvider = { state.isPasswordVisible }, onPasswordVisibleChange = state::onPasswordVisibleChange, keyboardOptions = KeyboardOptions( @@ -198,7 +186,7 @@ private fun CheckPasswordTextField( valueProvider = { state.checkPassword }, onValueChange = state::onCheckPasswordChange, modifier = modifier, - placeholder = { Text(text = stringResource(Res.string.check_password)) }, + placeholder = { Text(text = "비밀번호 확인") }, passwordVisibleProvider = { state.isCheckPasswordVisible }, onPasswordVisibleChange = state::onCheckPasswordVisibleChange, keyboardOptions = KeyboardOptions( @@ -224,9 +212,6 @@ private fun Message( onLoginFinish: () -> Unit, ) { val uiState = uiStateProvider() - val existEmailMessage = stringResource(Res.string.join_exist_email_message) - val networkErrorMessage = stringResource(Res.string.network_error) - val unknownErrorMessage = stringResource(Res.string.unknown_error) LaunchedEffect( uiState.isLoginFinish, @@ -238,9 +223,9 @@ private fun Message( when { uiState.isLoginFinish -> onLoginFinish() - uiState.isExistEmail -> state.showMessage(existEmailMessage) - uiState.isNetworkError -> state.showMessage(networkErrorMessage) - uiState.isUnknownError -> state.showMessage(uiState.message) + uiState.isExistEmail -> state.showMessage("이미 사용되는 이메일이에요 ${Emoji.invalid.random()}") + uiState.isNetworkError -> state.showMessage("네트워크 연결 상태를 확인해 주세요 ${Emoji.fail.random()}") + uiState.isUnknownError -> state.showMessage("알 수 없는 에러가 발생했어요 잠시 후 다시 시도해 주세요 ${Emoji.error.random()}") } uiState.onMessageShow() diff --git a/app/feature/account/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/account/join/JoinViewModel.kt b/app/feature/account/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/account/join/JoinViewModel.kt index fd13bd4d..c929058d 100644 --- a/app/feature/account/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/account/join/JoinViewModel.kt +++ b/app/feature/account/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/account/join/JoinViewModel.kt @@ -43,7 +43,7 @@ internal class JoinViewModel( when (throwable) { is ExistEmailException -> _uiState.update { it.copy(isProgress = false, isExistEmail = true) } is NetworkException -> _uiState.update { it.copy(isProgress = false, isNetworkError = true) } - else -> _uiState.update { it.copy(isProgress = false, isUnknownError = true, message = it.toString()) } + else -> _uiState.update { it.copy(isProgress = false, isUnknownError = true) } } } diff --git a/app/feature/account/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/account/join/state/JoinUiState.kt b/app/feature/account/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/account/join/state/JoinUiState.kt index f5fc7f50..9c316718 100644 --- a/app/feature/account/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/account/join/state/JoinUiState.kt +++ b/app/feature/account/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/account/join/state/JoinUiState.kt @@ -6,7 +6,6 @@ internal data class JoinUiState( val isExistEmail: Boolean = false, val isNetworkError: Boolean = false, val isUnknownError: Boolean = false, - val message: String = "", val onMessageShow: () -> Unit = {}, ) { val hasMessage = isLoginFinish || isExistEmail || isNetworkError || isUnknownError diff --git a/app/feature/account/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/account/login/LoginScreen.kt b/app/feature/account/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/account/login/LoginScreen.kt index 1143d792..235c3338 100644 --- a/app/feature/account/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/account/login/LoginScreen.kt +++ b/app/feature/account/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/account/login/LoginScreen.kt @@ -22,17 +22,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.text.input.KeyboardType +import io.github.taetae98coding.diary.core.design.system.emoji.Emoji +import io.github.taetae98coding.diary.core.design.system.icon.NavigateUpIcon import io.github.taetae98coding.diary.core.design.system.theme.DiaryTheme -import io.github.taetae98coding.diary.core.resources.Res -import io.github.taetae98coding.diary.core.resources.account_not_found_error -import io.github.taetae98coding.diary.core.resources.bottom_button_email_blank -import io.github.taetae98coding.diary.core.resources.bottom_button_password_blank -import io.github.taetae98coding.diary.core.resources.check_password -import io.github.taetae98coding.diary.core.resources.icon.NavigateUpIcon -import io.github.taetae98coding.diary.core.resources.login -import io.github.taetae98coding.diary.core.resources.login_button_message -import io.github.taetae98coding.diary.core.resources.network_error -import io.github.taetae98coding.diary.core.resources.unknown_error import io.github.taetae98coding.diary.feature.account.common.BasePasswordTextField import io.github.taetae98coding.diary.feature.account.common.BottomBarButton import io.github.taetae98coding.diary.feature.account.common.BottomBarButtonContent @@ -40,7 +32,6 @@ import io.github.taetae98coding.diary.feature.account.common.EmailTextField import io.github.taetae98coding.diary.feature.account.login.state.LoginScreenButtonUiState import io.github.taetae98coding.diary.feature.account.login.state.LoginScreenState import io.github.taetae98coding.diary.feature.account.login.state.LoginUiState -import org.jetbrains.compose.resources.stringResource @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -56,7 +47,7 @@ internal fun LoginScreen( modifier = modifier, topBar = { TopAppBar( - title = { Text(text = stringResource(Res.string.login)) }, + title = { Text(text = "로그인") }, navigationIcon = { IconButton(onClick = onNavigateUp) { NavigateUpIcon() @@ -110,9 +101,6 @@ private fun Message( onLoginFinish: () -> Unit, ) { val uiState = uiStateProvider() - val accountNotFoundErrorMessage = stringResource(Res.string.account_not_found_error) - val networkErrorMessage = stringResource(Res.string.network_error) - val unknownErrorMessage = stringResource(Res.string.unknown_error) LaunchedEffect( uiState.isLoginFinish, @@ -124,9 +112,9 @@ private fun Message( when { uiState.isLoginFinish -> onLoginFinish() - uiState.isAccountNotFound -> state.showMessage(accountNotFoundErrorMessage) - uiState.isNetworkError -> state.showMessage(networkErrorMessage) - uiState.isUnknownError -> state.showMessage(unknownErrorMessage) + uiState.isAccountNotFound -> state.showMessage("계정을 찾을 수 없어요 ${Emoji.fail.random()}") + uiState.isNetworkError -> state.showMessage("네트워크 연결 상태를 확인해 주세요 ${Emoji.fail.random()}") + uiState.isUnknownError -> state.showMessage("알 수 없는 에러가 발생했어요 잠시 후 다시 시도해 주세요 ${Emoji.error.random()}") } uiState.onMessageShow() @@ -141,15 +129,15 @@ private fun LoginButtonContent( BottomBarButtonContent(modifier = modifier) { when (uiState) { LoginScreenButtonUiState.LoginEnable -> { - Text(text = stringResource(Res.string.login_button_message)) + Text(text = "로그인") } LoginScreenButtonUiState.EmailBlank -> { - Text(text = stringResource(Res.string.bottom_button_email_blank)) + Text(text = "이메일을 입력해 주세요 🐸") } LoginScreenButtonUiState.PasswordBlank -> { - Text(text = stringResource(Res.string.bottom_button_password_blank)) + Text(text = "비밀번호를 입력해 주세요 🐷") } LoginScreenButtonUiState.Progress -> { @@ -178,7 +166,7 @@ private fun Content( valueProvider = { state.password }, onValueChange = state::onPasswordChange, modifier = textFieldModifier, - placeholder = { Text(text = stringResource(Res.string.check_password)) }, + placeholder = { Text(text = "비밀번호 확인") }, passwordVisibleProvider = { state.isPasswordVisible }, onPasswordVisibleChange = state::onPasswordVisibleChange, keyboardOptions = KeyboardOptions( diff --git a/app/feature/calendar/build.gradle.kts b/app/feature/calendar/build.gradle.kts index 2aa424e7..6ddfc99e 100644 --- a/app/feature/calendar/build.gradle.kts +++ b/app/feature/calendar/build.gradle.kts @@ -8,7 +8,7 @@ kotlin { dependencies { implementation(project(":app:core:calendar-compose")) - implementation(project(":app:domain:memo")) + implementation(project(":app:domain:calendar")) implementation(project(":app:domain:holiday")) } } diff --git a/app/feature/calendar/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/calendar/CalendarMemoViewModel.kt b/app/feature/calendar/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/calendar/CalendarMemoViewModel.kt index 78954d74..5274678e 100644 --- a/app/feature/calendar/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/calendar/CalendarMemoViewModel.kt +++ b/app/feature/calendar/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/calendar/CalendarMemoViewModel.kt @@ -3,7 +3,7 @@ package io.github.taetae98coding.diary.feature.calendar import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import io.github.taetae98coding.diary.core.calendar.compose.item.CalendarItemUiState -import io.github.taetae98coding.diary.domain.memo.usecase.FindCalendarMemoUseCase +import io.github.taetae98coding.diary.domain.calendar.usecase.FindCalendarMemoUseCase import io.github.taetae98coding.diary.library.coroutines.mapCollectionLatest import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow diff --git a/app/feature/calendar/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/calendar/CalendarScreen.kt b/app/feature/calendar/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/calendar/CalendarScreen.kt index f21cede0..fff6e917 100644 --- a/app/feature/calendar/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/calendar/CalendarScreen.kt +++ b/app/feature/calendar/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/calendar/CalendarScreen.kt @@ -9,7 +9,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.lifecycle.compose.LifecycleStartEffect +import androidx.lifecycle.compose.LifecycleResumeEffect import io.github.taetae98coding.diary.core.calendar.compose.Calendar import io.github.taetae98coding.diary.core.calendar.compose.item.CalendarItemUiState import io.github.taetae98coding.diary.core.calendar.compose.modifier.calendarDateRangeSelectable @@ -46,9 +46,9 @@ internal fun CalendarScreen( ), ) - LifecycleStartEffect(Unit) { + LifecycleResumeEffect(Unit) { today = LocalDate.todayIn() - onStopOrDispose { } + onPauseOrDispose { } } } } diff --git a/app/feature/memo/build.gradle.kts b/app/feature/memo/build.gradle.kts index 041461a8..7633a693 100644 --- a/app/feature/memo/build.gradle.kts +++ b/app/feature/memo/build.gradle.kts @@ -7,6 +7,7 @@ kotlin { commonMain { dependencies { implementation(project(":app:domain:memo")) + implementation(project(":app:domain:tag")) } } } diff --git a/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/MemoNavigation.kt b/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/MemoNavigation.kt index 0a4148b4..b8400a0a 100644 --- a/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/MemoNavigation.kt +++ b/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/MemoNavigation.kt @@ -7,6 +7,8 @@ import androidx.navigation.navigation import io.github.taetae98coding.diary.core.navigation.memo.MemoAddDestination import io.github.taetae98coding.diary.core.navigation.memo.MemoDestination import io.github.taetae98coding.diary.core.navigation.memo.MemoDetailDestination +import io.github.taetae98coding.diary.core.navigation.tag.TagAddDestination +import io.github.taetae98coding.diary.core.navigation.tag.TagDetailDestination import io.github.taetae98coding.diary.feature.memo.add.MemoAddRoute import io.github.taetae98coding.diary.feature.memo.detail.MemoDetailRoute import io.github.taetae98coding.diary.library.navigation.LocalDateNavType @@ -24,12 +26,16 @@ public fun NavGraphBuilder.memoNavigation( ) { MemoAddRoute( navigateUp = navController::popBackStack, + navigateToTagAdd = { navController.navigate(TagAddDestination) }, + navigateToTagDetail = { navController.navigate(TagDetailDestination(it)) }, ) } composable { MemoDetailRoute( navigateUp = navController::popBackStack, + navigateToTagAdd = { navController.navigate(TagAddDestination) }, + navigateToTagDetail = { navController.navigate(TagDetailDestination(it)) }, ) } } diff --git a/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/add/MemoAddRoute.kt b/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/add/MemoAddRoute.kt index 49dc19c3..0364b399 100644 --- a/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/add/MemoAddRoute.kt +++ b/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/add/MemoAddRoute.kt @@ -1,56 +1,99 @@ package io.github.taetae98coding.diary.feature.memo.add import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi +import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo import androidx.compose.material3.adaptive.layout.AnimatedPane import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffold +import androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole +import androidx.compose.material3.adaptive.layout.calculatePaneScaffoldDirective import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle -import io.github.taetae98coding.diary.core.resources.Res -import io.github.taetae98coding.diary.core.resources.memo_add +import androidx.window.core.layout.WindowWidthSizeClass +import io.github.taetae98coding.diary.core.compose.adaptive.isListVisible +import io.github.taetae98coding.diary.core.compose.back.KBackHandler import io.github.taetae98coding.diary.feature.memo.detail.MemoDetailActionButton import io.github.taetae98coding.diary.feature.memo.detail.MemoDetailFloatingButton import io.github.taetae98coding.diary.feature.memo.detail.MemoDetailNavigationButton import io.github.taetae98coding.diary.feature.memo.detail.MemoDetailScreen -import io.github.taetae98coding.diary.feature.memo.detail.rememberMemoDetailScreenAddState -import org.jetbrains.compose.resources.stringResource +import io.github.taetae98coding.diary.feature.memo.tag.MemoTagNavigationButton +import io.github.taetae98coding.diary.feature.memo.tag.MemoTagScreen import org.koin.compose.viewmodel.koinViewModel @OptIn(ExperimentalMaterial3AdaptiveApi::class) @Composable internal fun MemoAddRoute( navigateUp: () -> Unit, + navigateToTagAdd: () -> Unit, + navigateToTagDetail: (String) -> Unit, modifier: Modifier = Modifier, addViewModel: MemoAddViewModel = koinViewModel(), ) { - val navigator = rememberListDetailPaneScaffoldNavigator() + val windowAdaptiveInfo = currentWindowAdaptiveInfo() + val navigator = rememberListDetailPaneScaffoldNavigator(scaffoldDirective = calculatePaneScaffoldDirective(windowAdaptiveInfo)) ListDetailPaneScaffold( - directive = navigator.scaffoldDirective, + directive = navigator.scaffoldDirective.copy(defaultPanePreferredWidth = 450.dp), value = navigator.scaffoldValue, listPane = { + val state = rememberMemoDetailScreenAddState( + initialStart = addViewModel.route.start, + initialEndInclusive = addViewModel.route.endInclusive, + ) + AnimatedPane { - val state = rememberMemoDetailScreenAddState( - initialStart = addViewModel.route.start, - initialEndInclusive = addViewModel.route.endInclusive, - ) - val title = stringResource(Res.string.memo_add) val uiState by addViewModel.uiState.collectAsStateWithLifecycle() + val tagList by addViewModel.memoTagList.collectAsStateWithLifecycle() MemoDetailScreen( state = state, - titleProvider = { title }, + titleProvider = { "메모 추가" }, navigateButtonProvider = { MemoDetailNavigationButton.NavigateUp(onNavigateUp = navigateUp) }, actionButtonProvider = { MemoDetailActionButton.None }, floatingButtonProvider = { MemoDetailFloatingButton.Add { addViewModel.add(state.memoDetail) } }, uiStateProvider = { uiState }, + onTagTitle = { navigator.navigateTo(ThreePaneScaffoldRole.Primary) }, + onTag = navigateToTagDetail, + tagListProvider = { tagList }, ) } }, detailPane = { + AnimatedPane { + val isNavigateUpVisible = remember(windowAdaptiveInfo) { + if (windowAdaptiveInfo.windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.EXPANDED) { + !navigator.isListVisible() + } else { + true + } + } + + val memoTagList by addViewModel.memoTagList.collectAsStateWithLifecycle() + val tagList by addViewModel.tagList.collectAsStateWithLifecycle() + + MemoTagScreen( + navigateButtonProvider = { + if (isNavigateUpVisible) { + MemoTagNavigationButton.NavigateUp(onNavigateUp = navigator::navigateBack) + } else { + MemoTagNavigationButton.None + } + }, + onTagAdd = navigateToTagAdd, + memoTagListProvider = { memoTagList }, + tagListProvider = { tagList }, + ) + } }, modifier = modifier, ) + + KBackHandler( + isEnabled = navigator.canNavigateBack(), + onBack = navigator::navigateBack, + ) } diff --git a/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/add/MemoAddViewModel.kt b/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/add/MemoAddViewModel.kt index f6a5ca53..c9c1baa0 100644 --- a/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/add/MemoAddViewModel.kt +++ b/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/add/MemoAddViewModel.kt @@ -5,23 +5,33 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.navigation.toRoute import io.github.taetae98coding.diary.common.exception.memo.MemoTitleBlankException +import io.github.taetae98coding.diary.core.compose.runtime.SkipProperty import io.github.taetae98coding.diary.core.model.memo.MemoDetail import io.github.taetae98coding.diary.core.navigation.memo.MemoAddDestination import io.github.taetae98coding.diary.domain.memo.usecase.AddMemoUseCase +import io.github.taetae98coding.diary.domain.tag.usecase.PageTagUseCase import io.github.taetae98coding.diary.feature.memo.detail.MemoDetailScreenUiState +import io.github.taetae98coding.diary.feature.memo.tag.TagUiState import io.github.taetae98coding.diary.library.navigation.LocalDateNavType import kotlin.reflect.typeOf +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.mapLatest +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.datetime.LocalDate import org.koin.android.annotation.KoinViewModel +@OptIn(ExperimentalCoroutinesApi::class) @KoinViewModel internal class MemoAddViewModel( savedStateHandle: SavedStateHandle, private val addMemoUseCase: AddMemoUseCase, + pageTagUseCase: PageTagUseCase, ) : ViewModel() { val route = savedStateHandle.toRoute( typeMap = mapOf(typeOf() to LocalDateNavType), @@ -30,6 +40,50 @@ internal class MemoAddViewModel( private val _uiState = MutableStateFlow(MemoDetailScreenUiState(onMessageShow = ::clearMessage)) val uiState = _uiState.asStateFlow() + private val tagPageList = pageTagUseCase().mapLatest { it.getOrNull() } + .stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5_000), + initialValue = null, + ) + private val selectedTag = MutableStateFlow(emptySet()) + private val primaryTag = MutableStateFlow(null) + + val memoTagList = combine(tagPageList, selectedTag, primaryTag) { list, selected, primary -> + list?.filter { selected.contains(it.id) } + ?.map { + TagUiState( + id = it.id, + title = it.detail.title, + isSelected = it.id == primary, + color = it.detail.color, + select = SkipProperty { primaryTag(it.id) }, + unselect = SkipProperty { deletePrimaryTag() }, + ) + } + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5_000), + initialValue = null, + ) + + val tagList = combine(tagPageList, selectedTag) { list, selected -> + list?.map { + TagUiState( + id = it.id, + title = it.detail.title, + isSelected = selected.contains(it.id), + color = it.detail.color, + select = SkipProperty { selectTag(it.id) }, + unselect = SkipProperty { unselectTag(it.id) }, + ) + } + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5_000), + initialValue = null, + ) + private fun clearMessage() { _uiState.update { it.copy( @@ -45,7 +99,7 @@ internal class MemoAddViewModel( viewModelScope.launch { _uiState.update { it.copy(isProgress = true) } - addMemoUseCase(detail) + addMemoUseCase(detail = detail, primaryTag = primaryTag.value, tagIds = selectedTag.value) .onSuccess { _uiState.update { it.copy(isProgress = false, isAdd = true) } } .onFailure { handleThrowable(it) } } @@ -57,4 +111,30 @@ internal class MemoAddViewModel( else -> _uiState.update { it.copy(isProgress = false, isUnknownError = true) } } } + + private fun selectTag(tagId: String) { + selectedTag.update { + buildSet { + addAll(it) + add(tagId) + } + } + } + + private fun unselectTag(tagId: String) { + selectedTag.update { + buildSet { + addAll(it) + remove(tagId) + } + } + } + + private fun primaryTag(tagId: String) { + primaryTag.update { tagId } + } + + private fun deletePrimaryTag() { + primaryTag.update { null } + } } diff --git a/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/add/RememberMemoDetailScreenAddState.kt b/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/add/RememberMemoDetailScreenAddState.kt new file mode 100644 index 00000000..43b5b50b --- /dev/null +++ b/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/add/RememberMemoDetailScreenAddState.kt @@ -0,0 +1,33 @@ +package io.github.taetae98coding.diary.feature.memo.add + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import io.github.taetae98coding.diary.core.design.system.diary.color.rememberDiaryColorState +import io.github.taetae98coding.diary.core.design.system.diary.component.rememberDiaryComponentState +import io.github.taetae98coding.diary.core.design.system.diary.date.rememberDiaryDateState +import io.github.taetae98coding.diary.feature.memo.detail.MemoDetailScreenState +import kotlinx.datetime.LocalDate + +@Composable +internal fun rememberMemoDetailScreenAddState( + initialStart: LocalDate?, + initialEndInclusive: LocalDate?, +): MemoDetailScreenState.Add { + val coroutineScope = rememberCoroutineScope() + val componentState = rememberDiaryComponentState() + val dateState = rememberDiaryDateState( + initialStart = initialStart, + initialEndInclusive = initialEndInclusive, + ) + val colorState = rememberDiaryColorState() + + return remember { + MemoDetailScreenState.Add( + coroutineScope = coroutineScope, + componentState = componentState, + dateState = dateState, + colorState = colorState, + ) + } +} diff --git a/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/detail/MemoDetailRoute.kt b/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/detail/MemoDetailRoute.kt index 6ff7daae..3bfcec5d 100644 --- a/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/detail/MemoDetailRoute.kt +++ b/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/detail/MemoDetailRoute.kt @@ -1,37 +1,52 @@ package io.github.taetae98coding.diary.feature.memo.detail import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi +import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo import androidx.compose.material3.adaptive.layout.AnimatedPane import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffold +import androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole +import androidx.compose.material3.adaptive.layout.calculatePaneScaffoldDirective import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.window.core.layout.WindowWidthSizeClass +import io.github.taetae98coding.diary.core.compose.adaptive.isListVisible +import io.github.taetae98coding.diary.core.compose.back.KBackHandler +import io.github.taetae98coding.diary.feature.memo.tag.MemoTagNavigationButton +import io.github.taetae98coding.diary.feature.memo.tag.MemoTagScreen import org.koin.compose.viewmodel.koinViewModel @OptIn(ExperimentalMaterial3AdaptiveApi::class) @Composable internal fun MemoDetailRoute( navigateUp: () -> Unit, + navigateToTagAdd: () -> Unit, + navigateToTagDetail: (String) -> Unit, modifier: Modifier = Modifier, detailViewModel: MemoDetailViewModel = koinViewModel(), + detailTagViewModel: MemoDetailTagViewModel = koinViewModel(), ) { - val navigator = rememberListDetailPaneScaffoldNavigator() + val windowAdaptiveInfo = currentWindowAdaptiveInfo() + val navigator = rememberListDetailPaneScaffoldNavigator(scaffoldDirective = calculatePaneScaffoldDirective(windowAdaptiveInfo)) ListDetailPaneScaffold( directive = navigator.scaffoldDirective, value = navigator.scaffoldValue, listPane = { + val detail by detailViewModel.detail.collectAsStateWithLifecycle() + val state = rememberMemoDetailScreenDetailState( + onDelete = navigateUp, + onUpdate = navigateUp, + detailProvider = { detail }, + ) + AnimatedPane { - val detail by detailViewModel.detail.collectAsStateWithLifecycle() - val state = rememberMemoDetailScreenDetailState( - onDelete = navigateUp, - onUpdate = navigateUp, - detailProvider = { detail }, - ) val actionButton by detailViewModel.actionButton.collectAsStateWithLifecycle() val uiState by detailViewModel.uiState.collectAsStateWithLifecycle() + val tagList by detailTagViewModel.memoTagUiStateList.collectAsStateWithLifecycle() MemoDetailScreen( state = state, @@ -40,11 +55,44 @@ internal fun MemoDetailRoute( actionButtonProvider = { actionButton }, floatingButtonProvider = { MemoDetailFloatingButton.None }, uiStateProvider = { uiState }, + onTagTitle = { navigator.navigateTo(ThreePaneScaffoldRole.Primary) }, + onTag = navigateToTagDetail, + tagListProvider = { tagList }, ) } }, detailPane = { + val isNavigateUpVisible = remember(windowAdaptiveInfo) { + if (windowAdaptiveInfo.windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.EXPANDED) { + !navigator.isListVisible() + } else { + true + } + } + + AnimatedPane { + val memoTagList by detailTagViewModel.memoTagUiStateList.collectAsStateWithLifecycle() + val tagList by detailTagViewModel.tagList.collectAsStateWithLifecycle() + + MemoTagScreen( + navigateButtonProvider = { + if (isNavigateUpVisible) { + MemoTagNavigationButton.NavigateUp(onNavigateUp = navigator::navigateBack) + } else { + MemoTagNavigationButton.None + } + }, + onTagAdd = navigateToTagAdd, + memoTagListProvider = { memoTagList }, + tagListProvider = { tagList }, + ) + } }, modifier = modifier, ) + + KBackHandler( + isEnabled = navigator.canNavigateBack(), + onBack = navigator::navigateBack, + ) } diff --git a/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/detail/MemoDetailScreen.kt b/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/detail/MemoDetailScreen.kt index 9ff3134a..470555c0 100644 --- a/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/detail/MemoDetailScreen.kt +++ b/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/detail/MemoDetailScreen.kt @@ -1,25 +1,26 @@ package io.github.taetae98coding.diary.feature.memo.detail import androidx.compose.foundation.basicMarquee +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.IconButton import androidx.compose.material3.IconToggleButton import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarHost import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar +import androidx.compose.material3.minimumInteractiveComponentSize import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.derivedStateOf @@ -27,19 +28,19 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import io.github.taetae98coding.diary.core.compose.button.FloatingAddButton import io.github.taetae98coding.diary.core.design.system.diary.color.DiaryColor import io.github.taetae98coding.diary.core.design.system.diary.component.DiaryComponent import io.github.taetae98coding.diary.core.design.system.diary.date.DiaryDate +import io.github.taetae98coding.diary.core.design.system.emoji.Emoji +import io.github.taetae98coding.diary.core.design.system.icon.ChevronRightIcon +import io.github.taetae98coding.diary.core.design.system.icon.DeleteIcon +import io.github.taetae98coding.diary.core.design.system.icon.FinishIcon +import io.github.taetae98coding.diary.core.design.system.icon.NavigateUpIcon import io.github.taetae98coding.diary.core.design.system.theme.DiaryTheme -import io.github.taetae98coding.diary.core.resources.Res -import io.github.taetae98coding.diary.core.resources.icon.AddIcon -import io.github.taetae98coding.diary.core.resources.icon.DeleteIcon -import io.github.taetae98coding.diary.core.resources.icon.FinishIcon -import io.github.taetae98coding.diary.core.resources.icon.NavigateUpIcon -import io.github.taetae98coding.diary.core.resources.memo_add_message -import io.github.taetae98coding.diary.core.resources.title_blank_error -import io.github.taetae98coding.diary.core.resources.unknown_error -import org.jetbrains.compose.resources.stringResource +import io.github.taetae98coding.diary.feature.memo.tag.PrimaryMemoTag +import io.github.taetae98coding.diary.feature.memo.tag.TagFlow +import io.github.taetae98coding.diary.feature.memo.tag.TagUiState @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -50,6 +51,9 @@ internal fun MemoDetailScreen( actionButtonProvider: () -> MemoDetailActionButton, floatingButtonProvider: () -> MemoDetailFloatingButton, uiStateProvider: () -> MemoDetailScreenUiState, + onTagTitle: () -> Unit, + onTag: (String) -> Unit, + tagListProvider: () -> List?, modifier: Modifier = Modifier, ) { Scaffold( @@ -73,7 +77,7 @@ internal fun MemoDetailScreen( } } - else -> Unit + is MemoDetailNavigationButton.None -> Unit } }, actions = { @@ -102,21 +106,21 @@ internal fun MemoDetailScreen( is MemoDetailFloatingButton.Add -> { val isProgress by remember { derivedStateOf { uiStateProvider().isProgress } } - FloatingActionButton(onClick = button.onAdd) { - if (isProgress) { - CircularProgressIndicator(modifier = Modifier.size(24.dp)) - } else { - AddIcon() - } - } + FloatingAddButton( + onClick = button.onAdd, + progressProvider = { isProgress }, + ) } - else -> Unit + is MemoDetailFloatingButton.None -> Unit } }, ) { Content( state = state, + onTagTitle = onTagTitle, + onTag = onTag, + tagListProvider = tagListProvider, modifier = Modifier.fillMaxSize() .padding(it) .padding(DiaryTheme.dimen.screenPaddingValues), @@ -137,9 +141,6 @@ private fun Message( uiStateProvider: () -> MemoDetailScreenUiState, ) { val uiState = uiStateProvider() - val addMessage = stringResource(Res.string.memo_add_message) - val titleBlankMessage = stringResource(Res.string.title_blank_error) - val unknownErrorMessage = stringResource(Res.string.unknown_error) LaunchedEffect( uiState.isAdd, @@ -152,7 +153,7 @@ private fun Message( when { uiState.isAdd -> { - state.showMessage(addMessage) + state.showMessage("메모 추가 ${Emoji.congratulate.random()}") state.clearInput() state.requestTitleFocus() } @@ -170,11 +171,11 @@ private fun Message( } uiState.isTitleBlankError -> { - state.showMessage(titleBlankMessage) + state.showMessage("제목을 입력해 주세요 ${Emoji.check.random()}") state.titleError() } - uiState.isUnknownError -> state.showMessage(unknownErrorMessage) + uiState.isUnknownError -> state.showMessage("알 수 없는 에러가 발생했어요 잠시 후 다시 시도해 주세요 ${Emoji.error.random()}") } uiState.onMessageShow() @@ -195,6 +196,9 @@ private fun LaunchedFocus( @Composable private fun Content( state: MemoDetailScreenState, + onTagTitle: () -> Unit, + onTag: (String) -> Unit, + tagListProvider: () -> List?, modifier: Modifier = Modifier, ) { Column( @@ -204,14 +208,60 @@ private fun Content( ) { DiaryComponent(state = state.componentState) DiaryDate(state = state.dateState) - Row { - DiaryColor( - state = state.colorState, - modifier = Modifier.weight(1F) - .height(100.dp), + InternalDiaryTag( + onTitle = onTagTitle, + onTag = onTag, + listProvider = tagListProvider, + ) + InternalDiaryColor(state = state) + } +} + +@Composable +private fun InternalDiaryTag( + onTitle: () -> Unit, + onTag: (String) -> Unit, + listProvider: () -> List?, + modifier: Modifier = Modifier, +) { + TagFlow( + title = { + Row( + modifier = Modifier.fillMaxWidth() + .clickable(onClick = onTitle) + .minimumInteractiveComponentSize() + .padding(horizontal = DiaryTheme.dimen.diaryHorizontalPadding), + horizontalArrangement = Arrangement.SpaceBetween, + ) { + Text(text = "태그") + ChevronRightIcon() + } + }, + listProvider = listProvider, + empty = { Text(text = "태그가 없어요 🐻‍❄️") }, + tag = { + PrimaryMemoTag( + uiState = it, + onClick = { onTag(it.id) }, ) + }, + modifier = modifier.fillMaxWidth() + .heightIn(min = 150.dp, max = 200.dp), + ) +} - Spacer(modifier = Modifier.weight(1F)) - } +@Composable +private fun InternalDiaryColor( + state: MemoDetailScreenState, + modifier: Modifier = Modifier, +) { + Row(modifier = modifier) { + DiaryColor( + state = state.colorState, + modifier = Modifier.weight(1F) + .height(100.dp), + ) + + Spacer(modifier = Modifier.weight(1F)) } } diff --git a/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/detail/MemoDetailScreenState.kt b/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/detail/MemoDetailScreenState.kt index feda2bda..1ea8745a 100644 --- a/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/detail/MemoDetailScreenState.kt +++ b/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/detail/MemoDetailScreenState.kt @@ -11,7 +11,8 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.launch internal sealed class MemoDetailScreenState { - abstract val coroutineScope: CoroutineScope + protected abstract val coroutineScope: CoroutineScope + abstract val componentState: DiaryComponentState abstract val dateState: DiaryDateState abstract val colorState: DiaryColorState diff --git a/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/detail/MemoDetailTagViewModel.kt b/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/detail/MemoDetailTagViewModel.kt new file mode 100644 index 00000000..813fead0 --- /dev/null +++ b/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/detail/MemoDetailTagViewModel.kt @@ -0,0 +1,119 @@ +package io.github.taetae98coding.diary.feature.memo.detail + +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import io.github.taetae98coding.diary.core.compose.runtime.SkipProperty +import io.github.taetae98coding.diary.core.navigation.memo.MemoDetailDestination +import io.github.taetae98coding.diary.domain.memo.usecase.DeleteMemoPrimaryTagUseCase +import io.github.taetae98coding.diary.domain.memo.usecase.FindMemoTagUseCase +import io.github.taetae98coding.diary.domain.memo.usecase.SelectMemoTagUseCase +import io.github.taetae98coding.diary.domain.memo.usecase.UnselectMemoTagUseCase +import io.github.taetae98coding.diary.domain.memo.usecase.UpdateMemoPrimaryTagUseCase +import io.github.taetae98coding.diary.domain.tag.usecase.PageTagUseCase +import io.github.taetae98coding.diary.feature.memo.tag.TagUiState +import io.github.taetae98coding.diary.library.coroutines.mapCollectionLatest +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.mapLatest +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch +import org.koin.android.annotation.KoinViewModel + +@OptIn(ExperimentalCoroutinesApi::class) +@KoinViewModel +internal class MemoDetailTagViewModel( + savedStateHandle: SavedStateHandle, + findMemoTagUseCase: FindMemoTagUseCase, + pageTagUseCase: PageTagUseCase, + private val updateMemoPrimaryTagUseCase: UpdateMemoPrimaryTagUseCase, + private val deleteMemoPrimaryTagUseCase: DeleteMemoPrimaryTagUseCase, + private val selectMemoTagUseCase: SelectMemoTagUseCase, + private val unselectMemoTagUseCase: UnselectMemoTagUseCase, +) : ViewModel() { + private val memoId = savedStateHandle.getStateFlow(MemoDetailDestination.MEMO_ID, null) + + private val memoTagList = memoId.flatMapLatest { findMemoTagUseCase(it) } + .mapLatest { it.getOrNull() } + .stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5_000), + initialValue = null, + ) + + val memoTagUiStateList = memoTagList.mapCollectionLatest { + TagUiState( + id = it.tag.id, + title = it.tag.detail.title, + isSelected = it.isPrimary, + color = it.tag.detail.color, + select = SkipProperty { primaryTag(it.tag.id) }, + unselect = SkipProperty { deletePrimaryTag() }, + ) + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5_000), + initialValue = null, + ) + + private val pageTagList = pageTagUseCase().mapLatest { it.getOrNull() } + .stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5_000), + initialValue = null, + ) + + val tagList = combine(memoTagList, pageTagList) { memoTagList, pageTagList -> + if (memoTagList == null || pageTagList == null) return@combine null + + val tagIdSet = memoTagList.map { it.tag.id }.toSet() + + buildList { + addAll(memoTagList.map { it.tag }) + addAll(pageTagList) + }.distinctBy { + it.id + }.sortedBy { + it.detail.title + }.map { + TagUiState( + id = it.id, + title = it.detail.title, + isSelected = tagIdSet.contains(it.id), + color = it.detail.color, + select = SkipProperty { selectTag(it.id) }, + unselect = SkipProperty { unselectTag(it.id) }, + ) + } + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5_000), + initialValue = null, + ) + + private fun primaryTag(tagId: String) { + viewModelScope.launch { + updateMemoPrimaryTagUseCase(memoId.value, tagId) + } + } + + private fun deletePrimaryTag() { + viewModelScope.launch { + deleteMemoPrimaryTagUseCase(memoId.value) + } + } + + private fun selectTag(tagId: String) { + viewModelScope.launch { + selectMemoTagUseCase(memoId.value, tagId) + } + } + + private fun unselectTag(tagId: String) { + viewModelScope.launch { + unselectMemoTagUseCase(memoId.value, tagId) + } + } +} diff --git a/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/detail/RememberMemoDetailScreenState.kt b/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/detail/RememberMemoDetailScreenDetailState.kt similarity index 71% rename from app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/detail/RememberMemoDetailScreenState.kt rename to app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/detail/RememberMemoDetailScreenDetailState.kt index 6566a48c..3dfc0b1a 100644 --- a/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/detail/RememberMemoDetailScreenState.kt +++ b/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/detail/RememberMemoDetailScreenDetailState.kt @@ -8,30 +8,6 @@ import io.github.taetae98coding.diary.core.design.system.diary.color.rememberDia import io.github.taetae98coding.diary.core.design.system.diary.component.rememberDiaryComponentState import io.github.taetae98coding.diary.core.design.system.diary.date.rememberDiaryDateState import io.github.taetae98coding.diary.core.model.memo.MemoDetail -import kotlinx.datetime.LocalDate - -@Composable -internal fun rememberMemoDetailScreenAddState( - initialStart: LocalDate?, - initialEndInclusive: LocalDate?, -): MemoDetailScreenState.Add { - val coroutineScope = rememberCoroutineScope() - val componentState = rememberDiaryComponentState() - val dateState = rememberDiaryDateState( - initialStart = initialStart, - initialEndInclusive = initialEndInclusive, - ) - val colorState = rememberDiaryColorState() - - return remember { - MemoDetailScreenState.Add( - coroutineScope = coroutineScope, - componentState = componentState, - dateState = dateState, - colorState = colorState, - ) - } -} @Composable internal fun rememberMemoDetailScreenDetailState( diff --git a/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/tag/MemoTag.kt b/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/tag/MemoTag.kt new file mode 100644 index 00000000..078c6e2c --- /dev/null +++ b/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/tag/MemoTag.kt @@ -0,0 +1,86 @@ +package io.github.taetae98coding.diary.feature.memo.tag + +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.FilterChipDefaults +import androidx.compose.material3.SelectableChipColors +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import io.github.taetae98coding.diary.core.design.system.chip.DiaryFilterChip +import io.github.taetae98coding.diary.core.design.system.icon.TagIcon +import io.github.taetae98coding.diary.library.color.toContrastColor + +@Composable +internal fun PrimaryMemoTag( + uiState: TagUiState, + modifier: Modifier = Modifier, +) { + PrimaryMemoTag( + uiState = uiState, + onClick = { + if (uiState.isSelected) { + uiState.unselect.value() + } else { + uiState.select.value() + } + }, + modifier = modifier, + ) +} + +@Composable +internal fun PrimaryMemoTag( + uiState: TagUiState, + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { + MemoTag( + uiState = uiState, + onClick = onClick, + modifier = modifier, + colors = FilterChipDefaults.filterChipColors( + selectedContainerColor = Color(uiState.color), + selectedLabelColor = Color(uiState.color).toContrastColor(), + selectedLeadingIconColor = Color(uiState.color).toContrastColor(), + ), + ) +} + +@Composable +internal fun MemoTag( + uiState: TagUiState, + modifier: Modifier = Modifier, + colors: SelectableChipColors = FilterChipDefaults.filterChipColors(), +) { + MemoTag( + uiState = uiState, + onClick = { + if (uiState.isSelected) { + uiState.unselect.value() + } else { + uiState.select.value() + } + }, + modifier = modifier, + colors = colors, + ) +} + +@Composable +internal fun MemoTag( + uiState: TagUiState, + onClick: () -> Unit, + modifier: Modifier = Modifier, + colors: SelectableChipColors = FilterChipDefaults.filterChipColors(), +) { + DiaryFilterChip( + selected = uiState.isSelected, + onClick = onClick, + label = { Text(text = uiState.title) }, + modifier = modifier, + leadingIcon = { TagIcon() }, + shape = CircleShape, + colors = colors, + ) +} diff --git a/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/tag/MemoTagNavigationButton.kt b/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/tag/MemoTagNavigationButton.kt new file mode 100644 index 00000000..c5bcc06c --- /dev/null +++ b/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/tag/MemoTagNavigationButton.kt @@ -0,0 +1,6 @@ +package io.github.taetae98coding.diary.feature.memo.tag + +internal sealed class MemoTagNavigationButton { + data object None : MemoTagNavigationButton() + data class NavigateUp(val onNavigateUp: () -> Unit) : MemoTagNavigationButton() +} diff --git a/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/tag/MemoTagScreen.kt b/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/tag/MemoTagScreen.kt new file mode 100644 index 00000000..d30b5148 --- /dev/null +++ b/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/tag/MemoTagScreen.kt @@ -0,0 +1,105 @@ +package io.github.taetae98coding.diary.feature.memo.tag + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Button +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.IconButton +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.minimumInteractiveComponentSize +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import io.github.taetae98coding.diary.core.design.system.icon.NavigateUpIcon +import io.github.taetae98coding.diary.core.design.system.theme.DiaryTheme + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +internal fun MemoTagScreen( + navigateButtonProvider: () -> MemoTagNavigationButton, + onTagAdd: () -> Unit, + memoTagListProvider: () -> List?, + tagListProvider: () -> List?, + modifier: Modifier = Modifier, +) { + Scaffold( + modifier = modifier, + topBar = { + TopAppBar( + title = { Text(text = "메모 태그") }, + navigationIcon = { + when (val button = navigateButtonProvider()) { + is MemoTagNavigationButton.NavigateUp -> { + IconButton(onClick = button.onNavigateUp) { + NavigateUpIcon() + } + } + + is MemoTagNavigationButton.None -> Unit + } + }, + ) + }, + ) { + Content( + onTagAdd = onTagAdd, + memoTagListProvider = memoTagListProvider, + tagListProvider = tagListProvider, + modifier = Modifier.fillMaxWidth() + .padding(it) + .padding(DiaryTheme.dimen.diaryPaddingValues), + ) + } +} + +@Composable +private fun Content( + onTagAdd: () -> Unit, + memoTagListProvider: () -> List?, + tagListProvider: () -> List?, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier, + verticalArrangement = Arrangement.spacedBy(DiaryTheme.dimen.itemSpace), + ) { + TagFlow( + modifier = Modifier.fillMaxWidth() + .weight(3F), + listProvider = memoTagListProvider, + title = { + Text( + text = "캘린더 태그", + modifier = Modifier.fillMaxWidth() + .minimumInteractiveComponentSize() + .padding(horizontal = DiaryTheme.dimen.diaryHorizontalPadding), + ) + }, + empty = { Text(text = "태그가 없어요 🐻") }, + tag = { PrimaryMemoTag(uiState = it) }, + ) + + TagFlow( + modifier = Modifier.fillMaxWidth() + .weight(7F), + listProvider = tagListProvider, + title = { + Text( + text = "태그", + modifier = Modifier.fillMaxWidth() + .minimumInteractiveComponentSize() + .padding(horizontal = DiaryTheme.dimen.diaryHorizontalPadding), + ) + }, + empty = { + Button(onClick = onTagAdd) { + Text(text = "태그 추가하러 가기") + } + }, + tag = { MemoTag(uiState = it) }, + ) + } +} diff --git a/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/tag/TagFlow.kt b/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/tag/TagFlow.kt new file mode 100644 index 00000000..833dd8cd --- /dev/null +++ b/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/tag/TagFlow.kt @@ -0,0 +1,65 @@ +package io.github.taetae98coding.diary.feature.memo.tag + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.FlowRow +import androidx.compose.foundation.layout.IntrinsicSize +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Card +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.runtime.Composable +import androidx.compose.runtime.key +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import io.github.taetae98coding.diary.core.design.system.theme.DiaryTheme + +@OptIn(ExperimentalLayoutApi::class) +@Composable +internal fun TagFlow( + title: @Composable () -> Unit, + listProvider: () -> List?, + empty: @Composable () -> Unit, + tag: @Composable (TagUiState) -> Unit, + modifier: Modifier = Modifier, +) { + Card(modifier = modifier.height(IntrinsicSize.Min)) { + val list = listProvider() + + title() + + if (list == null) { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center, + ) { + CircularProgressIndicator() + } + } else if (list.isEmpty()) { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center, + ) { + empty() + } + } else { + FlowRow( + modifier = Modifier.fillMaxSize() + .verticalScroll(rememberScrollState()) + .padding(DiaryTheme.dimen.itemSpace), + horizontalArrangement = Arrangement.spacedBy(DiaryTheme.dimen.itemSpace, Alignment.CenterHorizontally), + verticalArrangement = Arrangement.spacedBy(DiaryTheme.dimen.itemSpace, Alignment.CenterVertically), + ) { + list.forEach { + key(it.id) { + tag(it) + } + } + } + } + } +} diff --git a/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/tag/TagUiState.kt b/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/tag/TagUiState.kt new file mode 100644 index 00000000..054d8aac --- /dev/null +++ b/app/feature/memo/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/memo/tag/TagUiState.kt @@ -0,0 +1,12 @@ +package io.github.taetae98coding.diary.feature.memo.tag + +import io.github.taetae98coding.diary.core.compose.runtime.SkipProperty + +public data class TagUiState( + val id: String, + val title: String, + val isSelected: Boolean, + val color: Int, + val select: SkipProperty<() -> Unit>, + val unselect: SkipProperty<() -> Unit>, +) diff --git a/app/feature/more/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/more/MoreScreen.kt b/app/feature/more/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/more/MoreScreen.kt index a3ab5ac9..bc02c5df 100644 --- a/app/feature/more/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/more/MoreScreen.kt +++ b/app/feature/more/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/more/MoreScreen.kt @@ -13,11 +13,8 @@ import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import io.github.taetae98coding.diary.core.design.system.theme.DiaryTheme -import io.github.taetae98coding.diary.core.resources.Res -import io.github.taetae98coding.diary.core.resources.more import io.github.taetae98coding.diary.feature.more.account.MoreAccount import io.github.taetae98coding.diary.feature.more.account.state.MoreAccountUiState -import org.jetbrains.compose.resources.stringResource @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -30,7 +27,7 @@ internal fun MoreScreen( Scaffold( modifier = modifier, topBar = { - TopAppBar(title = { Text(text = stringResource(Res.string.more)) }) + TopAppBar(title = { Text(text = "더보기") }) }, ) { Content( diff --git a/app/feature/more/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/more/account/MoreAccount.kt b/app/feature/more/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/more/account/MoreAccount.kt index 9214c753..9d5d5f8c 100644 --- a/app/feature/more/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/more/account/MoreAccount.kt +++ b/app/feature/more/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/more/account/MoreAccount.kt @@ -22,18 +22,12 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import io.github.taetae98coding.diary.core.design.system.chip.DiaryAssistChip -import io.github.taetae98coding.diary.core.resources.Res -import io.github.taetae98coding.diary.core.resources.guest -import io.github.taetae98coding.diary.core.resources.icon.AccountIcon -import io.github.taetae98coding.diary.core.resources.icon.LoginIcon -import io.github.taetae98coding.diary.core.resources.icon.LogoutIcon -import io.github.taetae98coding.diary.core.resources.join -import io.github.taetae98coding.diary.core.resources.login -import io.github.taetae98coding.diary.core.resources.logout +import io.github.taetae98coding.diary.core.design.system.icon.AccountIcon +import io.github.taetae98coding.diary.core.design.system.icon.LoginIcon +import io.github.taetae98coding.diary.core.design.system.icon.LogoutIcon import io.github.taetae98coding.diary.feature.more.account.state.MoreAccountUiState import io.github.taetae98coding.diary.library.color.multiplyAlpha import io.github.taetae98coding.diary.library.shimmer.m3.shimmer -import org.jetbrains.compose.resources.stringResource @Composable internal fun MoreAccount( @@ -111,7 +105,7 @@ private fun Email( is MoreAccountUiState.Guest -> { Text( - text = stringResource(Res.string.guest), + text = "게스트", modifier = modifier, ) } @@ -165,7 +159,7 @@ private fun ButtonRow( ) { DiaryAssistChip( onClick = onLogin, - label = { Text(text = stringResource(Res.string.login)) }, + label = { Text(text = "로그인") }, modifier = Modifier.animateItem(), leadingIcon = { LoginIcon() }, shape = CircleShape, @@ -178,7 +172,7 @@ private fun ButtonRow( ) { DiaryAssistChip( onClick = onJoin, - label = { Text(text = stringResource(Res.string.join)) }, + label = { Text(text = "회원가입") }, modifier = Modifier.animateItem(), leadingIcon = { AccountIcon() }, shape = CircleShape, @@ -193,7 +187,7 @@ private fun ButtonRow( ) { DiaryAssistChip( onClick = uiState.logout, - label = { Text(text = stringResource(Res.string.logout)) }, + label = { Text(text = "로그아웃") }, modifier = Modifier.animateItem(), leadingIcon = { LogoutIcon() }, shape = CircleShape, diff --git a/app/feature/tag/README.md b/app/feature/tag/README.md new file mode 100644 index 00000000..4b47cba6 --- /dev/null +++ b/app/feature/tag/README.md @@ -0,0 +1,3 @@ +# :app:feature:tag module +## Dependency graph +![Dependency graph](../../../docs/images/graphs/dep_graph_app_feature_tag.svg) diff --git a/app/feature/tag/build.gradle.kts b/app/feature/tag/build.gradle.kts new file mode 100644 index 00000000..f6887fa8 --- /dev/null +++ b/app/feature/tag/build.gradle.kts @@ -0,0 +1,17 @@ +plugins { + id("diary.app.feature") +} + +kotlin { + sourceSets { + commonMain { + dependencies { + implementation(project(":app:domain:tag")) + } + } + } +} + +android { + namespace = "${Build.NAMESPACE}.feature.tag" +} diff --git a/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/TagFeatureModule.kt b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/TagFeatureModule.kt new file mode 100644 index 00000000..9247bacd --- /dev/null +++ b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/TagFeatureModule.kt @@ -0,0 +1,8 @@ +package io.github.taetae98coding.diary.feature.tag + +import org.koin.core.annotation.ComponentScan +import org.koin.core.annotation.Module + +@Module +@ComponentScan +public class TagFeatureModule diff --git a/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/TagNavigate.kt b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/TagNavigate.kt new file mode 100644 index 00000000..58b76c92 --- /dev/null +++ b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/TagNavigate.kt @@ -0,0 +1,9 @@ +package io.github.taetae98coding.diary.feature.tag + +internal sealed class TagNavigate { + data object None : TagNavigate() + + data object Add : TagNavigate() + + data class Tag(val tagId: String) : TagNavigate() +} diff --git a/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/TagNavigation.kt b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/TagNavigation.kt new file mode 100644 index 00000000..6bb76baf --- /dev/null +++ b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/TagNavigation.kt @@ -0,0 +1,35 @@ +package io.github.taetae98coding.diary.feature.tag + +import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.compose.composable +import io.github.taetae98coding.diary.core.compose.adaptive.isListVisible +import io.github.taetae98coding.diary.core.navigation.tag.TagAddDestination +import io.github.taetae98coding.diary.core.navigation.tag.TagDestination +import io.github.taetae98coding.diary.core.navigation.tag.TagDetailDestination +import io.github.taetae98coding.diary.feature.tag.add.TagAddRoute +import io.github.taetae98coding.diary.feature.tag.detail.TagDetailRoute + +@OptIn(ExperimentalMaterial3AdaptiveApi::class) +public fun NavGraphBuilder.tagNavigation( + navController: NavController, +) { + composable { backStackEntry -> + TagRoute( + onScaffoldValueChange = { backStackEntry.savedStateHandle["app_navigation_visible"] = it.isListVisible() }, + ) + } + + composable { + TagAddRoute( + navigateUp = navController::popBackStack, + ) + } + + composable { + TagDetailRoute( + navigateUp = navController::popBackStack, + ) + } +} diff --git a/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/TagRoute.kt b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/TagRoute.kt new file mode 100644 index 00000000..c53b3978 --- /dev/null +++ b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/TagRoute.kt @@ -0,0 +1,247 @@ +package io.github.taetae98coding.diary.feature.tag + +import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi +import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo +import androidx.compose.material3.adaptive.layout.AnimatedPane +import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffold +import androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole +import androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue +import androidx.compose.material3.adaptive.layout.calculatePaneScaffoldDirective +import androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator +import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.window.core.layout.WindowWidthSizeClass +import io.github.taetae98coding.diary.core.compose.adaptive.isDetailVisible +import io.github.taetae98coding.diary.core.compose.adaptive.isListVisible +import io.github.taetae98coding.diary.core.compose.back.KBackHandler +import io.github.taetae98coding.diary.feature.tag.add.TagAddViewModel +import io.github.taetae98coding.diary.feature.tag.add.rememberTagDetailScreenAddState +import io.github.taetae98coding.diary.feature.tag.detail.TagDetailActionButton +import io.github.taetae98coding.diary.feature.tag.detail.TagDetailFloatingButton +import io.github.taetae98coding.diary.feature.tag.detail.TagDetailNavigationButton +import io.github.taetae98coding.diary.feature.tag.detail.TagDetailScreen +import io.github.taetae98coding.diary.feature.tag.detail.TagDetailViewModel +import io.github.taetae98coding.diary.feature.tag.detail.rememberTagDetailScreenDetailState +import io.github.taetae98coding.diary.feature.tag.list.TagListFloatingButton +import io.github.taetae98coding.diary.feature.tag.list.TagListScreen +import io.github.taetae98coding.diary.feature.tag.list.TagListViewModel +import io.github.taetae98coding.diary.feature.tag.list.rememberTagListScreenState +import org.koin.compose.viewmodel.koinViewModel + +@OptIn(ExperimentalMaterial3AdaptiveApi::class) +@Composable +internal fun TagRoute( + onScaffoldValueChange: (ThreePaneScaffoldValue) -> Unit, + modifier: Modifier = Modifier, + listViewModel: TagListViewModel = koinViewModel(), + addViewModel: TagAddViewModel = koinViewModel(), + detailViewModel: TagDetailViewModel = koinViewModel(), +) { + val windowAdaptiveInfo = currentWindowAdaptiveInfo() + val navigator = rememberListDetailPaneScaffoldNavigator(scaffoldDirective = calculatePaneScaffoldDirective(windowAdaptiveInfo)) + + var tagNavigate by remember { mutableStateOf(TagNavigate.None) } + var detailPaneRefreshCount by remember { mutableIntStateOf(0) } + + ListDetailPaneScaffold( + directive = navigator.scaffoldDirective, + value = navigator.scaffoldValue, + listPane = { + AnimatedPane { + val isFloatingVisible by remember { + derivedStateOf { + if (navigator.isDetailVisible()) { + navigator.currentDestination?.content != null + } else { + true + } + } + } + + val list by listViewModel.list.collectAsStateWithLifecycle() + val uiState by listViewModel.uiState.collectAsStateWithLifecycle() + + TagListScreen( + state = rememberTagListScreenState(), + floatingButtonProvider = { + if (isFloatingVisible) { + TagListFloatingButton.Add( + onAdd = { + if (navigator.isDetailVisible()) { + tagNavigate = TagNavigate.Add + } else { + navigator.navigateTo(ThreePaneScaffoldRole.Primary) + } + }, + ) + } else { + TagListFloatingButton.None + } + }, + listProvider = { list }, + onTag = { + if (navigator.isDetailVisible() && navigator.currentDestination?.content != null) { + tagNavigate = TagNavigate.Tag(it) + } else { + navigator.navigateTo(ThreePaneScaffoldRole.Primary, it) + detailPaneRefreshCount++ + } + }, + uiStateProvider = { uiState }, + ) + } + }, + detailPane = { + AnimatedPane { + val tagDetail by detailViewModel.tagDetail.collectAsStateWithLifecycle() + val isAdd = remember(detailPaneRefreshCount) { navigator.currentDestination?.content == null } + val state = if (isAdd) { + rememberTagDetailScreenAddState() + } else { + rememberTagDetailScreenDetailState( + onUpdate = { + when (val navigate = tagNavigate) { + is TagNavigate.Add -> { + navigator.navigateTo(ThreePaneScaffoldRole.Primary) + detailPaneRefreshCount++ + tagNavigate = TagNavigate.None + } + + is TagNavigate.Tag -> { + navigator.navigateTo(ThreePaneScaffoldRole.Primary, navigate.tagId) + tagNavigate = TagNavigate.None + } + + is TagNavigate.None -> { + navigator.navigateBack() + } + } + }, + onDelete = { + navigator.navigateBack() + if (windowAdaptiveInfo.windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.EXPANDED) { + detailPaneRefreshCount++ + } + }, + detailProvider = { tagDetail }, + ) + } + val isNavigateUpVisible = remember(windowAdaptiveInfo) { + if (windowAdaptiveInfo.windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.EXPANDED) { + !navigator.isListVisible() + } else { + true + } + } + val detailActionButton by detailViewModel.actionButton.collectAsStateWithLifecycle() + val uiState = if (isAdd) { + addViewModel.uiState.collectAsStateWithLifecycle() + } else { + detailViewModel.uiState.collectAsStateWithLifecycle() + } + + TagDetailScreen( + state = state, + titleProvider = { + if (isAdd) { + "태그 추가" + } else { + tagDetail?.title + } + }, + navigateButtonProvider = { + if (isNavigateUpVisible) { + TagDetailNavigationButton.NavigateUp( + onNavigateUp = { + if (isAdd) { + navigator.navigateBack() + } else { + detailViewModel.update(state.tagDetail) + } + }, + ) + } else { + TagDetailNavigationButton.None + } + }, + actionButtonProvider = { + if (isAdd) { + TagDetailActionButton.None + } else { + detailActionButton + } + }, + floatingButtonProvider = { + if (isAdd) { + TagDetailFloatingButton.Add(onAdd = { addViewModel.add(state.tagDetail) }) + } else { + TagDetailFloatingButton.None + } + }, + uiStateProvider = { uiState.value }, + ) + + LaunchedEffect(tagNavigate) { + when (tagNavigate) { + is TagNavigate.Add -> { + detailViewModel.update(state.tagDetail) + } + + is TagNavigate.Tag -> { + detailViewModel.update(state.tagDetail) + } + + is TagNavigate.None -> Unit + } + } + } + }, + modifier = modifier, + ) + + LaunchedScaffoldValue( + navigator = navigator, + onScaffoldValueChange = onScaffoldValueChange, + ) + + LaunchedFetch( + navigator = navigator, + detailViewModel = detailViewModel, + ) + + KBackHandler( + isEnabled = navigator.canNavigateBack(), + onBack = navigator::navigateBack, + ) +} + +@OptIn(ExperimentalMaterial3AdaptiveApi::class) +@Composable +private fun LaunchedFetch( + navigator: ThreePaneScaffoldNavigator, + detailViewModel: TagDetailViewModel, +) { + LaunchedEffect(navigator.currentDestination?.content, detailViewModel) { + detailViewModel.fetch(navigator.currentDestination?.content) + } +} + +@OptIn(ExperimentalMaterial3AdaptiveApi::class) +@Composable +private fun LaunchedScaffoldValue( + navigator: ThreePaneScaffoldNavigator, + onScaffoldValueChange: (ThreePaneScaffoldValue) -> Unit, +) { + LaunchedEffect(navigator.scaffoldValue) { + onScaffoldValueChange(navigator.scaffoldValue) + } +} diff --git a/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/add/RememberTagDetailScreenAddState.kt b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/add/RememberTagDetailScreenAddState.kt new file mode 100644 index 00000000..9a3c3f47 --- /dev/null +++ b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/add/RememberTagDetailScreenAddState.kt @@ -0,0 +1,23 @@ +package io.github.taetae98coding.diary.feature.tag.add + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import io.github.taetae98coding.diary.core.design.system.diary.color.rememberDiaryColorState +import io.github.taetae98coding.diary.core.design.system.diary.component.rememberDiaryComponentState +import io.github.taetae98coding.diary.feature.tag.detail.TagDetailScreenState + +@Composable +internal fun rememberTagDetailScreenAddState(): TagDetailScreenState.Add { + val coroutineScope = rememberCoroutineScope() + val componentState = rememberDiaryComponentState() + val colorState = rememberDiaryColorState() + + return remember { + TagDetailScreenState.Add( + coroutineScope = coroutineScope, + componentState = componentState, + colorState = colorState, + ) + } +} diff --git a/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/add/TagAddRoute.kt b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/add/TagAddRoute.kt new file mode 100644 index 00000000..8dcb0c4c --- /dev/null +++ b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/add/TagAddRoute.kt @@ -0,0 +1,51 @@ +package io.github.taetae98coding.diary.feature.tag.add + +import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi +import androidx.compose.material3.adaptive.layout.AnimatedPane +import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffold +import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import io.github.taetae98coding.diary.feature.tag.detail.TagDetailActionButton +import io.github.taetae98coding.diary.feature.tag.detail.TagDetailFloatingButton +import io.github.taetae98coding.diary.feature.tag.detail.TagDetailNavigationButton +import io.github.taetae98coding.diary.feature.tag.detail.TagDetailScreen +import org.koin.compose.viewmodel.koinViewModel + +@OptIn(ExperimentalMaterial3AdaptiveApi::class) +@Composable +internal fun TagAddRoute( + navigateUp: () -> Unit, + modifier: Modifier = Modifier, + addViewModel: TagAddViewModel = koinViewModel(), +) { + val navigator = rememberListDetailPaneScaffoldNavigator() + + ListDetailPaneScaffold( + directive = navigator.scaffoldDirective, + value = navigator.scaffoldValue, + listPane = { + AnimatedPane { + val state = rememberTagDetailScreenAddState() + val uiState by addViewModel.uiState.collectAsStateWithLifecycle() + + TagDetailScreen( + state = state, + titleProvider = { "태그 추가" }, + navigateButtonProvider = { + TagDetailNavigationButton.NavigateUp(onNavigateUp = navigateUp) + }, + actionButtonProvider = { TagDetailActionButton.None }, + floatingButtonProvider = { TagDetailFloatingButton.Add(onAdd = { addViewModel.add(state.tagDetail) }) }, + uiStateProvider = { uiState }, + ) + } + }, + detailPane = { + + }, + modifier = modifier, + ) +} diff --git a/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/add/TagAddViewModel.kt b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/add/TagAddViewModel.kt new file mode 100644 index 00000000..0a181318 --- /dev/null +++ b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/add/TagAddViewModel.kt @@ -0,0 +1,48 @@ +package io.github.taetae98coding.diary.feature.tag.add + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import io.github.taetae98coding.diary.common.exception.tag.TagTitleBlankException +import io.github.taetae98coding.diary.core.model.tag.TagDetail +import io.github.taetae98coding.diary.domain.tag.usecase.AddTagUseCase +import io.github.taetae98coding.diary.feature.tag.detail.TagDetailScreenUiState +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import org.koin.android.annotation.KoinViewModel + +@KoinViewModel +internal class TagAddViewModel( + private val addTagUseCase: AddTagUseCase, +) : ViewModel() { + private val _uiState = MutableStateFlow(TagDetailScreenUiState(onMessageShow = ::clearMessage)) + val uiState = _uiState.asStateFlow() + + fun add(detail: TagDetail) { + viewModelScope.launch { + _uiState.update { it.copy(isProgress = true) } + addTagUseCase(detail) + .onSuccess { _uiState.update { it.copy(isProgress = false, isAdd = true) } } + .onFailure { handleThrowable(it) } + } + } + + private fun handleThrowable(throwable: Throwable) { + when (throwable) { + is TagTitleBlankException -> _uiState.update { it.copy(isProgress = false, isTitleBlankError = true) } + else -> _uiState.update { it.copy(isProgress = false, isUnknownError = true) } + } + } + + private fun clearMessage() { + _uiState.update { + it.copy( + isAdd = false, + isTitleBlankError = false, + isUnknownError = false, + ) + } + } +} diff --git a/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/RememberTagDetailScreenDetailState.kt b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/RememberTagDetailScreenDetailState.kt new file mode 100644 index 00000000..2af8d380 --- /dev/null +++ b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/RememberTagDetailScreenDetailState.kt @@ -0,0 +1,38 @@ +package io.github.taetae98coding.diary.feature.tag.detail + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.graphics.Color +import io.github.taetae98coding.diary.core.design.system.diary.color.rememberDiaryColorState +import io.github.taetae98coding.diary.core.design.system.diary.component.rememberDiaryComponentState +import io.github.taetae98coding.diary.core.model.tag.TagDetail + +@Composable +internal fun rememberTagDetailScreenDetailState( + onUpdate: () -> Unit, + onDelete: () -> Unit, + detailProvider: () -> TagDetail?, +): TagDetailScreenState.Detail { + val detail = detailProvider() + val coroutineScope = rememberCoroutineScope() + val componentState = rememberDiaryComponentState( + inputs = arrayOf(detail?.title, detail?.description), + initialTitle = detail?.title.orEmpty(), + initialDescription = detail?.description.orEmpty(), + ) + val colorState = rememberDiaryColorState( + inputs = arrayOf(detail?.color), + initialColor = detail?.color?.let { Color(it) } ?: Color.Unspecified, + ) + + return remember(componentState, colorState) { + TagDetailScreenState.Detail( + onUpdate = onUpdate, + onDelete = onDelete, + coroutineScope = coroutineScope, + componentState = componentState, + colorState = colorState, + ) + } +} diff --git a/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailActionButton.kt b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailActionButton.kt new file mode 100644 index 00000000..c0b3b9e8 --- /dev/null +++ b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailActionButton.kt @@ -0,0 +1,11 @@ +package io.github.taetae98coding.diary.feature.tag.detail + +internal sealed class TagDetailActionButton { + data object None : TagDetailActionButton() + + data class FinishAndDetail( + val isFinish: Boolean, + val onFinishChange: (Boolean) -> Unit, + val delete: () -> Unit, + ) : TagDetailActionButton() +} diff --git a/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailFloatingButton.kt b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailFloatingButton.kt new file mode 100644 index 00000000..2ecc5321 --- /dev/null +++ b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailFloatingButton.kt @@ -0,0 +1,6 @@ +package io.github.taetae98coding.diary.feature.tag.detail + +internal sealed class TagDetailFloatingButton { + data object None : TagDetailFloatingButton() + data class Add(val onAdd: () -> Unit) : TagDetailFloatingButton() +} diff --git a/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailNavigationButton.kt b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailNavigationButton.kt new file mode 100644 index 00000000..9bf91a40 --- /dev/null +++ b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailNavigationButton.kt @@ -0,0 +1,6 @@ +package io.github.taetae98coding.diary.feature.tag.detail + +internal sealed class TagDetailNavigationButton { + data object None : TagDetailNavigationButton() + data class NavigateUp(val onNavigateUp: () -> Unit) : TagDetailNavigationButton() +} diff --git a/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailRoute.kt b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailRoute.kt new file mode 100644 index 00000000..001b445b --- /dev/null +++ b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailRoute.kt @@ -0,0 +1,55 @@ +package io.github.taetae98coding.diary.feature.tag.detail + +import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi +import androidx.compose.material3.adaptive.layout.AnimatedPane +import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffold +import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import org.koin.compose.viewmodel.koinViewModel + +@OptIn(ExperimentalMaterial3AdaptiveApi::class) +@Composable +internal fun TagDetailRoute( + navigateUp: () -> Unit, + modifier: Modifier = Modifier, + detailViewModel: TagDetailViewModel = koinViewModel(), +) { + val navigator = rememberListDetailPaneScaffoldNavigator() + + ListDetailPaneScaffold( + directive = navigator.scaffoldDirective, + value = navigator.scaffoldValue, + listPane = { + AnimatedPane { + val tagDetail by detailViewModel.tagDetail.collectAsStateWithLifecycle() + val state = rememberTagDetailScreenDetailState( + onUpdate = navigateUp, + onDelete = navigateUp, + detailProvider = { tagDetail }, + ) + val actionButton by detailViewModel.actionButton.collectAsStateWithLifecycle() + val uiState by detailViewModel.uiState.collectAsStateWithLifecycle() + + TagDetailScreen( + state = state, + titleProvider = { tagDetail?.title }, + navigateButtonProvider = { + TagDetailNavigationButton.NavigateUp( + onNavigateUp = { detailViewModel.update(state.tagDetail) }, + ) + }, + actionButtonProvider = { actionButton }, + floatingButtonProvider = { TagDetailFloatingButton.None }, + uiStateProvider = { uiState }, + ) + } + }, + detailPane = { + + }, + modifier = modifier, + ) +} diff --git a/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailScreen.kt b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailScreen.kt new file mode 100644 index 00000000..e7ec2905 --- /dev/null +++ b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailScreen.kt @@ -0,0 +1,228 @@ +package io.github.taetae98coding.diary.feature.tag.detail + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.scaleIn +import androidx.compose.animation.scaleOut +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.IconButton +import androidx.compose.material3.IconToggleButton +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import io.github.taetae98coding.diary.core.compose.button.FloatingAddButton +import io.github.taetae98coding.diary.core.design.system.diary.color.DiaryColor +import io.github.taetae98coding.diary.core.design.system.diary.component.DiaryComponent +import io.github.taetae98coding.diary.core.design.system.emoji.Emoji +import io.github.taetae98coding.diary.core.design.system.icon.DeleteIcon +import io.github.taetae98coding.diary.core.design.system.icon.FinishIcon +import io.github.taetae98coding.diary.core.design.system.icon.NavigateUpIcon +import io.github.taetae98coding.diary.core.design.system.theme.DiaryTheme + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +internal fun TagDetailScreen( + state: TagDetailScreenState, + titleProvider: () -> String?, + navigateButtonProvider: () -> TagDetailNavigationButton, + actionButtonProvider: () -> TagDetailActionButton, + floatingButtonProvider: () -> TagDetailFloatingButton, + uiStateProvider: () -> TagDetailScreenUiState, + modifier: Modifier = Modifier, +) { + Scaffold( + modifier = modifier, + topBar = { + TopAppBar( + title = { titleProvider()?.let { Text(text = it) } }, + navigationIcon = { + when (val button = navigateButtonProvider()) { + is TagDetailNavigationButton.NavigateUp -> { + IconButton(onClick = button.onNavigateUp) { + NavigateUpIcon() + } + } + + is TagDetailNavigationButton.None -> Unit + } + }, + actions = { + val button = actionButtonProvider() + + AnimatedVisibility( + visible = button is TagDetailActionButton.FinishAndDetail, + enter = fadeIn(), + exit = fadeOut(), + ) { + val isFinish = if (button is TagDetailActionButton.FinishAndDetail) { + button.isFinish + } else { + false + } + + IconToggleButton( + checked = isFinish, + onCheckedChange = { + if (button is TagDetailActionButton.FinishAndDetail) { + button.onFinishChange(it) + } + }, + ) { + FinishIcon() + } + } + + AnimatedVisibility( + visible = button is TagDetailActionButton.FinishAndDetail, + enter = fadeIn(), + exit = fadeOut(), + ) { + IconButton( + onClick = { + if (button is TagDetailActionButton.FinishAndDetail) { + button.delete() + } + }, + ) { + DeleteIcon() + } + } + }, + ) + }, + snackbarHost = { SnackbarHost(hostState = state.hostState) }, + floatingActionButton = { + val button = floatingButtonProvider() + + AnimatedVisibility( + visible = button is TagDetailFloatingButton.Add, + enter = scaleIn(), + exit = scaleOut(), + ) { + val isProgress by remember { derivedStateOf { uiStateProvider().isProgress } } + + FloatingAddButton( + onClick = { + if (button is TagDetailFloatingButton.Add) { + button.onAdd() + } + }, + progressProvider = { isProgress }, + ) + } + }, + ) { + Content( + state = state, + modifier = Modifier.fillMaxSize() + .padding(DiaryTheme.dimen.screenPaddingValues) + .padding(it), + ) + } + + Message( + state = state, + uiStateProvider = uiStateProvider, + ) + + LaunchedFocus(state = state) +} + +@Composable +private fun Message( + state: TagDetailScreenState, + uiStateProvider: () -> TagDetailScreenUiState, +) { + val uiState = uiStateProvider() + + LaunchedEffect( + uiState.isAdd, + uiState.isDelete, + uiState.isUpdate, + uiState.isTitleBlankError, + uiState.isUnknownError, + ) { + if (!uiState.hasMessage) return@LaunchedEffect + + when { + uiState.isAdd -> { + state.showMessage("태그 추가 ${Emoji.congratulate.random()}") + state.clearInput() + state.requestTitleFocus() + } + + uiState.isDelete -> { + if (state is TagDetailScreenState.Detail) { + state.onDelete() + } + } + + uiState.isUpdate -> { + if (state is TagDetailScreenState.Detail) { + state.onUpdate() + } + } + + uiState.isTitleBlankError -> { + state.showMessage("제목을 입력해 주세요 ${Emoji.check.random()}") + state.titleError() + } + + uiState.isUnknownError -> state.showMessage("알 수 없는 에러가 발생했어요 잠시 후 다시 시도해 주세요 ${Emoji.error.random()}") + } + + uiState.onMessageShow() + } +} + +@Composable +private fun LaunchedFocus( + state: TagDetailScreenState, +) { + LaunchedEffect(state) { + if (state is TagDetailScreenState.Add) { + state.requestTitleFocus() + } + } +} + +@Composable +private fun Content( + state: TagDetailScreenState, + modifier: Modifier = Modifier, +) { + Column( + modifier = Modifier.verticalScroll(state = rememberScrollState()) + .then(modifier), + verticalArrangement = Arrangement.spacedBy(DiaryTheme.dimen.itemSpace), + ) { + DiaryComponent(state = state.componentState) + Row { + DiaryColor( + state = state.colorState, + modifier = Modifier.weight(1F) + .height(100.dp), + ) + + Spacer(modifier = Modifier.weight(1F)) + } + } +} diff --git a/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailScreenState.kt b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailScreenState.kt new file mode 100644 index 00000000..3b4bbdc3 --- /dev/null +++ b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailScreenState.kt @@ -0,0 +1,61 @@ +package io.github.taetae98coding.diary.feature.tag.detail + +import androidx.compose.material3.SnackbarHostState +import androidx.compose.ui.graphics.toArgb +import io.github.taetae98coding.diary.core.design.system.diary.color.DiaryColorState +import io.github.taetae98coding.diary.core.design.system.diary.component.DiaryComponentState +import io.github.taetae98coding.diary.core.model.tag.TagDetail +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch + +internal sealed class TagDetailScreenState { + protected abstract val coroutineScope: CoroutineScope + + abstract val componentState: DiaryComponentState + abstract val colorState: DiaryColorState + + private var messageJob: Job? = null + + val hostState: SnackbarHostState = SnackbarHostState() + + data class Add( + override val coroutineScope: CoroutineScope, + override val componentState: DiaryComponentState, + override val colorState: DiaryColorState, + ) : TagDetailScreenState() + + data class Detail( + val onUpdate: () -> Unit, + val onDelete: () -> Unit, + override val coroutineScope: CoroutineScope, + override val componentState: DiaryComponentState, + override val colorState: DiaryColorState, + ) : TagDetailScreenState() + + val tagDetail: TagDetail + get() { + return TagDetail( + title = componentState.title, + description = componentState.description, + color = colorState.color.toArgb(), + ) + } + + fun requestTitleFocus() { + componentState.requestTitleFocus() + } + + fun clearInput() { + componentState.clearInput() + } + + fun titleError() { + componentState.titleError() + } + + fun showMessage(message: String) { + messageJob?.cancel() + messageJob = coroutineScope.launch { hostState.showSnackbar(message) } + } +} diff --git a/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailScreenUiState.kt b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailScreenUiState.kt new file mode 100644 index 00000000..e219ccbf --- /dev/null +++ b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailScreenUiState.kt @@ -0,0 +1,13 @@ +package io.github.taetae98coding.diary.feature.tag.detail + +internal data class TagDetailScreenUiState( + val isProgress: Boolean = false, + val isAdd: Boolean = false, + val isDelete: Boolean = false, + val isUpdate: Boolean = false, + val isTitleBlankError: Boolean = false, + val isUnknownError: Boolean = false, + val onMessageShow: () -> Unit = {}, +) { + val hasMessage = isAdd || isDelete || isUpdate || isTitleBlankError || isUnknownError +} diff --git a/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailViewModel.kt b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailViewModel.kt new file mode 100644 index 00000000..83a27eec --- /dev/null +++ b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/detail/TagDetailViewModel.kt @@ -0,0 +1,115 @@ +package io.github.taetae98coding.diary.feature.tag.detail + +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import io.github.taetae98coding.diary.core.model.tag.TagDetail +import io.github.taetae98coding.diary.core.navigation.tag.TagDetailDestination +import io.github.taetae98coding.diary.domain.tag.usecase.DeleteTagUseCase +import io.github.taetae98coding.diary.domain.tag.usecase.FindTagUseCase +import io.github.taetae98coding.diary.domain.tag.usecase.FinishTagUseCase +import io.github.taetae98coding.diary.domain.tag.usecase.RestartTagUseCase +import io.github.taetae98coding.diary.domain.tag.usecase.UpdateTagUseCase +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.mapLatest +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import org.koin.android.annotation.KoinViewModel + +@OptIn(ExperimentalCoroutinesApi::class) +@KoinViewModel +internal class TagDetailViewModel( + private val savedStateHandle: SavedStateHandle, + private val findTagUseCase: FindTagUseCase, + private val finishTagUseCase: FinishTagUseCase, + private val restartTagUseCase: RestartTagUseCase, + private val deleteTagUseCase: DeleteTagUseCase, + private val updateTagUseCase: UpdateTagUseCase, +) : ViewModel() { + private val tagId = savedStateHandle.getStateFlow(TagDetailDestination.TAG_ID, null) + + private val tag = tagId.filterNotNull() + .flatMapLatest { findTagUseCase(it) } + .mapLatest { it.getOrNull() } + .stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5_000), + initialValue = null, + ) + + private val _uiState = MutableStateFlow(TagDetailScreenUiState(onMessageShow = ::clearMessage)) + val uiState = _uiState.asStateFlow() + + val tagDetail = tag.mapLatest { it?.detail } + .stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5_000), + initialValue = null, + ) + + val actionButton = tag.mapLatest { + TagDetailActionButton.FinishAndDetail( + isFinish = it?.isFinish ?: false, + onFinishChange = ::onFinishChange, + delete = ::delete, + ) + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5_000), + initialValue = TagDetailActionButton.FinishAndDetail( + isFinish = false, + onFinishChange = ::onFinishChange, + delete = ::delete, + ), + ) + + private fun onFinishChange(isFinish: Boolean) { + viewModelScope.launch { + if (isFinish) { + finishTagUseCase(tagId.value).onFailure { handleThrowable() } + } else { + restartTagUseCase(tagId.value).onFailure { handleThrowable() } + } + } + } + + private fun delete() { + viewModelScope.launch { + deleteTagUseCase(tagId.value) + .onSuccess { _uiState.update { it.copy(isDelete = true) } } + .onFailure { handleThrowable() } + } + } + + private fun handleThrowable() { + _uiState.update { it.copy(isUnknownError = true) } + } + + private fun clearMessage() { + _uiState.update { + it.copy( + isUpdate = false, + isDelete = false, + isUnknownError = false, + ) + } + } + + fun update(detail: TagDetail) { + viewModelScope.launch { + updateTagUseCase(tagId.value, detail) + .onSuccess { _uiState.update { it.copy(isUpdate = true) } } + .onFailure { handleThrowable() } + } + } + + fun fetch(tagId: String?) { + savedStateHandle[TagDetailDestination.TAG_ID] = tagId + } +} diff --git a/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/list/RememberTagListScreenState.kt b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/list/RememberTagListScreenState.kt new file mode 100644 index 00000000..34089879 --- /dev/null +++ b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/list/RememberTagListScreenState.kt @@ -0,0 +1,14 @@ +package io.github.taetae98coding.diary.feature.tag.list + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope + +@Composable +internal fun rememberTagListScreenState(): TagListScreenState { + val coroutineScope = rememberCoroutineScope() + + return remember { + TagListScreenState(coroutineScope = coroutineScope) + } +} diff --git a/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/list/TagListFloatingButton.kt b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/list/TagListFloatingButton.kt new file mode 100644 index 00000000..413e5b5b --- /dev/null +++ b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/list/TagListFloatingButton.kt @@ -0,0 +1,6 @@ +package io.github.taetae98coding.diary.feature.tag.list + +internal sealed class TagListFloatingButton { + data object None : TagListFloatingButton() + data class Add(val onAdd: () -> Unit) : TagListFloatingButton() +} diff --git a/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/list/TagListItemUiState.kt b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/list/TagListItemUiState.kt new file mode 100644 index 00000000..76c41c84 --- /dev/null +++ b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/list/TagListItemUiState.kt @@ -0,0 +1,10 @@ +package io.github.taetae98coding.diary.feature.tag.list + +import io.github.taetae98coding.diary.core.compose.runtime.SkipProperty + +internal data class TagListItemUiState( + val id: String, + val title: String, + val finish: SkipProperty<() -> Unit>, + val delete: SkipProperty<() -> Unit>, +) diff --git a/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/list/TagListScreen.kt b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/list/TagListScreen.kt new file mode 100644 index 00000000..daa162d7 --- /dev/null +++ b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/list/TagListScreen.kt @@ -0,0 +1,192 @@ +package io.github.taetae98coding.diary.feature.tag.list + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.scaleIn +import androidx.compose.animation.scaleOut +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.Card +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import io.github.taetae98coding.diary.core.compose.button.FloatingAddButton +import io.github.taetae98coding.diary.core.compose.swipe.FinishAndDeleteSwipeBox +import io.github.taetae98coding.diary.core.design.system.emoji.Emoji +import io.github.taetae98coding.diary.core.design.system.theme.DiaryTheme + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +internal fun TagListScreen( + state: TagListScreenState, + floatingButtonProvider: () -> TagListFloatingButton, + listProvider: () -> List?, + onTag: (String) -> Unit, + uiStateProvider: () -> TagListScreenUiState, + modifier: Modifier = Modifier, +) { + Scaffold( + modifier = modifier, + topBar = { + TopAppBar( + title = { Text(text = "태그") }, + ) + }, + snackbarHost = { SnackbarHost(hostState = state.hostState) }, + floatingActionButton = { + val button = floatingButtonProvider() + + AnimatedVisibility( + visible = button is TagListFloatingButton.Add, + enter = scaleIn(), + exit = scaleOut(), + ) { + FloatingAddButton( + onClick = { + if (button is TagListFloatingButton.Add) { + button.onAdd() + } + }, + ) + } + }, + ) { + Content( + listProvider = listProvider, + onTag = onTag, + modifier = Modifier.fillMaxSize() + .padding(it), + ) + } + + Message( + state = state, + uiStateProvider = uiStateProvider, + ) +} + +@Composable +private fun Message( + state: TagListScreenState, + uiStateProvider: () -> TagListScreenUiState, +) { + val uiState = uiStateProvider() + + LaunchedEffect( + uiState.finishTagId, + uiState.deleteTagId, + uiState.isUnknownError, + ) { + if (!uiState.hasMessage) return@LaunchedEffect + + when { + !uiState.finishTagId.isNullOrBlank() -> { + state.showMessage( + message = "태그 완료 ${Emoji.congratulate.random()}", + actionLabel = "취소", + ) { + uiState.restartTag(uiState.finishTagId) + } + } + + !uiState.deleteTagId.isNullOrBlank() -> { + state.showMessage( + message = "태그 삭제 ${Emoji.congratulate.random()}", + actionLabel = "취소", + ) { + uiState.restoreTag(uiState.deleteTagId) + } + } + + uiState.isUnknownError -> state.showMessage("알 수 없는 에러가 발생했어요 잠시 후 다시 시도해 주세요 ${Emoji.error.random()}") + } + + uiState.onMessageShow() + } +} + +@Composable +private fun Content( + listProvider: () -> List?, + onTag: (String) -> Unit, + modifier: Modifier = Modifier, +) { + LazyColumn( + modifier = modifier, + contentPadding = DiaryTheme.dimen.screenPaddingValues, + verticalArrangement = Arrangement.spacedBy(DiaryTheme.dimen.itemSpace), + ) { + val list = listProvider() + + if (list == null) { + items( + count = 5, + contentType = { "Tag" }, + ) { + TagItem( + uiState = null, + onClick = {}, + ) + } + } else { + if (list.isEmpty()) { + item { + Box( + modifier = Modifier.fillParentMaxSize(), + contentAlignment = Alignment.Center, + ) { + Text( + text = "태그가 없어요 🐼", + style = DiaryTheme.typography.headlineMedium, + ) + } + } + } else { + items( + items = list, + key = { it.id }, + contentType = { "Tag" }, + ) { + TagItem( + uiState = it, + onClick = { onTag(it.id) }, + modifier = Modifier.animateItem(), + ) + } + } + } + } +} + +@Composable +private fun TagItem( + uiState: TagListItemUiState?, + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { + FinishAndDeleteSwipeBox( + modifier = modifier, + onFinish = { uiState?.finish?.value?.invoke() }, + onDelete = { uiState?.delete?.value?.invoke() }, + ) { + Card(onClick = onClick) { + Box( + modifier = Modifier.fillMaxSize() + .padding(16.dp), + contentAlignment = Alignment.Center, + ) { + Text(text = uiState?.title.orEmpty()) + } + } + } +} diff --git a/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/list/TagListScreenState.kt b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/list/TagListScreenState.kt new file mode 100644 index 00000000..5f5535cb --- /dev/null +++ b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/list/TagListScreenState.kt @@ -0,0 +1,43 @@ +package io.github.taetae98coding.diary.feature.tag.list + +import androidx.compose.material3.SnackbarDuration +import androidx.compose.material3.SnackbarHostState +import androidx.compose.material3.SnackbarResult +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch + +internal class TagListScreenState( + private val coroutineScope: CoroutineScope, +) { + private var messageJob: Job? = null + + val hostState: SnackbarHostState = SnackbarHostState() + + fun showMessage( + message: String, + actionLabel: String, + onResult: (SnackbarResult) -> Unit, + ) { + messageJob?.cancel() + messageJob = coroutineScope.launch { + val result = hostState.showSnackbar( + message = message, + actionLabel = actionLabel, + duration = SnackbarDuration.Long, + ) + + if (isActive) { + onResult(result) + } + } + } + + fun showMessage( + message: String, + ) { + messageJob?.cancel() + messageJob = coroutineScope.launch { hostState.showSnackbar(message = message) } + } +} diff --git a/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/list/TagListScreenUiState.kt b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/list/TagListScreenUiState.kt new file mode 100644 index 00000000..11b5f985 --- /dev/null +++ b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/list/TagListScreenUiState.kt @@ -0,0 +1,12 @@ +package io.github.taetae98coding.diary.feature.tag.list + +internal data class TagListScreenUiState( + val finishTagId: String? = null, + val deleteTagId: String? = null, + val isUnknownError: Boolean = false, + val restartTag: (String) -> Unit = {}, + val restoreTag: (String) -> Unit = {}, + val onMessageShow: () -> Unit = {}, +) { + val hasMessage = !finishTagId.isNullOrBlank() || !deleteTagId.isNullOrBlank() || isUnknownError +} diff --git a/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/list/TagListViewModel.kt b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/list/TagListViewModel.kt new file mode 100644 index 00000000..d1aadbdb --- /dev/null +++ b/app/feature/tag/src/commonMain/kotlin/io/github/taetae98coding/diary/feature/tag/list/TagListViewModel.kt @@ -0,0 +1,92 @@ +package io.github.taetae98coding.diary.feature.tag.list + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import io.github.taetae98coding.diary.core.compose.runtime.SkipProperty +import io.github.taetae98coding.diary.domain.tag.usecase.DeleteTagUseCase +import io.github.taetae98coding.diary.domain.tag.usecase.FinishTagUseCase +import io.github.taetae98coding.diary.domain.tag.usecase.PageTagUseCase +import io.github.taetae98coding.diary.domain.tag.usecase.RestartTagUseCase +import io.github.taetae98coding.diary.domain.tag.usecase.RestoreTagUseCase +import io.github.taetae98coding.diary.library.coroutines.mapCollectionLatest +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.mapLatest +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import org.koin.android.annotation.KoinViewModel + +@OptIn(ExperimentalCoroutinesApi::class) +@KoinViewModel +internal class TagListViewModel( + pageTagUseCase: PageTagUseCase, + private val finishTagUseCase: FinishTagUseCase, + private val deleteTagUseCase: DeleteTagUseCase, + private val restartTagUseCase: RestartTagUseCase, + private val restoreTagUseCase: RestoreTagUseCase, +) : ViewModel() { + private val _uiState = MutableStateFlow( + TagListScreenUiState( + restartTag = ::restart, + restoreTag = ::restore, + onMessageShow = ::clearMessage, + ), + ) + val uiState = _uiState.asStateFlow() + + val list = pageTagUseCase().mapLatest { it.getOrNull() } + .mapCollectionLatest { + TagListItemUiState( + id = it.id, + title = it.detail.title, + finish = SkipProperty { finish(it.id) }, + delete = SkipProperty { delete(it.id) }, + ) + } + .stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5_000), + initialValue = null, + ) + + private fun finish(id: String) { + viewModelScope.launch { + finishTagUseCase(id) + .onSuccess { _uiState.update { it.copy(finishTagId = id) } } + .onFailure { _uiState.update { it.copy(isUnknownError = true) } } + } + } + + private fun delete(id: String) { + viewModelScope.launch { + deleteTagUseCase(id) + .onSuccess { _uiState.update { it.copy(deleteTagId = id) } } + .onFailure { _uiState.update { it.copy(isUnknownError = true) } } + } + } + + private fun restart(id: String) { + viewModelScope.launch { + restartTagUseCase(id) + } + } + + private fun restore(id: String) { + viewModelScope.launch { + restoreTagUseCase(id) + } + } + + private fun clearMessage() { + _uiState.update { + it.copy( + finishTagId = null, + deleteTagId = null, + isUnknownError = false, + ) + } + } +} diff --git a/app/platform/android/build.gradle.kts b/app/platform/android/build.gradle.kts index 0b164010..8f7aee33 100644 --- a/app/platform/android/build.gradle.kts +++ b/app/platform/android/build.gradle.kts @@ -87,11 +87,7 @@ android { dependencies { implementation(project(":app:platform:common")) - implementation(project(":app:core:diary-database-room")) implementation(project(":app:core:diary-service")) - implementation(project(":app:core:account-preferences-datastore")) - implementation(project(":app:core:holiday-preferences-datastore")) - implementation(project(":app:core:holiday-database-room")) implementation(project(":app:core:holiday-service")) implementation(project(":app:domain:fcm")) diff --git a/app/platform/android/dependencies/realReleaseRuntimeClasspath.txt b/app/platform/android/dependencies/realReleaseRuntimeClasspath.txt index 2463ee65..7f95324b 100644 --- a/app/platform/android/dependencies/realReleaseRuntimeClasspath.txt +++ b/app/platform/android/dependencies/realReleaseRuntimeClasspath.txt @@ -79,30 +79,30 @@ androidx.drawerlayout:drawerlayout:1.1.1 androidx.dynamicanimation:dynamicanimation:1.0.0 androidx.emoji2:emoji2-views-helper:1.3.0 androidx.emoji2:emoji2:1.3.0 -androidx.fragment:fragment-ktx:1.8.3 -androidx.fragment:fragment:1.8.3 +androidx.fragment:fragment-ktx:1.8.5 +androidx.fragment:fragment:1.8.5 androidx.graphics:graphics-path:1.0.1 androidx.interpolator:interpolator:1.0.0 androidx.legacy:legacy-support-core-utils:1.0.0 -androidx.lifecycle:lifecycle-common-java8:2.8.5 -androidx.lifecycle:lifecycle-common-jvm:2.8.5 -androidx.lifecycle:lifecycle-common:2.8.5 -androidx.lifecycle:lifecycle-livedata-core-ktx:2.8.5 -androidx.lifecycle:lifecycle-livedata-core:2.8.5 -androidx.lifecycle:lifecycle-livedata:2.8.5 -androidx.lifecycle:lifecycle-process:2.8.5 -androidx.lifecycle:lifecycle-runtime-android:2.8.5 -androidx.lifecycle:lifecycle-runtime-compose-android:2.8.5 -androidx.lifecycle:lifecycle-runtime-compose:2.8.5 -androidx.lifecycle:lifecycle-runtime-ktx-android:2.8.5 -androidx.lifecycle:lifecycle-runtime-ktx:2.8.5 -androidx.lifecycle:lifecycle-runtime:2.8.5 -androidx.lifecycle:lifecycle-viewmodel-android:2.8.5 -androidx.lifecycle:lifecycle-viewmodel-compose-android:2.8.5 -androidx.lifecycle:lifecycle-viewmodel-compose:2.8.5 -androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.5 -androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.5 -androidx.lifecycle:lifecycle-viewmodel:2.8.5 +androidx.lifecycle:lifecycle-common-java8:2.8.7 +androidx.lifecycle:lifecycle-common-jvm:2.8.7 +androidx.lifecycle:lifecycle-common:2.8.7 +androidx.lifecycle:lifecycle-livedata-core-ktx:2.8.7 +androidx.lifecycle:lifecycle-livedata-core:2.8.7 +androidx.lifecycle:lifecycle-livedata:2.8.7 +androidx.lifecycle:lifecycle-process:2.8.7 +androidx.lifecycle:lifecycle-runtime-android:2.8.7 +androidx.lifecycle:lifecycle-runtime-compose-android:2.8.7 +androidx.lifecycle:lifecycle-runtime-compose:2.8.7 +androidx.lifecycle:lifecycle-runtime-ktx-android:2.8.7 +androidx.lifecycle:lifecycle-runtime-ktx:2.8.7 +androidx.lifecycle:lifecycle-runtime:2.8.7 +androidx.lifecycle:lifecycle-viewmodel-android:2.8.7 +androidx.lifecycle:lifecycle-viewmodel-compose-android:2.8.7 +androidx.lifecycle:lifecycle-viewmodel-compose:2.8.7 +androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.7 +androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.7 +androidx.lifecycle:lifecycle-viewmodel:2.8.7 androidx.loader:loader:1.0.0 androidx.localbroadcastmanager:localbroadcastmanager:1.0.0 androidx.navigation:navigation-common-ktx:2.8.0-rc01 @@ -200,23 +200,23 @@ com.squareup.okhttp3:okhttp-sse:4.12.0 com.squareup.okhttp3:okhttp:4.12.0 com.squareup.okio:okio-jvm:3.9.1 com.squareup.okio:okio:3.9.1 -io.insert-koin:koin-android:4.0.0 -io.insert-koin:koin-annotations-bom:2.0.0-Beta1 -io.insert-koin:koin-annotations-jvm:2.0.0-Beta1 -io.insert-koin:koin-annotations:2.0.0-Beta1 -io.insert-koin:koin-bom:4.0.0 -io.insert-koin:koin-compose-jvm:4.0.0 -io.insert-koin:koin-compose-viewmodel-jvm:4.0.0 -io.insert-koin:koin-compose-viewmodel-navigation-jvm:4.0.0 -io.insert-koin:koin-compose-viewmodel-navigation:4.0.0 -io.insert-koin:koin-compose-viewmodel:4.0.0 -io.insert-koin:koin-compose:4.0.0 -io.insert-koin:koin-core-jvm:4.0.0 -io.insert-koin:koin-core-viewmodel-jvm:4.0.0 -io.insert-koin:koin-core-viewmodel-navigation-jvm:4.0.0 -io.insert-koin:koin-core-viewmodel-navigation:4.0.0 -io.insert-koin:koin-core-viewmodel:4.0.0 -io.insert-koin:koin-core:4.0.0 +io.insert-koin:koin-android:4.1.0-Beta1 +io.insert-koin:koin-annotations-bom:2.0.0-Beta2 +io.insert-koin:koin-annotations-jvm:2.0.0-Beta2 +io.insert-koin:koin-annotations:2.0.0-Beta2 +io.insert-koin:koin-bom:4.1.0-Beta1 +io.insert-koin:koin-compose-jvm:4.1.0-Beta1 +io.insert-koin:koin-compose-viewmodel-jvm:4.1.0-Beta1 +io.insert-koin:koin-compose-viewmodel-navigation-jvm:4.1.0-Beta1 +io.insert-koin:koin-compose-viewmodel-navigation:4.1.0-Beta1 +io.insert-koin:koin-compose-viewmodel:4.1.0-Beta1 +io.insert-koin:koin-compose:4.1.0-Beta1 +io.insert-koin:koin-core-jvm:4.1.0-Beta1 +io.insert-koin:koin-core-viewmodel-jvm:4.1.0-Beta1 +io.insert-koin:koin-core-viewmodel-navigation-jvm:4.1.0-Beta1 +io.insert-koin:koin-core-viewmodel-navigation:4.1.0-Beta1 +io.insert-koin:koin-core-viewmodel:4.1.0-Beta1 +io.insert-koin:koin-core:4.1.0-Beta1 io.ktor:ktor-client-content-negotiation-jvm:3.0.1 io.ktor:ktor-client-content-negotiation:3.0.1 io.ktor:ktor-client-core-jvm:3.0.1 @@ -250,8 +250,8 @@ org.jetbrains.androidx.core:core-bundle:1.0.1 org.jetbrains.androidx.lifecycle:lifecycle-common:2.8.4 org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose:2.8.4 org.jetbrains.androidx.lifecycle:lifecycle-runtime:2.8.4 -org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.8.2 -org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.2 +org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.8.3 +org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3 org.jetbrains.androidx.lifecycle:lifecycle-viewmodel:2.8.4 org.jetbrains.androidx.navigation:navigation-common:2.8.0-alpha10 org.jetbrains.androidx.navigation:navigation-compose:2.8.0-alpha10 @@ -262,8 +262,6 @@ org.jetbrains.compose.animation:animation-core:1.7.1 org.jetbrains.compose.animation:animation:1.7.1 org.jetbrains.compose.annotation-internal:annotation:1.7.1 org.jetbrains.compose.collection-internal:collection:1.7.1 -org.jetbrains.compose.components:components-resources-android:1.7.1 -org.jetbrains.compose.components:components-resources:1.7.1 org.jetbrains.compose.foundation:foundation-layout:1.7.1 org.jetbrains.compose.foundation:foundation:1.7.1 org.jetbrains.compose.material3.adaptive:adaptive-layout:1.0.1 diff --git a/app/platform/android/src/main/kotlin/io/github/taetae98coding/diary/DiaryActivity.kt b/app/platform/android/src/main/kotlin/io/github/taetae98coding/diary/DiaryActivity.kt index 6662c3bc..f19bb7db 100644 --- a/app/platform/android/src/main/kotlin/io/github/taetae98coding/diary/DiaryActivity.kt +++ b/app/platform/android/src/main/kotlin/io/github/taetae98coding/diary/DiaryActivity.kt @@ -9,7 +9,7 @@ import androidx.activity.result.contract.ActivityResultContracts import io.github.taetae98coding.diary.app.App public class DiaryActivity : ComponentActivity() { - private val notificationPermissionLauncer = registerForActivityResult( + private val notificationPermissionLauncher = registerForActivityResult( contract = ActivityResultContracts.RequestPermission(), callback = {} ) @@ -21,6 +21,6 @@ public class DiaryActivity : ComponentActivity() { App() } - notificationPermissionLauncer.launch(Manifest.permission.POST_NOTIFICATIONS) + notificationPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS) } } diff --git a/app/platform/android/src/main/kotlin/io/github/taetae98coding/diary/initializer/KoinInitializer.kt b/app/platform/android/src/main/kotlin/io/github/taetae98coding/diary/initializer/KoinInitializer.kt index 6e6f32cc..e1d90532 100644 --- a/app/platform/android/src/main/kotlin/io/github/taetae98coding/diary/initializer/KoinInitializer.kt +++ b/app/platform/android/src/main/kotlin/io/github/taetae98coding/diary/initializer/KoinInitializer.kt @@ -5,11 +5,7 @@ import androidx.startup.Initializer import io.github.taetae98coding.diary.BuildConfig import io.github.taetae98coding.diary.KoinAndroidModule import io.github.taetae98coding.diary.app.AppModule -import io.github.taetae98coding.diary.core.account.preferences.datastore.AccountDataStorePreferencesModule -import io.github.taetae98coding.diary.core.diary.database.room.DiaryRoomDatabaseModule import io.github.taetae98coding.diary.core.diary.service.DiaryServiceModule -import io.github.taetae98coding.diary.core.holiday.database.room.HolidayRoomDatabaseModule -import io.github.taetae98coding.diary.core.holiday.preferences.datastore.HolidayDataStorePreferencesModule import io.github.taetae98coding.diary.core.holiday.service.HolidayServiceModule import org.koin.android.ext.koin.androidContext import org.koin.core.KoinApplication @@ -28,11 +24,7 @@ public class KoinInitializer : Initializer { KoinAndroidModule().module, AppModule().module, diaryServiceModule(), - AccountDataStorePreferencesModule().module, - HolidayDataStorePreferencesModule().module, - HolidayRoomDatabaseModule().module, holidayServiceModule(), - DiaryRoomDatabaseModule().module, ) } } diff --git a/app/platform/common/build.gradle.kts b/app/platform/common/build.gradle.kts index 209b0741..e18ce643 100644 --- a/app/platform/common/build.gradle.kts +++ b/app/platform/common/build.gradle.kts @@ -7,6 +7,7 @@ kotlin { commonMain { dependencies { implementation(project(":app:data:memo")) + implementation(project(":app:data:tag")) implementation(project(":app:data:account")) implementation(project(":app:data:credential")) implementation(project(":app:data:holiday")) @@ -15,18 +16,30 @@ kotlin { implementation(project(":app:data:fcm")) implementation(project(":app:domain:memo")) + implementation(project(":app:domain:tag")) implementation(project(":app:domain:account")) implementation(project(":app:domain:credential")) implementation(project(":app:domain:holiday")) implementation(project(":app:domain:backup")) implementation(project(":app:domain:fetch")) implementation(project(":app:domain:fcm")) + implementation(project(":app:domain:calendar")) implementation(project(":app:core:coroutines")) + + implementation(project(":app:core:backup-database-room")) + + implementation(project(":app:core:account-preferences-datastore")) + + implementation(project(":app:core:diary-database-room")) implementation(project(":app:core:diary-service")) + implementation(project(":app:core:holiday-service")) + implementation(project(":app:core:holiday-preferences-datastore")) + implementation(project(":app:core:holiday-database-room")) implementation(project(":app:feature:memo")) + implementation(project(":app:feature:tag")) implementation(project(":app:feature:calendar")) implementation(project(":app:feature:more")) implementation(project(":app:feature:account")) diff --git a/app/platform/common/src/commonMain/kotlin/io/github/taetae98coding/diary/app/App.kt b/app/platform/common/src/commonMain/kotlin/io/github/taetae98coding/diary/app/App.kt index efdf640b..e41455e2 100644 --- a/app/platform/common/src/commonMain/kotlin/io/github/taetae98coding/diary/app/App.kt +++ b/app/platform/common/src/commonMain/kotlin/io/github/taetae98coding/diary/app/App.kt @@ -8,23 +8,30 @@ import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffo import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffoldDefaults import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteType import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.navigation.NavBackStackEntry import androidx.navigation.NavDestination.Companion.hasRoute import androidx.navigation.NavDestination.Companion.hierarchy import androidx.navigation.compose.currentBackStackEntryAsState import io.github.taetae98coding.diary.app.navigation.AppNavigation import io.github.taetae98coding.diary.app.state.rememberAppState +import io.github.taetae98coding.diary.core.design.system.icon.CalendarIcon +import io.github.taetae98coding.diary.core.design.system.icon.MemoIcon +import io.github.taetae98coding.diary.core.design.system.icon.MoreIcon +import io.github.taetae98coding.diary.core.design.system.icon.TagIcon import io.github.taetae98coding.diary.core.design.system.theme.DiaryTheme import io.github.taetae98coding.diary.core.navigation.calendar.CalendarDestination import io.github.taetae98coding.diary.core.navigation.memo.MemoDestination import io.github.taetae98coding.diary.core.navigation.more.MoreDestination -import io.github.taetae98coding.diary.core.resources.icon.CalendarIcon -import io.github.taetae98coding.diary.core.resources.icon.MemoIcon -import io.github.taetae98coding.diary.core.resources.icon.MoreIcon -import org.jetbrains.compose.resources.stringResource +import io.github.taetae98coding.diary.core.navigation.tag.TagDestination +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.flowOf @Composable public fun App() { @@ -42,15 +49,17 @@ private fun AppScaffold( ) { val state = rememberAppState() val backStackEntry by state.navController.currentBackStackEntryAsState() + var isNavigationVisibleFromBackStackEntry by remember { mutableStateOf(null) } val isNavigationVisible by remember { derivedStateOf { val visibleDestination = listOf( + TagDestination::class, MemoDestination::class, CalendarDestination::class, MoreDestination::class, ) - visibleDestination.any { + isNavigationVisibleFromBackStackEntry ?: visibleDestination.any { backStackEntry?.destination ?.hasRoute(it) ?: false @@ -62,6 +71,7 @@ private fun AppScaffold( navigationSuiteItems = { listOf( // AppNavigation.Memo, + AppNavigation.Tag, AppNavigation.Calendar, AppNavigation.More, ).forEach { navigation -> @@ -74,7 +84,7 @@ private fun AppScaffold( selected = isSelected, onClick = { state.navigate(navigation) }, icon = { AppNavigationIcon(navigation = navigation) }, - label = { Text(text = stringResource(navigation.title)) }, + label = { Text(text = navigation.title) }, alwaysShowLabel = true, ) } @@ -88,6 +98,24 @@ private fun AppScaffold( ) { AppNavHost(state = state) } + + LaunchedNavigationVisible( + backStackEntryProvider = { backStackEntry }, + onNavigationVisibleChange = { isNavigationVisibleFromBackStackEntry = it }, + ) +} + +@Composable +private fun LaunchedNavigationVisible( + backStackEntryProvider: () -> NavBackStackEntry?, + onNavigationVisibleChange: (Boolean?) -> Unit, +) { + val backStackEntry = backStackEntryProvider() + + LaunchedEffect(backStackEntry) { + val flow = backStackEntry?.savedStateHandle?.getStateFlow("app_navigation_visible", null) ?: flowOf(null) + flow.collectLatest { onNavigationVisibleChange(it) } + } } @Composable @@ -99,5 +127,6 @@ private fun AppNavigationIcon( AppNavigation.Memo -> MemoIcon(modifier = modifier) AppNavigation.Calendar -> CalendarIcon(modifier = modifier) AppNavigation.More -> MoreIcon(modifier = modifier) + AppNavigation.Tag -> TagIcon(modifier = modifier) } } diff --git a/app/platform/common/src/commonMain/kotlin/io/github/taetae98coding/diary/app/AppModule.kt b/app/platform/common/src/commonMain/kotlin/io/github/taetae98coding/diary/app/AppModule.kt index bb6b99bd..c4322d3e 100644 --- a/app/platform/common/src/commonMain/kotlin/io/github/taetae98coding/diary/app/AppModule.kt +++ b/app/platform/common/src/commonMain/kotlin/io/github/taetae98coding/diary/app/AppModule.kt @@ -1,7 +1,12 @@ package io.github.taetae98coding.diary.app +import io.github.taetae98coding.diary.core.account.preferences.datastore.AccountDataStorePreferencesModule +import io.github.taetae98coding.diary.core.backup.database.room.BackupRoomDatabaseModule import io.github.taetae98coding.diary.core.coroutines.CoroutinesModule +import io.github.taetae98coding.diary.core.diary.database.room.DiaryRoomDatabaseModule import io.github.taetae98coding.diary.core.diary.service.DiaryServiceModule +import io.github.taetae98coding.diary.core.holiday.database.room.HolidayRoomDatabaseModule +import io.github.taetae98coding.diary.core.holiday.preferences.datastore.HolidayDataStorePreferencesModule import io.github.taetae98coding.diary.core.holiday.service.HolidayServiceModule import io.github.taetae98coding.diary.data.account.AccountDataModule import io.github.taetae98coding.diary.data.backup.BackupDataModule @@ -10,17 +15,21 @@ import io.github.taetae98coding.diary.data.fcm.FCMDataModule import io.github.taetae98coding.diary.data.fetch.FetchDataModule import io.github.taetae98coding.diary.data.holiday.HolidayDataModule import io.github.taetae98coding.diary.data.memo.MemoDataModule +import io.github.taetae98coding.diary.data.tag.TagDataModule import io.github.taetae98coding.diary.domain.account.AccountDomainModule import io.github.taetae98coding.diary.domain.backup.BackupDomainModule +import io.github.taetae98coding.diary.domain.calendar.CalendarDomainModule import io.github.taetae98coding.diary.domain.credential.CredentialDomainModule import io.github.taetae98coding.diary.domain.fcm.FCMDomainModule import io.github.taetae98coding.diary.domain.fetch.FetchDomainModule import io.github.taetae98coding.diary.domain.holiday.HolidayDomainModule import io.github.taetae98coding.diary.domain.memo.MemoDomainModule +import io.github.taetae98coding.diary.domain.tag.TagDomainModule import io.github.taetae98coding.diary.feature.account.AccountFeatureModule import io.github.taetae98coding.diary.feature.calendar.CalendarFeatureModule import io.github.taetae98coding.diary.feature.memo.MemoFeatureModule import io.github.taetae98coding.diary.feature.more.MoreFeatureModule +import io.github.taetae98coding.diary.feature.tag.TagFeatureModule import io.github.taetae98coding.diary.library.firebase.KFirebase import io.github.taetae98coding.diary.library.firebase.messaging.KFirebaseMessaging import io.github.taetae98coding.diary.library.firebase.messaging.messaging @@ -32,9 +41,20 @@ import org.koin.core.annotation.Singleton @Module( includes = [ CoroutinesModule::class, + + BackupRoomDatabaseModule::class, + + AccountDataStorePreferencesModule::class, + DiaryServiceModule::class, + DiaryRoomDatabaseModule::class, + + HolidayDataStorePreferencesModule::class, + HolidayRoomDatabaseModule::class, HolidayServiceModule::class, + MemoDataModule::class, + TagDataModule::class, AccountDataModule::class, HolidayDataModule::class, BackupDataModule::class, @@ -42,13 +62,16 @@ import org.koin.core.annotation.Singleton FCMDataModule::class, CredentialDataModule::class, MemoDomainModule::class, + TagDomainModule::class, AccountDomainModule::class, HolidayDomainModule::class, BackupDomainModule::class, FetchDomainModule::class, FCMDomainModule::class, CredentialDomainModule::class, + CalendarDomainModule::class, MemoFeatureModule::class, + TagFeatureModule::class, CalendarFeatureModule::class, MoreFeatureModule::class, AccountFeatureModule::class, diff --git a/app/platform/common/src/commonMain/kotlin/io/github/taetae98coding/diary/app/AppNavHost.kt b/app/platform/common/src/commonMain/kotlin/io/github/taetae98coding/diary/app/AppNavHost.kt index 5699e549..7e11c8e6 100644 --- a/app/platform/common/src/commonMain/kotlin/io/github/taetae98coding/diary/app/AppNavHost.kt +++ b/app/platform/common/src/commonMain/kotlin/io/github/taetae98coding/diary/app/AppNavHost.kt @@ -9,6 +9,7 @@ import io.github.taetae98coding.diary.feature.account.accountNavigation import io.github.taetae98coding.diary.feature.calendar.calendarNavigation import io.github.taetae98coding.diary.feature.memo.memoNavigation import io.github.taetae98coding.diary.feature.more.moreNavigation +import io.github.taetae98coding.diary.feature.tag.tagNavigation @Composable internal fun AppNavHost( @@ -21,6 +22,7 @@ internal fun AppNavHost( modifier = modifier, ) { memoNavigation(navController = state.navController) + tagNavigation(navController = state.navController) calendarNavigation(navController = state.navController) moreNavigation(navController = state.navController) accountNavigation(navController = state.navController) diff --git a/app/platform/common/src/commonMain/kotlin/io/github/taetae98coding/diary/app/navigation/AppNavigation.kt b/app/platform/common/src/commonMain/kotlin/io/github/taetae98coding/diary/app/navigation/AppNavigation.kt index a159440e..bf9ccb00 100644 --- a/app/platform/common/src/commonMain/kotlin/io/github/taetae98coding/diary/app/navigation/AppNavigation.kt +++ b/app/platform/common/src/commonMain/kotlin/io/github/taetae98coding/diary/app/navigation/AppNavigation.kt @@ -3,28 +3,29 @@ package io.github.taetae98coding.diary.app.navigation import io.github.taetae98coding.diary.core.navigation.calendar.CalendarDestination import io.github.taetae98coding.diary.core.navigation.memo.MemoDestination import io.github.taetae98coding.diary.core.navigation.more.MoreDestination -import io.github.taetae98coding.diary.core.resources.Res -import io.github.taetae98coding.diary.core.resources.calendar -import io.github.taetae98coding.diary.core.resources.memo -import io.github.taetae98coding.diary.core.resources.more -import org.jetbrains.compose.resources.StringResource +import io.github.taetae98coding.diary.core.navigation.tag.TagDestination internal enum class AppNavigation( - val title: StringResource, + val title: String, val route: Any, ) { Memo( - title = Res.string.memo, + title = "메모", route = MemoDestination, ), + Tag( + title = "태그", + route = TagDestination, + ), + Calendar( - title = Res.string.calendar, + title = "캘린더", route = CalendarDestination, ), More( - title = Res.string.more, + title = "더보기", route = MoreDestination, ) } diff --git a/app/platform/ios/build.gradle.kts b/app/platform/ios/build.gradle.kts index f8764a00..72ad08d5 100644 --- a/app/platform/ios/build.gradle.kts +++ b/app/platform/ios/build.gradle.kts @@ -31,11 +31,7 @@ kotlin { dependencies { implementation(project(":app:platform:common")) implementation(project(":app:core:coroutines")) - implementation(project(":app:core:diary-database-room")) implementation(project(":app:core:diary-service")) - implementation(project(":app:core:account-preferences-datastore")) - implementation(project(":app:core:holiday-preferences-datastore")) - implementation(project(":app:core:holiday-database-room")) implementation(project(":app:core:holiday-service")) implementation(compose.ui) diff --git a/app/platform/jvm/build.gradle.kts b/app/platform/jvm/build.gradle.kts index 67e301e8..0b3f2d9d 100644 --- a/app/platform/jvm/build.gradle.kts +++ b/app/platform/jvm/build.gradle.kts @@ -18,11 +18,7 @@ kotlin { dependencies { implementation(project(":app:platform:common")) implementation(project(":app:core:coroutines")) - implementation(project(":app:core:diary-database-room")) implementation(project(":app:core:diary-service")) - implementation(project(":app:core:account-preferences-datastore")) - implementation(project(":app:core:holiday-preferences-datastore")) - implementation(project(":app:core:holiday-database-room")) implementation(project(":app:core:holiday-service")) implementation(project(":library:koin-room")) diff --git a/app/platform/jvm/src/jvmMain/kotlin/io/github/taetae98coding/diary/initializer/KoinInitializer.kt b/app/platform/jvm/src/jvmMain/kotlin/io/github/taetae98coding/diary/initializer/KoinInitializer.kt index fc0b90ea..7f24fa6d 100644 --- a/app/platform/jvm/src/jvmMain/kotlin/io/github/taetae98coding/diary/initializer/KoinInitializer.kt +++ b/app/platform/jvm/src/jvmMain/kotlin/io/github/taetae98coding/diary/initializer/KoinInitializer.kt @@ -2,11 +2,7 @@ package io.github.taetae98coding.diary.initializer import io.github.taetae98coding.diary.BuildKonfig import io.github.taetae98coding.diary.app.AppModule -import io.github.taetae98coding.diary.core.account.preferences.datastore.AccountDataStorePreferencesModule -import io.github.taetae98coding.diary.core.diary.database.room.DiaryRoomDatabaseModule import io.github.taetae98coding.diary.core.diary.service.DiaryServiceModule -import io.github.taetae98coding.diary.core.holiday.database.room.HolidayRoomDatabaseModule -import io.github.taetae98coding.diary.core.holiday.preferences.datastore.HolidayDataStorePreferencesModule import io.github.taetae98coding.diary.core.holiday.service.HolidayServiceModule import io.github.taetae98coding.diary.library.koin.datastore.koinDataStoreDefaultPath import io.github.taetae98coding.diary.library.koin.room.koinRoomDefaultPath @@ -25,11 +21,7 @@ internal fun initKoin(): KoinApplication { modules( AppModule().module, diaryServiceModule(), - AccountDataStorePreferencesModule().module, - HolidayDataStorePreferencesModule().module, - HolidayRoomDatabaseModule().module, holidayServiceModule(), - DiaryRoomDatabaseModule().module, ) } } diff --git a/app/platform/wasm/README.md b/app/platform/wasm/README.md deleted file mode 100644 index 38264ccb..00000000 --- a/app/platform/wasm/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# :app:platform:wasm module -## Dependency graph -![Dependency graph](../../../docs/images/graphs/dep_graph_app_platform_wasm.svg) diff --git a/app/platform/wasm/build.gradle.kts b/app/platform/wasm/build.gradle.kts deleted file mode 100644 index 31ef32bc..00000000 --- a/app/platform/wasm/build.gradle.kts +++ /dev/null @@ -1,73 +0,0 @@ - -import com.codingfeline.buildkonfig.compiler.FieldSpec -import ext.getLocalProperty -import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl - -private val localProperties = requireNotNull(project.getLocalProperty()) - -plugins { - id("diary.kotlin.multiplatform") - id("diary.compose") - alias(libs.plugins.buildkonfig) -} - -@OptIn(ExperimentalWasmDsl::class) -kotlin { - wasmJs { - moduleName = "diary" - browser { - commonWebpackConfig { - outputFileName = "diary.js" - } - - runTask { -// devServerProperty.set(devServerProperty.get().copy(port = 8080)) - } - } - - binaries.executable() - } - - applyDefaultHierarchyTemplate() - - sourceSets { - commonMain { - dependencies { - implementation(project(":app:platform:common")) - implementation(project(":app:core:coroutines")) - implementation(project(":app:core:diary-database-memory")) - implementation(project(":app:core:diary-service")) - implementation(project(":app:core:account-preferences-memory")) - implementation(project(":app:core:holiday-preferences-memory")) - implementation(project(":app:core:holiday-database-memory")) - implementation(project(":app:core:holiday-service")) - - implementation(compose.ui) - implementation(libs.lifecycle.compose) - - implementation(project.dependencies.platform(libs.koin.bom)) - implementation(libs.koin.core) - - runtimeOnly(libs.ktor.client.js) - } - } - } -} - -buildkonfig { - packageName = Build.NAMESPACE - - defaultConfigs {} - - defaultConfigs("dev") { - buildConfigField(FieldSpec.Type.STRING, "DIARY_API_URL", localProperties.getProperty("diary.dev.api.base.url")) - buildConfigField(FieldSpec.Type.STRING, "HOLIDAY_API_URL", localProperties.getProperty("holiday.dev.api.url")) - buildConfigField(FieldSpec.Type.STRING, "HOLIDAY_API_KEY", localProperties.getProperty("holiday.dev.api.key")) - } - - defaultConfigs("real") { - buildConfigField(FieldSpec.Type.STRING, "DIARY_API_URL", localProperties.getProperty("diary.real.api.base.url")) - buildConfigField(FieldSpec.Type.STRING, "HOLIDAY_API_URL", localProperties.getProperty("holiday.real.api.url")) - buildConfigField(FieldSpec.Type.STRING, "HOLIDAY_API_KEY", localProperties.getProperty("holiday.real.api.key")) - } -} diff --git a/app/platform/wasm/src/wasmJsMain/kotlin/io/github/taetae98coding/diary/WasmJsApp.kt b/app/platform/wasm/src/wasmJsMain/kotlin/io/github/taetae98coding/diary/WasmJsApp.kt deleted file mode 100644 index ff695bbd..00000000 --- a/app/platform/wasm/src/wasmJsMain/kotlin/io/github/taetae98coding/diary/WasmJsApp.kt +++ /dev/null @@ -1,31 +0,0 @@ -package io.github.taetae98coding.diary - -import androidx.compose.ui.ExperimentalComposeUiApi -import androidx.compose.ui.window.CanvasBasedWindow -import androidx.lifecycle.compose.LifecycleResumeEffect -import androidx.lifecycle.compose.LifecycleStartEffect -import io.github.taetae98coding.diary.app.App -import io.github.taetae98coding.diary.core.coroutines.AppLifecycleOwner -import io.github.taetae98coding.diary.initializer.intiJs - -@OptIn(ExperimentalComposeUiApi::class) -public fun main() { - intiJs() - - CanvasBasedWindow( - title = "Diary", - canvasElementId = "compose", - ) { - App() - - LifecycleStartEffect(keys = arrayOf(AppLifecycleOwner)) { - AppLifecycleOwner.start() - onStopOrDispose { AppLifecycleOwner.stop() } - } - - LifecycleResumeEffect(keys = arrayOf(AppLifecycleOwner)) { - AppLifecycleOwner.resume() - onPauseOrDispose { AppLifecycleOwner.pause() } - } - } -} diff --git a/app/platform/wasm/src/wasmJsMain/kotlin/io/github/taetae98coding/diary/initializer/AppLifecycleOwnerInitializer.kt b/app/platform/wasm/src/wasmJsMain/kotlin/io/github/taetae98coding/diary/initializer/AppLifecycleOwnerInitializer.kt deleted file mode 100644 index f5cb9b85..00000000 --- a/app/platform/wasm/src/wasmJsMain/kotlin/io/github/taetae98coding/diary/initializer/AppLifecycleOwnerInitializer.kt +++ /dev/null @@ -1,8 +0,0 @@ -package io.github.taetae98coding.diary.initializer - -import io.github.taetae98coding.diary.core.coroutines.AppLifecycleOwner - -internal fun initAppLifecycleOwner(): AppLifecycleOwner { - AppLifecycleOwner.create() - return AppLifecycleOwner -} diff --git a/app/platform/wasm/src/wasmJsMain/kotlin/io/github/taetae98coding/diary/initializer/BackupManagerInitializer.kt b/app/platform/wasm/src/wasmJsMain/kotlin/io/github/taetae98coding/diary/initializer/BackupManagerInitializer.kt deleted file mode 100644 index 2dc42ffa..00000000 --- a/app/platform/wasm/src/wasmJsMain/kotlin/io/github/taetae98coding/diary/initializer/BackupManagerInitializer.kt +++ /dev/null @@ -1,14 +0,0 @@ -package io.github.taetae98coding.diary.initializer - -import androidx.lifecycle.LifecycleOwner -import io.github.taetae98coding.diary.app.manager.BackupManager -import org.koin.core.KoinApplication - -internal fun initBackupManager( - koinApplication: KoinApplication, -) { - val appLifecycleOwner = koinApplication.koin.get() - val backupManager = koinApplication.koin.get() - - backupManager.attach(appLifecycleOwner) -} diff --git a/app/platform/wasm/src/wasmJsMain/kotlin/io/github/taetae98coding/diary/initializer/FetchManagerInitializer.kt b/app/platform/wasm/src/wasmJsMain/kotlin/io/github/taetae98coding/diary/initializer/FetchManagerInitializer.kt deleted file mode 100644 index e0e722db..00000000 --- a/app/platform/wasm/src/wasmJsMain/kotlin/io/github/taetae98coding/diary/initializer/FetchManagerInitializer.kt +++ /dev/null @@ -1,14 +0,0 @@ -package io.github.taetae98coding.diary.initializer - -import androidx.lifecycle.LifecycleOwner -import io.github.taetae98coding.diary.app.manager.FetchManager -import org.koin.core.KoinApplication - -internal fun initFetchManager( - koinApplication: KoinApplication, -) { - val appLifecycleOwner = koinApplication.koin.get() - val fetchManager = koinApplication.koin.get() - - fetchManager.attach(appLifecycleOwner) -} diff --git a/app/platform/wasm/src/wasmJsMain/kotlin/io/github/taetae98coding/diary/initializer/JsInitializer.kt b/app/platform/wasm/src/wasmJsMain/kotlin/io/github/taetae98coding/diary/initializer/JsInitializer.kt deleted file mode 100644 index 5c28f4b9..00000000 --- a/app/platform/wasm/src/wasmJsMain/kotlin/io/github/taetae98coding/diary/initializer/JsInitializer.kt +++ /dev/null @@ -1,9 +0,0 @@ -package io.github.taetae98coding.diary.initializer - -internal fun intiJs() { - initAppLifecycleOwner() - val koinApplication = initKoin() - - initBackupManager(koinApplication) - initFetchManager(koinApplication) -} diff --git a/app/platform/wasm/src/wasmJsMain/kotlin/io/github/taetae98coding/diary/initializer/KoinInitializer.kt b/app/platform/wasm/src/wasmJsMain/kotlin/io/github/taetae98coding/diary/initializer/KoinInitializer.kt deleted file mode 100644 index 82a324f9..00000000 --- a/app/platform/wasm/src/wasmJsMain/kotlin/io/github/taetae98coding/diary/initializer/KoinInitializer.kt +++ /dev/null @@ -1,43 +0,0 @@ -package io.github.taetae98coding.diary.initializer - -import io.github.taetae98coding.diary.BuildKonfig -import io.github.taetae98coding.diary.app.AppModule -import io.github.taetae98coding.diary.core.diary.database.memory.DiaryMemoryDatabaseModule -import io.github.taetae98coding.diary.core.diary.service.DiaryServiceModule -import io.github.taetae98coding.diary.core.holiday.database.memory.HolidayMemoryDatabaseModule -import io.github.taetae98coding.diary.core.holiday.preferences.memory.HolidayPreferencesMemoryModule -import io.github.taetae98coding.diary.core.holiday.service.HolidayServiceModule -import io.github.taetae98coding.diary.fore.account.preferences.memory.AccountPreferencesMemoryModule -import org.koin.core.KoinApplication -import org.koin.core.context.startKoin -import org.koin.core.module.Module -import org.koin.core.qualifier.StringQualifier -import org.koin.dsl.module -import org.koin.ksp.generated.module - -internal fun initKoin(): KoinApplication { - return startKoin { - modules( - AppModule().module, - diaryServiceModule(), - AccountPreferencesMemoryModule().module, - HolidayPreferencesMemoryModule().module, - HolidayMemoryDatabaseModule().module, - holidayServiceModule(), - DiaryMemoryDatabaseModule().module, - ) - } -} - -private fun diaryServiceModule(): Module { - return module { - single(qualifier = StringQualifier(DiaryServiceModule.DIARY_API_URL)) { BuildKonfig.DIARY_API_URL } - } -} - -private fun holidayServiceModule(): Module { - return module { - single(qualifier = StringQualifier(HolidayServiceModule.HOLIDAY_API_URL)) { BuildKonfig.HOLIDAY_API_URL } - single(qualifier = StringQualifier(HolidayServiceModule.HOLIDAY_API_KEY)) { BuildKonfig.HOLIDAY_API_KEY } - } -} diff --git a/app/platform/wasm/src/wasmJsMain/resources/index.html b/app/platform/wasm/src/wasmJsMain/resources/index.html deleted file mode 100644 index 9eacf2f5..00000000 --- a/app/platform/wasm/src/wasmJsMain/resources/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - Diary - - - - - - - \ No newline at end of file diff --git a/build-logic/src/main/kotlin/ext/DependencyExt.kt b/build-logic/src/main/kotlin/ext/DependencyExt.kt index 9880477a..8e70f068 100644 --- a/build-logic/src/main/kotlin/ext/DependencyExt.kt +++ b/build-logic/src/main/kotlin/ext/DependencyExt.kt @@ -29,12 +29,6 @@ internal fun DependencyHandler.kspJvm( add("kspJvm", dependencyNotation) } -internal fun DependencyHandler.kspWasmJs( - dependencyNotation: Provider, -) { - add("kspWasmJs", dependencyNotation) -} - internal fun DependencyHandler.kspAndroid( dependencyNotation: Provider, ) { @@ -53,29 +47,11 @@ public fun DependencyHandler.kspCommon( dependencyNotation: Provider, ) { kspJvm(dependencyNotation) - kspWasmJs(dependencyNotation) kspIos(dependencyNotation) } public fun DependencyHandler.kspAll( dependencyNotation: Provider, -) { - kspJvm(dependencyNotation) - kspWasmJs(dependencyNotation) - kspAndroid(dependencyNotation) - kspIos(dependencyNotation) -} - -public fun DependencyHandler.kspDataStore( - dependencyNotation: Provider, -) { - kspJvm(dependencyNotation) - kspAndroid(dependencyNotation) - kspIos(dependencyNotation) -} - -public fun DependencyHandler.kspRoom( - dependencyNotation: Provider, ) { kspJvm(dependencyNotation) kspAndroid(dependencyNotation) diff --git a/build-logic/src/main/kotlin/plugin/convention/AppFeaturePlugin.kt b/build-logic/src/main/kotlin/plugin/convention/AppFeaturePlugin.kt index 6aaade2f..6da41127 100644 --- a/build-logic/src/main/kotlin/plugin/convention/AppFeaturePlugin.kt +++ b/build-logic/src/main/kotlin/plugin/convention/AppFeaturePlugin.kt @@ -4,13 +4,10 @@ import ext.compose import ext.library import ext.libs import ext.sourceSets -import ext.withCompose import ext.withKotlinMultiplatform import ext.withKsp import org.gradle.api.Plugin import org.gradle.api.Project -import org.gradle.kotlin.dsl.getByType -import org.jetbrains.compose.resources.ResourcesExtension import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi import plugin.android.AndroidLibraryPlugin import plugin.compose.ComposePlugin @@ -36,9 +33,9 @@ internal class AppFeaturePlugin : Plugin { sourceSets { commonMain { dependencies { + implementation(project(":app:core:compose")) implementation(project(":app:core:design-system")) implementation(project(":app:core:navigation")) - implementation(project(":app:core:resources")) implementation(project(":library:color")) implementation(project(":library:kotlin")) @@ -48,7 +45,6 @@ internal class AppFeaturePlugin : Plugin { implementation(project(":library:shimmer-m3")) implementation(compose.material3) - implementation(compose.components.resources) implementation(libs.library("compose-material3-adaptive-navigation")) implementation(libs.library("navigation-compose")) @@ -72,12 +68,6 @@ internal class AppFeaturePlugin : Plugin { } } - target.withCompose { - with(extensions.getByType()) { - generateResClass = ResourcesExtension.ResourceClassGeneration.Never - } - } - target.withKsp { arg("KOIN_USE_COMPOSE_VIEWMODEL", "true") arg("KOIN_DEFAULT_MODULE", "false") diff --git a/build-logic/src/main/kotlin/plugin/koin/KoinDataStorePlugin.kt b/build-logic/src/main/kotlin/plugin/koin/KoinDataStorePlugin.kt index 6a0a37ba..6a6d4cca 100644 --- a/build-logic/src/main/kotlin/plugin/koin/KoinDataStorePlugin.kt +++ b/build-logic/src/main/kotlin/plugin/koin/KoinDataStorePlugin.kt @@ -1,6 +1,6 @@ package plugin.koin -import ext.kspDataStore +import ext.kspAll import ext.library import ext.libs import ext.sourceSets @@ -34,8 +34,8 @@ internal class KoinDataStorePlugin : Plugin{ } target.withDependency { - kspDataStore(platform(libs.library("koin-annotations-bom"))) - kspDataStore(libs.library("koin-compiler")) + kspAll(platform(libs.library("koin-annotations-bom"))) + kspAll(libs.library("koin-compiler")) } } } diff --git a/build-logic/src/main/kotlin/plugin/koin/KoinRoomPlugin.kt b/build-logic/src/main/kotlin/plugin/koin/KoinRoomPlugin.kt index f7e57781..538f45df 100644 --- a/build-logic/src/main/kotlin/plugin/koin/KoinRoomPlugin.kt +++ b/build-logic/src/main/kotlin/plugin/koin/KoinRoomPlugin.kt @@ -1,6 +1,6 @@ package plugin.koin -import ext.kspRoom +import ext.kspAll import ext.library import ext.libs import ext.sourceSets @@ -40,8 +40,8 @@ internal class KoinRoomPlugin : Plugin{ } target.withDependency { - kspRoom(platform(libs.library("koin-annotations-bom"))) - kspRoom(libs.library("koin-compiler")) + kspAll(platform(libs.library("koin-annotations-bom"))) + kspAll(libs.library("koin-compiler")) } } } diff --git a/build-logic/src/main/kotlin/plugin/kotlin/KotlinMultiplatformAllPlugin.kt b/build-logic/src/main/kotlin/plugin/kotlin/KotlinMultiplatformAllPlugin.kt index 8dc0ee86..513204ce 100644 --- a/build-logic/src/main/kotlin/plugin/kotlin/KotlinMultiplatformAllPlugin.kt +++ b/build-logic/src/main/kotlin/plugin/kotlin/KotlinMultiplatformAllPlugin.kt @@ -5,7 +5,6 @@ import org.gradle.api.Plugin import org.gradle.api.Project import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl -@OptIn(ExperimentalWasmDsl::class) internal class KotlinMultiplatformAllPlugin : Plugin{ private val kotlinMultiplatformPlugin = KotlinMultiplatformPlugin() @@ -15,10 +14,6 @@ internal class KotlinMultiplatformAllPlugin : Plugin{ target.withKotlinMultiplatform { jvm() - wasmJs { - browser() - } - androidTarget() iosX64() diff --git a/build-logic/src/main/kotlin/plugin/kotlin/KotlinMultiplatformCommonPlugin.kt b/build-logic/src/main/kotlin/plugin/kotlin/KotlinMultiplatformCommonPlugin.kt index 1bb4ffae..d093ffac 100644 --- a/build-logic/src/main/kotlin/plugin/kotlin/KotlinMultiplatformCommonPlugin.kt +++ b/build-logic/src/main/kotlin/plugin/kotlin/KotlinMultiplatformCommonPlugin.kt @@ -3,9 +3,7 @@ package plugin.kotlin import ext.withKotlinMultiplatform import org.gradle.api.Plugin import org.gradle.api.Project -import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl -@OptIn(ExperimentalWasmDsl::class) internal class KotlinMultiplatformCommonPlugin : Plugin{ private val kotlinMultiplatformPlugin = KotlinMultiplatformPlugin() @@ -15,10 +13,6 @@ internal class KotlinMultiplatformCommonPlugin : Plugin{ target.withKotlinMultiplatform { jvm() - wasmJs { - browser() - } - iosX64() iosArm64() iosSimulatorArm64() diff --git a/build-logic/src/main/kotlin/plugin/room/RoomPlugin.kt b/build-logic/src/main/kotlin/plugin/room/RoomPlugin.kt index 2eb04985..c68d1942 100644 --- a/build-logic/src/main/kotlin/plugin/room/RoomPlugin.kt +++ b/build-logic/src/main/kotlin/plugin/room/RoomPlugin.kt @@ -1,7 +1,7 @@ package plugin.room import ext.bundle -import ext.kspRoom +import ext.kspAll import ext.library import ext.libs import ext.sourceSets @@ -54,7 +54,7 @@ internal class RoomPlugin : Plugin { } target.withDependency { - kspRoom(libs.library("room-compiler")) + kspAll(libs.library("room-compiler")) } } } diff --git a/common/exception/src/commonMain/kotlin/io/github/taetae98coding/diary/common/exception/tag/TagTitleBlankException.kt b/common/exception/src/commonMain/kotlin/io/github/taetae98coding/diary/common/exception/tag/TagTitleBlankException.kt new file mode 100644 index 00000000..3a626379 --- /dev/null +++ b/common/exception/src/commonMain/kotlin/io/github/taetae98coding/diary/common/exception/tag/TagTitleBlankException.kt @@ -0,0 +1,3 @@ +package io.github.taetae98coding.diary.common.exception.tag + +public class TagTitleBlankException : Exception() diff --git a/common/model/src/commonMain/kotlin/io/github/taetae98coding/diary/common/model/memo/MemoEntity.kt b/common/model/src/commonMain/kotlin/io/github/taetae98coding/diary/common/model/memo/MemoEntity.kt index 57e18be3..8c706b73 100644 --- a/common/model/src/commonMain/kotlin/io/github/taetae98coding/diary/common/model/memo/MemoEntity.kt +++ b/common/model/src/commonMain/kotlin/io/github/taetae98coding/diary/common/model/memo/MemoEntity.kt @@ -8,23 +8,27 @@ import kotlinx.serialization.Serializable @Serializable public data class MemoEntity( @SerialName("id") - val id: String, + val id: String = "", @SerialName("title") - val title: String, + val title: String = "", @SerialName("description") - val description: String, + val description: String = "", @SerialName("start") - val start: LocalDate?, + val start: LocalDate? = null, @SerialName("endInclusive") - val endInclusive: LocalDate?, + val endInclusive: LocalDate? = null, @SerialName("color") - val color: Int, + val color: Int = -16777216, @SerialName("owner") - val owner: String, + val owner: String = "", + @SerialName("primaryTag") + val primaryTag: String? = null, + @SerialName("tagIds") + val tagIds: Set = emptySet(), @SerialName("isFinish") - val isFinish: Boolean, + val isFinish: Boolean = false, @SerialName("isDelete") - val isDelete: Boolean, + val isDelete: Boolean = false, @SerialName("updateAt") - val updateAt: Instant, + val updateAt: Instant = Instant.fromEpochMilliseconds(0L), ) diff --git a/common/model/src/commonMain/kotlin/io/github/taetae98coding/diary/common/model/memo/MemoTagEntity.kt b/common/model/src/commonMain/kotlin/io/github/taetae98coding/diary/common/model/memo/MemoTagEntity.kt new file mode 100644 index 00000000..71e7cbfa --- /dev/null +++ b/common/model/src/commonMain/kotlin/io/github/taetae98coding/diary/common/model/memo/MemoTagEntity.kt @@ -0,0 +1,17 @@ +package io.github.taetae98coding.diary.common.model.memo + +import kotlinx.datetime.Instant +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +public data class MemoTagEntity( + @SerialName("memoId") + val memoId: String, + @SerialName("tagId") + val tagId: String, + @SerialName("isSelected") + val isSelected: Boolean, + @SerialName("updateAt") + val updateAt: Instant +) diff --git a/common/model/src/commonMain/kotlin/io/github/taetae98coding/diary/common/model/memo/TagEntity.kt b/common/model/src/commonMain/kotlin/io/github/taetae98coding/diary/common/model/memo/TagEntity.kt new file mode 100644 index 00000000..e500e8fd --- /dev/null +++ b/common/model/src/commonMain/kotlin/io/github/taetae98coding/diary/common/model/memo/TagEntity.kt @@ -0,0 +1,25 @@ +package io.github.taetae98coding.diary.common.model.memo + +import kotlinx.datetime.Instant +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +public data class TagEntity( + @SerialName("id") + val id: String, + @SerialName("title") + val title: String, + @SerialName("description") + val description: String, + @SerialName("color") + val color: Int, + @SerialName("owner") + val owner: String, + @SerialName("isFinish") + val isFinish: Boolean, + @SerialName("isDelete") + val isDelete: Boolean, + @SerialName("updateAt") + val updateAt: Instant, +) diff --git a/docs/images/graphs/dep_graph_app_core_account_preferences.svg b/docs/images/graphs/dep_graph_app_core_account_preferences.svg index 9bffdb5b..e69de29b 100644 --- a/docs/images/graphs/dep_graph_app_core_account_preferences.svg +++ b/docs/images/graphs/dep_graph_app_core_account_preferences.svg @@ -1,9 +0,0 @@ - - - - - - :app:core:account-preferences - - - diff --git a/docs/images/graphs/dep_graph_app_core_account_preferences_datastore.svg b/docs/images/graphs/dep_graph_app_core_account_preferences_datastore.svg index 61354813..e69de29b 100644 --- a/docs/images/graphs/dep_graph_app_core_account_preferences_datastore.svg +++ b/docs/images/graphs/dep_graph_app_core_account_preferences_datastore.svg @@ -1,25 +0,0 @@ - - - - - - :app:core:account-preferences-datastore - - - - :library:koin-datastore - - - - - - - - :app:core:account-preferences - - - - - - - diff --git a/docs/images/graphs/dep_graph_app_core_account_preferences_memory.svg b/docs/images/graphs/dep_graph_app_core_account_preferences_memory.svg deleted file mode 100644 index 7855adbc..00000000 --- a/docs/images/graphs/dep_graph_app_core_account_preferences_memory.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - :app:core:account-preferences-memory - - - - :app:core:account-preferences - - - - - - - diff --git a/docs/images/graphs/dep_graph_app_core_backup_database.svg b/docs/images/graphs/dep_graph_app_core_backup_database.svg new file mode 100644 index 00000000..260f5d31 --- /dev/null +++ b/docs/images/graphs/dep_graph_app_core_backup_database.svg @@ -0,0 +1,17 @@ + + + + + + :app:core:backup-database + + + + :app:core:model + + + + + + + diff --git a/docs/images/graphs/dep_graph_app_core_backup_database_room.svg b/docs/images/graphs/dep_graph_app_core_backup_database_room.svg new file mode 100644 index 00000000..7bd31a73 --- /dev/null +++ b/docs/images/graphs/dep_graph_app_core_backup_database_room.svg @@ -0,0 +1,57 @@ + + + + + + :app:core:backup-database-room + + + + :library:koin-room + + + + + + + + :app:core:backup-database + + + + + + + + :library:coroutines + + + + + + + + :library:room + + + + + + + + :app:core:model + + + + + + + + :library:datetime + + + + + + + diff --git a/docs/images/graphs/dep_graph_app_core_calendar_compose.svg b/docs/images/graphs/dep_graph_app_core_calendar_compose.svg index 02506fff..8e9a2f5a 100644 --- a/docs/images/graphs/dep_graph_app_core_calendar_compose.svg +++ b/docs/images/graphs/dep_graph_app_core_calendar_compose.svg @@ -1,53 +1,41 @@ - + - + - - :app:core:calendar-compose + + :app:core:calendar-compose - - :app:core:resources - - - - - - - - :app:core:design-system + + :app:core:design-system - - + + - - :library:color + + :library:color - - + + - - :library:datetime + + :library:datetime - - + + - - - - - - + + - - + + diff --git a/docs/images/graphs/dep_graph_app_core_compose.svg b/docs/images/graphs/dep_graph_app_core_compose.svg new file mode 100644 index 00000000..a172ecd2 --- /dev/null +++ b/docs/images/graphs/dep_graph_app_core_compose.svg @@ -0,0 +1,33 @@ + + + + + + :app:core:compose + + + + :app:core:design-system + + + + + + + + :library:color + + + + + + + + :library:datetime + + + + + + + diff --git a/docs/images/graphs/dep_graph_app_core_coroutines.svg b/docs/images/graphs/dep_graph_app_core_coroutines.svg index 4b9225c7..e69de29b 100644 --- a/docs/images/graphs/dep_graph_app_core_coroutines.svg +++ b/docs/images/graphs/dep_graph_app_core_coroutines.svg @@ -1,9 +0,0 @@ - - - - - - :app:core:coroutines - - - diff --git a/docs/images/graphs/dep_graph_app_core_design_system.svg b/docs/images/graphs/dep_graph_app_core_design_system.svg index e2eeb2e6..363ccbb4 100644 --- a/docs/images/graphs/dep_graph_app_core_design_system.svg +++ b/docs/images/graphs/dep_graph_app_core_design_system.svg @@ -1,33 +1,25 @@ - + - + - - :app:core:design-system + + :app:core:design-system - - :app:core:resources + + :library:color - - + + - - :library:color + + :library:datetime - - - - - - :library:datetime - - - - + + diff --git a/docs/images/graphs/dep_graph_app_core_diary_database.svg b/docs/images/graphs/dep_graph_app_core_diary_database.svg index ef09f95c..e69de29b 100644 --- a/docs/images/graphs/dep_graph_app_core_diary_database.svg +++ b/docs/images/graphs/dep_graph_app_core_diary_database.svg @@ -1,17 +0,0 @@ - - - - - - :app:core:diary-database - - - - :app:core:model - - - - - - - diff --git a/docs/images/graphs/dep_graph_app_core_diary_database_memory.svg b/docs/images/graphs/dep_graph_app_core_diary_database_memory.svg deleted file mode 100644 index 67d7a4dc..00000000 --- a/docs/images/graphs/dep_graph_app_core_diary_database_memory.svg +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - :app:core:diary-database-memory - - - - :app:core:diary-database - - - - - - - - :library:coroutines - - - - - - - - :library:datetime - - - - - - - - :app:core:model - - - - - - - diff --git a/docs/images/graphs/dep_graph_app_core_diary_database_room.svg b/docs/images/graphs/dep_graph_app_core_diary_database_room.svg index 47e4357e..e69de29b 100644 --- a/docs/images/graphs/dep_graph_app_core_diary_database_room.svg +++ b/docs/images/graphs/dep_graph_app_core_diary_database_room.svg @@ -1,57 +0,0 @@ - - - - - - :app:core:diary-database-room - - - - :library:koin-room - - - - - - - - :app:core:diary-database - - - - - - - - :library:coroutines - - - - - - - - :library:room - - - - - - - - :app:core:model - - - - - - - - :library:datetime - - - - - - - diff --git a/docs/images/graphs/dep_graph_app_core_diary_service.svg b/docs/images/graphs/dep_graph_app_core_diary_service.svg index 829d47e3..e69de29b 100644 --- a/docs/images/graphs/dep_graph_app_core_diary_service.svg +++ b/docs/images/graphs/dep_graph_app_core_diary_service.svg @@ -1,41 +0,0 @@ - - - - - - :app:core:diary-service - - - - :app:core:model - - - - - - - - :common:exception - - - - - - - - :app:core:account-preferences - - - - - - - - :common:model - - - - - - - diff --git a/docs/images/graphs/dep_graph_app_core_holiday_database.svg b/docs/images/graphs/dep_graph_app_core_holiday_database.svg index 9109e744..e69de29b 100644 --- a/docs/images/graphs/dep_graph_app_core_holiday_database.svg +++ b/docs/images/graphs/dep_graph_app_core_holiday_database.svg @@ -1,17 +0,0 @@ - - - - - - :app:core:holiday-database - - - - :app:core:model - - - - - - - diff --git a/docs/images/graphs/dep_graph_app_core_holiday_database_memory.svg b/docs/images/graphs/dep_graph_app_core_holiday_database_memory.svg deleted file mode 100644 index d92fe9e0..00000000 --- a/docs/images/graphs/dep_graph_app_core_holiday_database_memory.svg +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - :app:core:holiday-database-memory - - - - :app:core:holiday-database - - - - - - - - :app:core:model - - - - - - - diff --git a/docs/images/graphs/dep_graph_app_core_holiday_database_room.svg b/docs/images/graphs/dep_graph_app_core_holiday_database_room.svg index 056af84b..e69de29b 100644 --- a/docs/images/graphs/dep_graph_app_core_holiday_database_room.svg +++ b/docs/images/graphs/dep_graph_app_core_holiday_database_room.svg @@ -1,57 +0,0 @@ - - - - - - :app:core:holiday-database-room - - - - :library:koin-room - - - - - - - - :app:core:holiday-database - - - - - - - - :library:room - - - - - - - - :library:coroutines - - - - - - - - :app:core:model - - - - - - - - :library:datetime - - - - - - - diff --git a/docs/images/graphs/dep_graph_app_core_holiday_preferences.svg b/docs/images/graphs/dep_graph_app_core_holiday_preferences.svg index 1ce3fa19..e69de29b 100644 --- a/docs/images/graphs/dep_graph_app_core_holiday_preferences.svg +++ b/docs/images/graphs/dep_graph_app_core_holiday_preferences.svg @@ -1,9 +0,0 @@ - - - - - - :app:core:holiday-preferences - - - diff --git a/docs/images/graphs/dep_graph_app_core_holiday_preferences_datastore.svg b/docs/images/graphs/dep_graph_app_core_holiday_preferences_datastore.svg index f6cb9ebc..e69de29b 100644 --- a/docs/images/graphs/dep_graph_app_core_holiday_preferences_datastore.svg +++ b/docs/images/graphs/dep_graph_app_core_holiday_preferences_datastore.svg @@ -1,33 +0,0 @@ - - - - - - :app:core:holiday-preferences-datastore - - - - :library:koin-datastore - - - - - - - - :app:core:holiday-preferences - - - - - - - - :library:datetime - - - - - - - diff --git a/docs/images/graphs/dep_graph_app_core_holiday_preferences_memory.svg b/docs/images/graphs/dep_graph_app_core_holiday_preferences_memory.svg deleted file mode 100644 index 8a676109..00000000 --- a/docs/images/graphs/dep_graph_app_core_holiday_preferences_memory.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - :app:core:holiday-preferences-memory - - - - :app:core:holiday-preferences - - - - - - - diff --git a/docs/images/graphs/dep_graph_app_core_holiday_service.svg b/docs/images/graphs/dep_graph_app_core_holiday_service.svg index 4c38263a..e69de29b 100644 --- a/docs/images/graphs/dep_graph_app_core_holiday_service.svg +++ b/docs/images/graphs/dep_graph_app_core_holiday_service.svg @@ -1,17 +0,0 @@ - - - - - - :app:core:holiday-service - - - - :app:core:model - - - - - - - diff --git a/docs/images/graphs/dep_graph_app_core_model.svg b/docs/images/graphs/dep_graph_app_core_model.svg index 9a93db0f..e69de29b 100644 --- a/docs/images/graphs/dep_graph_app_core_model.svg +++ b/docs/images/graphs/dep_graph_app_core_model.svg @@ -1,9 +0,0 @@ - - - - - - :app:core:model - - - diff --git a/docs/images/graphs/dep_graph_app_core_navigation.svg b/docs/images/graphs/dep_graph_app_core_navigation.svg index 16a862be..e69de29b 100644 --- a/docs/images/graphs/dep_graph_app_core_navigation.svg +++ b/docs/images/graphs/dep_graph_app_core_navigation.svg @@ -1,17 +0,0 @@ - - - - - - :app:core:navigation - - - - :library:datetime - - - - - - - diff --git a/docs/images/graphs/dep_graph_app_core_resources.svg b/docs/images/graphs/dep_graph_app_core_resources.svg deleted file mode 100644 index a504d055..00000000 --- a/docs/images/graphs/dep_graph_app_core_resources.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - :app:core:resources - - - diff --git a/docs/images/graphs/dep_graph_app_data_account.svg b/docs/images/graphs/dep_graph_app_data_account.svg index 2a8edad1..e69de29b 100644 --- a/docs/images/graphs/dep_graph_app_data_account.svg +++ b/docs/images/graphs/dep_graph_app_data_account.svg @@ -1,73 +0,0 @@ - - - - - - :app:data:account - - - - :library:coroutines - - - - - - - - :library:datetime - - - - - - - - :app:core:account-preferences - - - - - - - - :app:domain:account - - - - - - - - - - - - - - - - :app:core:model - - - - - - - - :common:exception - - - - - - - - :library:kotlin - - - - - - - diff --git a/docs/images/graphs/dep_graph_app_data_backup.svg b/docs/images/graphs/dep_graph_app_data_backup.svg index df5d0da8..e4dc9936 100644 --- a/docs/images/graphs/dep_graph_app_data_backup.svg +++ b/docs/images/graphs/dep_graph_app_data_backup.svg @@ -1,137 +1,149 @@ - + - + - - :app:data:backup + + :app:data:backup - - :library:coroutines + + :library:coroutines - - + + - - :library:datetime + + :library:datetime - - + + - - :app:core:diary-database + + :app:core:backup-database - - + + - - :app:core:diary-service + + :app:core:diary-database - - + + - - :app:domain:backup + + :app:core:diary-service + + + + + + + + :app:domain:backup - - + + - - :app:core:model + + :app:core:model + + + + - - + + - - + + - - :common:exception + + :common:exception - - + + - - :app:core:account-preferences + + :app:core:account-preferences - - + + - - :common:model + + :common:model - - + + - - + + - - + + - - + + - - + + - - :library:kotlin + + :library:kotlin - - + + - - :app:domain:account + + :app:domain:account - - + + - - + + - - + + - - + + - - + + - - + + diff --git a/docs/images/graphs/dep_graph_app_data_credential.svg b/docs/images/graphs/dep_graph_app_data_credential.svg index eb52a792..d604d9d7 100644 --- a/docs/images/graphs/dep_graph_app_data_credential.svg +++ b/docs/images/graphs/dep_graph_app_data_credential.svg @@ -1,221 +1,225 @@ - + - - - - :app:data:credential + + + + :app:data:credential - - - :library:coroutines + + + :library:coroutines - - + + - - - :library:datetime + + + :library:datetime - - + + - - - :app:core:account-preferences + + + :app:core:account-preferences - - + + - - - :app:core:diary-service + + + :app:core:diary-service - - + + - - - :app:domain:credential + + + :app:domain:credential - - + + - - + + - - - :app:core:model + + + :app:core:model - - + + - - - :common:exception + + + :common:exception - - + + - - - :common:model + + + :common:model - - + + - - + + - - + + - - + + - - + + - - - :library:kotlin + + + :library:kotlin - - + + - - - :app:domain:fetch + + + :app:domain:fetch - - + + - - - :app:domain:backup + + + :app:domain:backup - - + + - - - :app:domain:fcm + + + :app:domain:fcm - - + + - - + + - - + + - - + + - - + + - - - - - - :app:domain:account + + - - + + + + + + :app:domain:account - - + + - - + + - - + + - - + + - - + + - - + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + diff --git a/docs/images/graphs/dep_graph_app_data_fcm.svg b/docs/images/graphs/dep_graph_app_data_fcm.svg index 7bae6781..e69de29b 100644 --- a/docs/images/graphs/dep_graph_app_data_fcm.svg +++ b/docs/images/graphs/dep_graph_app_data_fcm.svg @@ -1,141 +0,0 @@ - - - - - - :app:data:fcm - - - - :library:coroutines - - - - - - - - :library:datetime - - - - - - - - :app:core:diary-service - - - - - - - - :app:domain:fcm - - - - - - - - :library:firebase-messaging - - - - - - - - :app:core:model - - - - - - - - :common:exception - - - - - - - - :app:core:account-preferences - - - - - - - - :common:model - - - - - - - - - - - - - - - - - - - - - - - - :library:kotlin - - - - - - - - :app:domain:account - - - - - - - - :library:firebase-common - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/images/graphs/dep_graph_app_data_fetch.svg b/docs/images/graphs/dep_graph_app_data_fetch.svg index 0ebf197d..2ecc3dfc 100644 --- a/docs/images/graphs/dep_graph_app_data_fetch.svg +++ b/docs/images/graphs/dep_graph_app_data_fetch.svg @@ -1,137 +1,169 @@ - + - - - - :app:data:fetch + + + + :app:data:fetch - + :library:coroutines - - + + - + :library:datetime - - + + - - - :app:core:diary-database + + + :app:core:diary-database - - + + - - - :app:core:diary-service + + + :app:core:diary-service - - + + - - - :app:domain:fetch + + + :app:domain:fetch - - + + - + :app:core:model - - + + - - + + - + :common:exception - - + + - + :app:core:account-preferences - - + + - + :common:model - - + + - - + + - - + + - - + + - - + + - + :library:kotlin - - + + - - - :app:domain:account + + + :app:domain:account + + + + + + + + :app:domain:backup - - + + - - + + - - + + - - + + + + + + + + + + - - + + - - + + + + + + + + + + + + + + + + + + diff --git a/docs/images/graphs/dep_graph_app_data_holiday.svg b/docs/images/graphs/dep_graph_app_data_holiday.svg index 0a78ba3d..e69de29b 100644 --- a/docs/images/graphs/dep_graph_app_data_holiday.svg +++ b/docs/images/graphs/dep_graph_app_data_holiday.svg @@ -1,97 +0,0 @@ - - - - - - :app:data:holiday - - - - :library:coroutines - - - - - - - - :library:datetime - - - - - - - - :app:core:holiday-preferences - - - - - - - - :app:core:holiday-database - - - - - - - - :app:core:holiday-service - - - - - - - - :app:domain:holiday - - - - - - - - :app:core:model - - - - - - - - - - - - - - - - - - - - - - - - :common:exception - - - - - - - - :library:kotlin - - - - - - - diff --git a/docs/images/graphs/dep_graph_app_data_memo.svg b/docs/images/graphs/dep_graph_app_data_memo.svg index 20207a19..e69de29b 100644 --- a/docs/images/graphs/dep_graph_app_data_memo.svg +++ b/docs/images/graphs/dep_graph_app_data_memo.svg @@ -1,137 +0,0 @@ - - - - - - :app:data:memo - - - - :library:coroutines - - - - - - - - :library:datetime - - - - - - - - :app:core:diary-database - - - - - - - - :app:domain:memo - - - - - - - - :app:core:model - - - - - - - - - - - - - - - - - - - - :common:exception - - - - - - - - :library:kotlin - - - - - - - - :app:domain:account - - - - - - - - :app:domain:backup - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/images/graphs/dep_graph_app_data_tag.svg b/docs/images/graphs/dep_graph_app_data_tag.svg new file mode 100644 index 00000000..93a2204b --- /dev/null +++ b/docs/images/graphs/dep_graph_app_data_tag.svg @@ -0,0 +1,137 @@ + + + + + + :app:data:tag + + + + :library:coroutines + + + + + + + + :library:datetime + + + + + + + + :app:core:diary-database + + + + + + + + :app:domain:tag + + + + + + + + :app:core:model + + + + + + + + + + + + + + + + + + + + :common:exception + + + + + + + + :library:kotlin + + + + + + + + :app:domain:account + + + + + + + + :app:domain:backup + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/images/graphs/dep_graph_app_domain_account.svg b/docs/images/graphs/dep_graph_app_domain_account.svg index f80a0c6d..e69de29b 100644 --- a/docs/images/graphs/dep_graph_app_domain_account.svg +++ b/docs/images/graphs/dep_graph_app_domain_account.svg @@ -1,49 +0,0 @@ - - - - - - :app:domain:account - - - - :app:core:model - - - - - - - - :common:exception - - - - - - - - :library:coroutines - - - - - - - - :library:datetime - - - - - - - - :library:kotlin - - - - - - - diff --git a/docs/images/graphs/dep_graph_app_domain_backup.svg b/docs/images/graphs/dep_graph_app_domain_backup.svg index 5ba19d9b..e69de29b 100644 --- a/docs/images/graphs/dep_graph_app_domain_backup.svg +++ b/docs/images/graphs/dep_graph_app_domain_backup.svg @@ -1,77 +0,0 @@ - - - - - - :app:domain:backup - - - - :app:core:model - - - - - - - - :common:exception - - - - - - - - :library:coroutines - - - - - - - - :library:datetime - - - - - - - - :library:kotlin - - - - - - - - :app:domain:account - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/images/graphs/dep_graph_app_domain_calendar.svg b/docs/images/graphs/dep_graph_app_domain_calendar.svg new file mode 100644 index 00000000..67b893f1 --- /dev/null +++ b/docs/images/graphs/dep_graph_app_domain_calendar.svg @@ -0,0 +1,177 @@ + + + + + + :app:domain:calendar + + + + :app:core:model + + + + + + + + :common:exception + + + + + + + + :library:coroutines + + + + + + + + :library:datetime + + + + + + + + :library:kotlin + + + + + + + + :app:domain:account + + + + + + + + :app:domain:memo + + + + + + + + :app:domain:tag + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + :app:domain:backup + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/images/graphs/dep_graph_app_domain_credential.svg b/docs/images/graphs/dep_graph_app_domain_credential.svg index f46dd985..9c2bc32f 100644 --- a/docs/images/graphs/dep_graph_app_domain_credential.svg +++ b/docs/images/graphs/dep_graph_app_domain_credential.svg @@ -1,169 +1,173 @@ - + - - - - :app:domain:credential + + + + :app:domain:credential - - - :app:core:model + + + :app:core:model - - + + - - - :common:exception + + + :common:exception - - + + - - - :library:coroutines + + + :library:coroutines - - + + - - - :library:datetime + + + :library:datetime - - + + - - - :library:kotlin + + + :library:kotlin - - + + - - - :app:domain:fetch + + + :app:domain:fetch - - + + - - - :app:domain:backup + + + :app:domain:backup - - + + - - - :app:domain:fcm + + + :app:domain:fcm - - + + - - + + - - + + - - + + - - + + - - - - - - :app:domain:account + + - - + + + + + + :app:domain:account - - + + - - + + - - + + - - + + - - + + - - + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + diff --git a/docs/images/graphs/dep_graph_app_domain_fcm.svg b/docs/images/graphs/dep_graph_app_domain_fcm.svg index 4abb57f6..e69de29b 100644 --- a/docs/images/graphs/dep_graph_app_domain_fcm.svg +++ b/docs/images/graphs/dep_graph_app_domain_fcm.svg @@ -1,77 +0,0 @@ - - - - - - :app:domain:fcm - - - - :app:core:model - - - - - - - - :common:exception - - - - - - - - :library:coroutines - - - - - - - - :library:datetime - - - - - - - - :library:kotlin - - - - - - - - :app:domain:account - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/images/graphs/dep_graph_app_domain_fetch.svg b/docs/images/graphs/dep_graph_app_domain_fetch.svg index eb1cc794..fae505eb 100644 --- a/docs/images/graphs/dep_graph_app_domain_fetch.svg +++ b/docs/images/graphs/dep_graph_app_domain_fetch.svg @@ -1,77 +1,109 @@ - + - - - - :app:domain:fetch + + + + :app:domain:fetch - - - :app:core:model + + + :app:core:model - - + + - - - :common:exception + + + :common:exception - - + + - - - :library:coroutines + + + :library:coroutines - - + + - - - :library:datetime + + + :library:datetime - - + + - - - :library:kotlin + + + :library:kotlin - - + + - - - :app:domain:account + + + :app:domain:account + + + + + + + + :app:domain:backup - - + + - - + + + + + + + + + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + diff --git a/docs/images/graphs/dep_graph_app_domain_holiday.svg b/docs/images/graphs/dep_graph_app_domain_holiday.svg index dec9f8bb..e69de29b 100644 --- a/docs/images/graphs/dep_graph_app_domain_holiday.svg +++ b/docs/images/graphs/dep_graph_app_domain_holiday.svg @@ -1,49 +0,0 @@ - - - - - - :app:domain:holiday - - - - :app:core:model - - - - - - - - :common:exception - - - - - - - - :library:coroutines - - - - - - - - :library:datetime - - - - - - - - :library:kotlin - - - - - - - diff --git a/docs/images/graphs/dep_graph_app_domain_memo.svg b/docs/images/graphs/dep_graph_app_domain_memo.svg index e504fb4a..e69de29b 100644 --- a/docs/images/graphs/dep_graph_app_domain_memo.svg +++ b/docs/images/graphs/dep_graph_app_domain_memo.svg @@ -1,109 +0,0 @@ - - - - - - :app:domain:memo - - - - :app:core:model - - - - - - - - :common:exception - - - - - - - - :library:coroutines - - - - - - - - :library:datetime - - - - - - - - :library:kotlin - - - - - - - - :app:domain:account - - - - - - - - :app:domain:backup - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/images/graphs/dep_graph_app_domain_tag.svg b/docs/images/graphs/dep_graph_app_domain_tag.svg new file mode 100644 index 00000000..8322c2f7 --- /dev/null +++ b/docs/images/graphs/dep_graph_app_domain_tag.svg @@ -0,0 +1,109 @@ + + + + + + :app:domain:tag + + + + :app:core:model + + + + + + + + :common:exception + + + + + + + + :library:coroutines + + + + + + + + :library:datetime + + + + + + + + :library:kotlin + + + + + + + + :app:domain:account + + + + + + + + :app:domain:backup + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/images/graphs/dep_graph_app_feature_account.svg b/docs/images/graphs/dep_graph_app_feature_account.svg index 2b02d637..241e82d9 100644 --- a/docs/images/graphs/dep_graph_app_feature_account.svg +++ b/docs/images/graphs/dep_graph_app_feature_account.svg @@ -1,265 +1,269 @@ - + - - - - :app:feature:account + + + + :app:feature:account - - - :app:core:design-system + + + :app:core:compose - - + + - - - :app:core:navigation + + + :app:core:design-system - - + + - - - :app:core:resources + + + :app:core:navigation - - + + - - - :library:color + + + :library:color - - + + - - - :library:kotlin + + + :library:kotlin - - + + - - - :library:navigation + + + :library:navigation - - + + - - - :library:coroutines + + + :library:coroutines - - + + - - - :library:datetime + + + :library:datetime - - + + - - - :library:shimmer-m3 + + + :library:shimmer-m3 - - + + - - - :app:domain:account + + + :app:domain:account - - + + - - - :app:domain:credential + + + :app:domain:credential - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - :app:core:model + + + :app:core:model - - + + - - - :common:exception + + + :common:exception - - + + - - + + - - + + - - + + - - + + - - + + - - - :app:domain:fetch + + + :app:domain:fetch - - + + - - - :app:domain:backup + + + :app:domain:backup - - + + - - - :app:domain:fcm + + + :app:domain:fcm - - + + - - + + - - + + - - - - - - + + - - + + - - + + - - + + + + + + - - + + - - + + - - + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + diff --git a/docs/images/graphs/dep_graph_app_feature_calendar.svg b/docs/images/graphs/dep_graph_app_feature_calendar.svg index ffb82911..2caedebc 100644 --- a/docs/images/graphs/dep_graph_app_feature_calendar.svg +++ b/docs/images/graphs/dep_graph_app_feature_calendar.svg @@ -1,253 +1,317 @@ - + - - - - :app:feature:calendar + + + + :app:feature:calendar - - - :app:core:design-system + + + :app:core:compose - - + + - - - :app:core:navigation + + + :app:core:design-system - - + + - - - :app:core:resources + + + :app:core:navigation - - + + - - - :library:color + + + :library:color - - + + - - - :library:kotlin + + + :library:kotlin - - + + - - - :library:navigation + + + :library:navigation - - + + - - - :library:coroutines + + + :library:coroutines - - + + - - - :library:datetime + + + :library:datetime - - + + - - - :library:shimmer-m3 + + + :library:shimmer-m3 - - + + - - - :app:core:calendar-compose + + + :app:core:calendar-compose - - + + - - - :app:domain:memo + + + :app:domain:calendar - - + + - - - :app:domain:holiday + + + :app:domain:holiday - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + :app:core:model - - + + - - - :app:core:model + + + :common:exception - - + + - - - :common:exception + + + :app:domain:account - - + + + + + + :app:domain:memo + + + + - - - :app:domain:account + + + :app:domain:tag - - + + - - - :app:domain:backup + + + - - - + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + :app:domain:backup - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + diff --git a/docs/images/graphs/dep_graph_app_feature_memo.svg b/docs/images/graphs/dep_graph_app_feature_memo.svg index fd49f951..e129de9f 100644 --- a/docs/images/graphs/dep_graph_app_feature_memo.svg +++ b/docs/images/graphs/dep_graph_app_feature_memo.svg @@ -1,201 +1,237 @@ - + - + - - :app:feature:memo + + :app:feature:memo - - :app:core:design-system + + :app:core:compose - - + + - - :app:core:navigation + + :app:core:design-system - - + + - - :app:core:resources + + :app:core:navigation - - + + - - :library:color + + :library:color - - + + - - :library:kotlin + + :library:kotlin - - + + - - :library:navigation + + :library:navigation - - + + - - :library:coroutines + + :library:coroutines - - + + - - :library:datetime + + :library:datetime - - + + - - :library:shimmer-m3 + + :library:shimmer-m3 - - + + - - :app:domain:memo + + :app:domain:memo - - + + + + + + :app:domain:tag - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + - - :app:core:model + + :app:core:model - - + + - - :common:exception + + :common:exception - - + + - - :app:domain:account + + :app:domain:account - - + + - - :app:domain:backup + + :app:domain:backup - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + diff --git a/docs/images/graphs/dep_graph_app_feature_more.svg b/docs/images/graphs/dep_graph_app_feature_more.svg index db462397..77e386c1 100644 --- a/docs/images/graphs/dep_graph_app_feature_more.svg +++ b/docs/images/graphs/dep_graph_app_feature_more.svg @@ -1,265 +1,269 @@ - + - - - - :app:feature:more + + + + :app:feature:more - - - :app:core:design-system + + + :app:core:compose - - + + - - - :app:core:navigation + + + :app:core:design-system - - + + - - - :app:core:resources + + + :app:core:navigation - - + + - - - :library:color + + + :library:color - - + + - - - :library:kotlin + + + :library:kotlin - - + + - - - :library:navigation + + + :library:navigation - - + + - - - :library:coroutines + + + :library:coroutines - - + + - - - :library:datetime + + + :library:datetime - - + + - - - :library:shimmer-m3 + + + :library:shimmer-m3 - - + + - - - :app:domain:account + + + :app:domain:account - - + + - - - :app:domain:credential + + + :app:domain:credential - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - :app:core:model + + + :app:core:model - - + + - - - :common:exception + + + :common:exception - - + + - - + + - - + + - - + + - - + + - - + + - - - :app:domain:fetch + + + :app:domain:fetch - - + + - - - :app:domain:backup + + + :app:domain:backup - - + + - - - :app:domain:fcm + + + :app:domain:fcm - - + + - - + + - - + + - - - - - - + + - - + + - - + + - - + + + + + + - - + + - - + + - - + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + diff --git a/docs/images/graphs/dep_graph_app_feature_tag.svg b/docs/images/graphs/dep_graph_app_feature_tag.svg new file mode 100644 index 00000000..aeeea13d --- /dev/null +++ b/docs/images/graphs/dep_graph_app_feature_tag.svg @@ -0,0 +1,201 @@ + + + + + + :app:feature:tag + + + + :app:core:compose + + + + + + + + :app:core:design-system + + + + + + + + :app:core:navigation + + + + + + + + :library:color + + + + + + + + :library:kotlin + + + + + + + + :library:navigation + + + + + + + + :library:coroutines + + + + + + + + :library:datetime + + + + + + + + :library:shimmer-m3 + + + + + + + + :app:domain:tag + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + :app:core:model + + + + + + + + :common:exception + + + + + + + + :app:domain:account + + + + + + + + :app:domain:backup + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/images/graphs/dep_graph_app_platform_android.svg b/docs/images/graphs/dep_graph_app_platform_android.svg index 91807a93..19556955 100644 --- a/docs/images/graphs/dep_graph_app_platform_android.svg +++ b/docs/images/graphs/dep_graph_app_platform_android.svg @@ -1,969 +1,1157 @@ - + - - - - :app:platform:android + + + + :app:platform:android - - - :app:platform:common + + + :app:platform:common - - + + - - - :app:core:diary-database-room + + + :app:core:diary-service - - + + - - - :app:core:diary-service + + + :app:core:holiday-service - - + + - - - :app:core:account-preferences-datastore + + + :app:domain:fcm - - + + - - - :app:core:holiday-preferences-datastore + + + + + + + - - + + - - - :app:core:holiday-database-room + + + :app:core:compose - - + + - - - :app:core:holiday-service + + + :app:core:design-system - - + + - - - :app:domain:fcm + + + :app:core:navigation - - + + + + + + :library:color - - + + + + + + :library:kotlin - - + + + + + + :library:navigation - - + + - - - :app:core:design-system + + + :library:coroutines - - + + - - - :app:core:navigation + + + :library:datetime - - + + - - - :app:core:resources + + + :library:shimmer-m3 - - + + - - - :library:color + + + :app:data:memo - - + + - - - :library:kotlin + + + :app:data:tag - - + + - - - :library:navigation + + + :app:data:account - - + + - - - :library:coroutines + + + :app:data:credential + + + + + + + + :app:data:holiday - - + + - - - :library:datetime + + + :app:data:backup - - + + - - - :library:shimmer-m3 + + + :app:data:fetch - - + + - - - :app:data:memo + + + :app:data:fcm - - - + + + - - - :app:data:account + + + :app:domain:memo - - + + - - - :app:data:credential + + + :app:domain:tag - - + + - - - :app:data:holiday + + + :app:domain:account - - + + - - - :app:data:backup + + + :app:domain:credential - - + + - - - :app:data:fetch + + + :app:domain:holiday - - + + - - - :app:data:fcm + + + :app:domain:backup - - + + - - - :app:domain:memo + + + :app:domain:fetch - - + + - - - :app:domain:account + + + :app:domain:calendar - - + + - - - :app:domain:credential + + + :app:core:coroutines - - + + - - - :app:domain:holiday + + + :app:core:backup-database-room - - + + - - - :app:domain:backup + + + :app:core:account-preferences-datastore - - + + - - - :app:domain:fetch + + + :app:core:diary-database-room - - + + - - - :app:core:coroutines + + + :app:core:holiday-preferences-datastore - - + + - - - :app:feature:memo + + + :app:core:holiday-database-room - - + + - - - :app:feature:calendar + + + :app:feature:memo - - + + - - - :app:feature:more + + + :app:feature:tag - - + + - - - :app:feature:account + + + :app:feature:calendar - - + + - - - :library:firebase-messaging + + + :app:feature:more - - + + + + + + :app:feature:account - - + + - - - :library:koin-room + + + :library:firebase-messaging - - + + - - - :app:core:diary-database + + + :app:core:model - - + + - - - :library:room + + + :common:exception - - + + - - - :app:core:model + + + :app:core:account-preferences - - + + - - - :common:exception + + + :common:model - - + + - - - :app:core:account-preferences + + + - - + + - - - :common:model + + + - - + + - - + + - - - :library:koin-datastore + + + - - + + - - + + - - + + - - - :app:core:holiday-preferences + + + - - + + - - + + - - + + - - + + - - - :app:core:holiday-database + + + - - + + + + + + :app:core:diary-database - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + - - + + - - + + - - + + - - - + + + - - + + + + + + :app:core:holiday-preferences - - + + + + + + :app:core:holiday-database - - + + - - + + - - + + - - + + - - + + - - + + + + + + :app:core:backup-database + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + :library:koin-room + + + + + + + + :library:room + + + + + + + + + + + + :library:koin-datastore + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - :app:core:calendar-compose + + + :app:core:calendar-compose - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - :library:firebase-common + + + :library:firebase-common - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + diff --git a/docs/images/graphs/dep_graph_app_platform_common.svg b/docs/images/graphs/dep_graph_app_platform_common.svg index 38c6255f..5c017775 100644 --- a/docs/images/graphs/dep_graph_app_platform_common.svg +++ b/docs/images/graphs/dep_graph_app_platform_common.svg @@ -1,849 +1,1137 @@ - + - - - - :app:platform:common + + + + :app:platform:common - - - :app:core:design-system + + + :app:core:compose - - + + - - - :app:core:navigation + + + :app:core:design-system - - + + - - - :app:core:resources + + + :app:core:navigation - - + + - - - :library:color + + + :library:color - - + + - - - :library:kotlin + + + :library:kotlin - - + + - - - :library:navigation + + + :library:navigation - - + + - - - :library:coroutines + + + :library:coroutines - - + + - - - :library:datetime + + + :library:datetime - - + + - - - :library:shimmer-m3 + + + :library:shimmer-m3 - - + + - - - :app:data:memo + + + :app:data:memo + + + + + + + + :app:data:tag + + + + + + + + :app:data:account + + + + + + + + :app:data:credential - - + + + + + + :app:data:holiday + + + + + + + + :app:data:backup + + + + + + + + :app:data:fetch + + + + - - - :app:data:account + + + :app:data:fcm - - + + - - - :app:data:credential + + + :app:domain:memo - - + + - - - :app:data:holiday + + + :app:domain:tag - - + + - - - :app:data:backup + + + :app:domain:account - - + + - - - :app:data:fetch + + + :app:domain:credential - - + + - - - :app:data:fcm + + + :app:domain:holiday - - + + - - - :app:domain:memo + + + :app:domain:backup - - + + - - - :app:domain:account + + + :app:domain:fetch - - + + - - - :app:domain:credential + + + :app:domain:fcm - - + + - - - :app:domain:holiday + + + :app:domain:calendar - - + + - - - :app:domain:backup + + + :app:core:coroutines - - + + - - - :app:domain:fetch + + + :app:core:backup-database-room - - + + - - - :app:domain:fcm + + + :app:core:account-preferences-datastore - - + + - - - :app:core:coroutines + + + :app:core:diary-database-room - - + + - - - :app:core:diary-service + + + :app:core:diary-service - - + + - + :app:core:holiday-service - - + + - - - :app:feature:memo + + + :app:core:holiday-preferences-datastore - - + + - - - :app:feature:calendar + + + :app:core:holiday-database-room - - + + - - - :app:feature:more + + + :app:feature:memo - - + + - - - :app:feature:account + + + :app:feature:tag - - + + - - - :library:firebase-messaging + + + :app:feature:calendar - - + + + + + + :app:feature:more - - + + + + + + :app:feature:account - - + + + + + + :library:firebase-messaging - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + :app:core:diary-database - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + :app:core:account-preferences + + + + + + + + + + + + - - + + + + + + - - - :app:core:diary-database + + + + + + + - - + + - - + + - - + + + + + + :app:core:holiday-preferences - - + + - - - :app:core:account-preferences + + + :app:core:holiday-database - - + + - - + + - - + + - - + + - - + + - - + + + + + + :app:core:backup-database + + + + - - + + - - + + - - + + - - + + - - - :app:core:holiday-preferences + + + - - + + - - - :app:core:holiday-database + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + :app:core:model - - + + + + + + :common:exception - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - :app:core:model + + + - - + + - - - :common:exception + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - + + + + + + + + + + + + + + + + + + + - - + + - - + + + + + + :library:koin-room - - + + + + + + :library:room - - + + - - + + + + + + :library:koin-datastore - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + :common:model - - + + - - + + - - + + - - + + - - - :common:model + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - :app:core:calendar-compose + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + :app:core:calendar-compose - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - :library:firebase-common + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + :library:firebase-common + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + diff --git a/docs/images/graphs/dep_graph_app_platform_ios.svg b/docs/images/graphs/dep_graph_app_platform_ios.svg index be51ee81..85f0f3d8 100644 --- a/docs/images/graphs/dep_graph_app_platform_ios.svg +++ b/docs/images/graphs/dep_graph_app_platform_ios.svg @@ -1,969 +1,1157 @@ - + - - - - :app:platform:ios + + + + :app:platform:ios - - - :app:platform:common + + + :app:platform:common - - + + - - - :app:core:coroutines + + + :app:core:coroutines - - + + - - - :app:core:diary-database-room + + + :app:core:diary-service - - + + - - - :app:core:diary-service + + + :app:core:holiday-service + + + + - - + + - - - :app:core:account-preferences-datastore + + + - - + + - - - :app:core:holiday-preferences-datastore + + + :app:core:compose - - + + - - - :app:core:holiday-database-room + + + :app:core:design-system - - + + - - - :app:core:holiday-service + + + :app:core:navigation + + + + + + + + :library:color - - + + + + + + :library:kotlin - - + + + + + + :library:navigation - - + + + + + + :library:coroutines - - + + - - - :app:core:design-system + + + :library:datetime - - + + - - - :app:core:navigation + + + :library:shimmer-m3 - - + + - - - :app:core:resources + + + :app:data:memo - - + + - - - :library:color + + + :app:data:tag - - + + - - - :library:kotlin + + + :app:data:account - - + + + + + + :app:data:credential + + + + - - - :library:navigation + + + :app:data:holiday - - + + - - - :library:coroutines + + + :app:data:backup - - + + - - - :library:datetime + + + :app:data:fetch - - + + - - - :library:shimmer-m3 + + + :app:data:fcm - - + + - - - :app:data:memo + + + :app:domain:memo - - - + + + - - - :app:data:account + + + :app:domain:tag - - + + - - - :app:data:credential + + + :app:domain:account - - + + - - - :app:data:holiday + + + :app:domain:credential - - + + - - - :app:data:backup + + + :app:domain:holiday - - + + - - - :app:data:fetch + + + :app:domain:backup - - + + - - - :app:data:fcm + + + :app:domain:fetch - - + + - - - :app:domain:memo + + + :app:domain:fcm - - + + - - - :app:domain:account + + + :app:domain:calendar - - + + - - - :app:domain:credential + + + :app:core:backup-database-room - - + + - - - :app:domain:holiday + + + :app:core:account-preferences-datastore - - + + - - - :app:domain:backup + + + :app:core:diary-database-room - - + + - - - :app:domain:fetch + + + :app:core:holiday-preferences-datastore - - + + - - - :app:domain:fcm + + + :app:core:holiday-database-room - - + + - - - :app:feature:memo + + + :app:feature:memo - - + + - - - :app:feature:calendar + + + :app:feature:tag - - + + - - - :app:feature:more + + + :app:feature:calendar - - + + - - - :app:feature:account + + + :app:feature:more - - + + - - - :library:firebase-messaging + + + :app:feature:account - - + + + + + + :library:firebase-messaging - - + + - - - :library:koin-room + + + :app:core:model - - + + - - - :app:core:diary-database + + + :common:exception - - + + - - - :library:room + + + :app:core:account-preferences - - + + - - - :app:core:model + + + :common:model - - + + - - - :common:exception + + + - - + + - - - :app:core:account-preferences + + + - - + + - - - :common:model + + + - - + + - - + + - - - :library:koin-datastore + + + - - + + - - + + + + + + :app:core:diary-database - - + + - - - :app:core:holiday-preferences + + + - - + + - - + + - - + + - - + + - - - :app:core:holiday-database + + + - - + + - - + + - - + + - - + + - - + + + + + + - - + + - - + + - - + + - - + + - - + + - - - + + + :app:core:holiday-preferences - - + + + + + + :app:core:holiday-database - - + + - - + + - - + + - - + + - - + + - - + + + + + + :app:core:backup-database - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + :library:koin-room + + + + + + + + :library:room + + + + + + + + + + + + :library:koin-datastore + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - :app:core:calendar-compose + + + :app:core:calendar-compose - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - :library:firebase-common + + + :library:firebase-common - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + diff --git a/docs/images/graphs/dep_graph_app_platform_jvm.svg b/docs/images/graphs/dep_graph_app_platform_jvm.svg index 81c5faac..4fac530e 100644 --- a/docs/images/graphs/dep_graph_app_platform_jvm.svg +++ b/docs/images/graphs/dep_graph_app_platform_jvm.svg @@ -1,977 +1,1165 @@ - + - - - - :app:platform:jvm + + + + :app:platform:jvm - - - :app:platform:common + + + :app:platform:common - - + + - - - :app:core:coroutines + + + :app:core:coroutines - - + + - - - :app:core:diary-database-room + + + :app:core:diary-service - - + + - - - :app:core:diary-service + + + :app:core:holiday-service - - + + - - - :app:core:account-preferences-datastore + + + :library:koin-room - - + + - - - :app:core:holiday-preferences-datastore + + + :library:koin-datastore - - - - - - :app:core:holiday-database-room + + - - + + - - - :app:core:holiday-service + + + - - + + - - - :library:koin-room + + + :app:core:compose - - + + - - - :library:koin-datastore + + + :app:core:design-system - - + + + + + + :app:core:navigation - - + + + + + + :library:color - - + + + + + + :library:kotlin - - + + - - - :app:core:design-system + + + :library:navigation - - + + - - - :app:core:navigation + + + :library:coroutines - - + + - - - :app:core:resources + + + :library:datetime - - + + - - - :library:color + + + :library:shimmer-m3 - - + + - - - :library:kotlin + + + :app:data:memo - - + + - - - :library:navigation + + + :app:data:tag - - + + - - - :library:coroutines + + + :app:data:account - - + + + + + + :app:data:credential - - - :library:datetime + + + + + + + :app:data:holiday - - + + - - - :library:shimmer-m3 + + + :app:data:backup - - + + - - - :app:data:memo + + + :app:data:fetch - - - + + + - - - :app:data:account + + + :app:data:fcm - - + + - - - :app:data:credential + + + :app:domain:memo - - + + - - - :app:data:holiday + + + :app:domain:tag - - + + - - - :app:data:backup + + + :app:domain:account - - + + - - - :app:data:fetch + + + :app:domain:credential - - + + - - - :app:data:fcm + + + :app:domain:holiday - - + + - - - :app:domain:memo + + + :app:domain:backup - - + + - - - :app:domain:account + + + :app:domain:fetch - - + + - - - :app:domain:credential + + + :app:domain:fcm - - + + - - - :app:domain:holiday + + + :app:domain:calendar - - + + - - - :app:domain:backup + + + :app:core:backup-database-room - - + + - - - :app:domain:fetch + + + :app:core:account-preferences-datastore - - + + - - - :app:domain:fcm + + + :app:core:diary-database-room - - + + - - - :app:feature:memo + + + :app:core:holiday-preferences-datastore - - + + - - - :app:feature:calendar + + + :app:core:holiday-database-room - - + + - - - :app:feature:more + + + :app:feature:memo - - + + - - - :app:feature:account + + + :app:feature:tag - - + + - - - :library:firebase-messaging + + + :app:feature:calendar - - + + + + + + :app:feature:more - - + + + + + + :app:feature:account - - + + - - - :app:core:diary-database + + + :library:firebase-messaging - - + + - - - :library:room + + + :app:core:model - - + + - - - :app:core:model + + + :common:exception - - + + - - - :common:exception + + + :app:core:account-preferences - - + + - - - :app:core:account-preferences + + + :common:model - - + + - - - :common:model + + + - - + + - - + + - - + + - - + + - - + + - - - :app:core:holiday-preferences + + + - - + + - - + + - - + + + + + + :app:core:diary-database - - + + - - - :app:core:holiday-database + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + :app:core:holiday-preferences - - + + + + + + :app:core:holiday-database - - + + - - + + - - + + - - + + - - + + - - + + + + + + :app:core:backup-database - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + :library:room + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - :app:core:calendar-compose + + + :app:core:calendar-compose - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - :library:firebase-common + + + :library:firebase-common - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + diff --git a/docs/images/graphs/dep_graph_app_platform_wasm.svg b/docs/images/graphs/dep_graph_app_platform_wasm.svg deleted file mode 100644 index d2bbad42..00000000 --- a/docs/images/graphs/dep_graph_app_platform_wasm.svg +++ /dev/null @@ -1,925 +0,0 @@ - - - - - - :app:platform:wasm - - - - :app:platform:common - - - - - - - - :app:core:coroutines - - - - - - - - :app:core:diary-database-memory - - - - - - - - :app:core:diary-service - - - - - - - - :app:core:account-preferences-memory - - - - - - - - :app:core:holiday-preferences-memory - - - - - - - - :app:core:holiday-database-memory - - - - - - - - :app:core:holiday-service - - - - - - - - - - - - - - - - - - - - :app:core:design-system - - - - - - - - :app:core:navigation - - - - - - - - :app:core:resources - - - - - - - - :library:color - - - - - - - - :library:kotlin - - - - - - - - :library:navigation - - - - - - - - :library:coroutines - - - - - - - - :library:datetime - - - - - - - - :library:shimmer-m3 - - - - - - - - :app:data:memo - - - - - - - - :app:data:account - - - - - - - - :app:data:credential - - - - - - - - :app:data:holiday - - - - - - - - :app:data:backup - - - - - - - - :app:data:fetch - - - - - - - - :app:data:fcm - - - - - - - - :app:domain:memo - - - - - - - - :app:domain:account - - - - - - - - :app:domain:credential - - - - - - - - :app:domain:holiday - - - - - - - - :app:domain:backup - - - - - - - - :app:domain:fetch - - - - - - - - :app:domain:fcm - - - - - - - - :app:feature:memo - - - - - - - - :app:feature:calendar - - - - - - - - :app:feature:more - - - - - - - - :app:feature:account - - - - - - - - :library:firebase-messaging - - - - - - - - - - - - - - - - :app:core:diary-database - - - - - - - - :app:core:model - - - - - - - - :common:exception - - - - - - - - :app:core:account-preferences - - - - - - - - :common:model - - - - - - - - - - - - :app:core:holiday-preferences - - - - - - - - :app:core:holiday-database - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - :app:core:calendar-compose - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - :library:firebase-common - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/images/graphs/dep_graph_common_exception.svg b/docs/images/graphs/dep_graph_common_exception.svg index 9fc01b65..e69de29b 100644 --- a/docs/images/graphs/dep_graph_common_exception.svg +++ b/docs/images/graphs/dep_graph_common_exception.svg @@ -1,9 +0,0 @@ - - - - - - :common:exception - - - diff --git a/docs/images/graphs/dep_graph_common_model.svg b/docs/images/graphs/dep_graph_common_model.svg index 10f453c6..e69de29b 100644 --- a/docs/images/graphs/dep_graph_common_model.svg +++ b/docs/images/graphs/dep_graph_common_model.svg @@ -1,9 +0,0 @@ - - - - - - :common:model - - - diff --git a/docs/images/graphs/dep_graph_library_color.svg b/docs/images/graphs/dep_graph_library_color.svg index 5fa2d0a8..e69de29b 100644 --- a/docs/images/graphs/dep_graph_library_color.svg +++ b/docs/images/graphs/dep_graph_library_color.svg @@ -1,9 +0,0 @@ - - - - - - :library:color - - - diff --git a/docs/images/graphs/dep_graph_library_coroutines.svg b/docs/images/graphs/dep_graph_library_coroutines.svg index 831e504b..e69de29b 100644 --- a/docs/images/graphs/dep_graph_library_coroutines.svg +++ b/docs/images/graphs/dep_graph_library_coroutines.svg @@ -1,9 +0,0 @@ - - - - - - :library:coroutines - - - diff --git a/docs/images/graphs/dep_graph_library_datetime.svg b/docs/images/graphs/dep_graph_library_datetime.svg index 94bc4256..e69de29b 100644 --- a/docs/images/graphs/dep_graph_library_datetime.svg +++ b/docs/images/graphs/dep_graph_library_datetime.svg @@ -1,9 +0,0 @@ - - - - - - :library:datetime - - - diff --git a/docs/images/graphs/dep_graph_library_firebase_common.svg b/docs/images/graphs/dep_graph_library_firebase_common.svg index 78fcd833..e69de29b 100644 --- a/docs/images/graphs/dep_graph_library_firebase_common.svg +++ b/docs/images/graphs/dep_graph_library_firebase_common.svg @@ -1,9 +0,0 @@ - - - - - - :library:firebase-common - - - diff --git a/docs/images/graphs/dep_graph_library_firebase_messaging.svg b/docs/images/graphs/dep_graph_library_firebase_messaging.svg index 65cd3e1b..e69de29b 100644 --- a/docs/images/graphs/dep_graph_library_firebase_messaging.svg +++ b/docs/images/graphs/dep_graph_library_firebase_messaging.svg @@ -1,17 +0,0 @@ - - - - - - :library:firebase-messaging - - - - :library:firebase-common - - - - - - - diff --git a/docs/images/graphs/dep_graph_library_koin_datastore.svg b/docs/images/graphs/dep_graph_library_koin_datastore.svg index a9cdf8cb..e69de29b 100644 --- a/docs/images/graphs/dep_graph_library_koin_datastore.svg +++ b/docs/images/graphs/dep_graph_library_koin_datastore.svg @@ -1,9 +0,0 @@ - - - - - - :library:koin-datastore - - - diff --git a/docs/images/graphs/dep_graph_library_koin_room.svg b/docs/images/graphs/dep_graph_library_koin_room.svg index 35267461..e69de29b 100644 --- a/docs/images/graphs/dep_graph_library_koin_room.svg +++ b/docs/images/graphs/dep_graph_library_koin_room.svg @@ -1,9 +0,0 @@ - - - - - - :library:koin-room - - - diff --git a/docs/images/graphs/dep_graph_library_kotlin.svg b/docs/images/graphs/dep_graph_library_kotlin.svg index 9f42633b..e69de29b 100644 --- a/docs/images/graphs/dep_graph_library_kotlin.svg +++ b/docs/images/graphs/dep_graph_library_kotlin.svg @@ -1,9 +0,0 @@ - - - - - - :library:kotlin - - - diff --git a/docs/images/graphs/dep_graph_library_navigation.svg b/docs/images/graphs/dep_graph_library_navigation.svg index 98993cd7..e69de29b 100644 --- a/docs/images/graphs/dep_graph_library_navigation.svg +++ b/docs/images/graphs/dep_graph_library_navigation.svg @@ -1,17 +0,0 @@ - - - - - - :library:navigation - - - - :library:datetime - - - - - - - diff --git a/docs/images/graphs/dep_graph_library_room.svg b/docs/images/graphs/dep_graph_library_room.svg index e880c6f8..e69de29b 100644 --- a/docs/images/graphs/dep_graph_library_room.svg +++ b/docs/images/graphs/dep_graph_library_room.svg @@ -1,17 +0,0 @@ - - - - - - :library:room - - - - :library:datetime - - - - - - - diff --git a/docs/images/graphs/dep_graph_library_shimmer_m3.svg b/docs/images/graphs/dep_graph_library_shimmer_m3.svg index 4e31ccd8..e69de29b 100644 --- a/docs/images/graphs/dep_graph_library_shimmer_m3.svg +++ b/docs/images/graphs/dep_graph_library_shimmer_m3.svg @@ -1,17 +0,0 @@ - - - - - - :library:shimmer-m3 - - - - :library:color - - - - - - - diff --git a/docs/images/graphs/dep_graph_server_app.svg b/docs/images/graphs/dep_graph_server_app.svg index 99884d1d..62e6930e 100644 --- a/docs/images/graphs/dep_graph_server_app.svg +++ b/docs/images/graphs/dep_graph_server_app.svg @@ -1,253 +1,317 @@ - + - + - - :server:app + + :server:app - - :server:core:database + + :server:core:database - - + + - - :server:data:account + + :server:data:account - - + + - - :server:data:memo + + :server:data:memo - - + + - - :server:data:fcm + + :server:data:tag - - + + - - :server:domain:account + + :server:data:fcm - - + + - - :server:domain:memo + + :server:domain:account - - + + - - :server:domain:fcm + + :server:domain:memo - - + + - - :server:feature:home + + :server:domain:tag - - + + - - :server:feature:account + + :server:domain:fcm - - + + - - :server:feature:memo + + :server:feature:home - - + + - - :server:feature:fcm + + :server:feature:account - - + + - - :common:model + + :server:feature:memo - - + + - - :server:core:model + + :server:feature:tag + + + + + + + + :server:feature:fcm + + + + + + + + :common:model + + + + + + + + :server:core:model - - + + - - + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + - - :common:exception + + :common:exception - - + + - - :library:kotlin + + :library:kotlin + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + diff --git a/docs/images/graphs/dep_graph_server_core_database.svg b/docs/images/graphs/dep_graph_server_core_database.svg index dfbf2f18..e69de29b 100644 --- a/docs/images/graphs/dep_graph_server_core_database.svg +++ b/docs/images/graphs/dep_graph_server_core_database.svg @@ -1,17 +0,0 @@ - - - - - - :server:core:database - - - - :server:core:model - - - - - - - diff --git a/docs/images/graphs/dep_graph_server_core_model.svg b/docs/images/graphs/dep_graph_server_core_model.svg index 7483a13d..e69de29b 100644 --- a/docs/images/graphs/dep_graph_server_core_model.svg +++ b/docs/images/graphs/dep_graph_server_core_model.svg @@ -1,9 +0,0 @@ - - - - - - :server:core:model - - - diff --git a/docs/images/graphs/dep_graph_server_data_account.svg b/docs/images/graphs/dep_graph_server_data_account.svg index ae0ff61b..e69de29b 100644 --- a/docs/images/graphs/dep_graph_server_data_account.svg +++ b/docs/images/graphs/dep_graph_server_data_account.svg @@ -1,57 +0,0 @@ - - - - - - :server:data:account - - - - :server:core:model - - - - - - - - :server:core:database - - - - - - - - :server:domain:account - - - - - - - - - - - - - - - - :common:exception - - - - - - - - :library:kotlin - - - - - - - diff --git a/docs/images/graphs/dep_graph_server_data_fcm.svg b/docs/images/graphs/dep_graph_server_data_fcm.svg index 0ccbfb26..e69de29b 100644 --- a/docs/images/graphs/dep_graph_server_data_fcm.svg +++ b/docs/images/graphs/dep_graph_server_data_fcm.svg @@ -1,57 +0,0 @@ - - - - - - :server:data:fcm - - - - :server:core:model - - - - - - - - :server:core:database - - - - - - - - :server:domain:fcm - - - - - - - - - - - - - - - - :common:exception - - - - - - - - :library:kotlin - - - - - - - diff --git a/docs/images/graphs/dep_graph_server_data_memo.svg b/docs/images/graphs/dep_graph_server_data_memo.svg index 2d17dc80..e69de29b 100644 --- a/docs/images/graphs/dep_graph_server_data_memo.svg +++ b/docs/images/graphs/dep_graph_server_data_memo.svg @@ -1,57 +0,0 @@ - - - - - - :server:data:memo - - - - :server:core:model - - - - - - - - :server:core:database - - - - - - - - :server:domain:memo - - - - - - - - - - - - - - - - :common:exception - - - - - - - - :library:kotlin - - - - - - - diff --git a/docs/images/graphs/dep_graph_server_data_tag.svg b/docs/images/graphs/dep_graph_server_data_tag.svg new file mode 100644 index 00000000..28ce38c7 --- /dev/null +++ b/docs/images/graphs/dep_graph_server_data_tag.svg @@ -0,0 +1,57 @@ + + + + + + :server:data:tag + + + + :server:core:model + + + + + + + + :server:core:database + + + + + + + + :server:domain:tag + + + + + + + + + + + + + + + + :common:exception + + + + + + + + :library:kotlin + + + + + + + diff --git a/docs/images/graphs/dep_graph_server_domain_account.svg b/docs/images/graphs/dep_graph_server_domain_account.svg index 5e392609..e69de29b 100644 --- a/docs/images/graphs/dep_graph_server_domain_account.svg +++ b/docs/images/graphs/dep_graph_server_domain_account.svg @@ -1,33 +0,0 @@ - - - - - - :server:domain:account - - - - :server:core:model - - - - - - - - :common:exception - - - - - - - - :library:kotlin - - - - - - - diff --git a/docs/images/graphs/dep_graph_server_domain_fcm.svg b/docs/images/graphs/dep_graph_server_domain_fcm.svg index 559a4e63..e69de29b 100644 --- a/docs/images/graphs/dep_graph_server_domain_fcm.svg +++ b/docs/images/graphs/dep_graph_server_domain_fcm.svg @@ -1,33 +0,0 @@ - - - - - - :server:domain:fcm - - - - :server:core:model - - - - - - - - :common:exception - - - - - - - - :library:kotlin - - - - - - - diff --git a/docs/images/graphs/dep_graph_server_domain_memo.svg b/docs/images/graphs/dep_graph_server_domain_memo.svg index 1fe3ec0b..e69de29b 100644 --- a/docs/images/graphs/dep_graph_server_domain_memo.svg +++ b/docs/images/graphs/dep_graph_server_domain_memo.svg @@ -1,33 +0,0 @@ - - - - - - :server:domain:memo - - - - :server:core:model - - - - - - - - :common:exception - - - - - - - - :library:kotlin - - - - - - - diff --git a/docs/images/graphs/dep_graph_server_domain_tag.svg b/docs/images/graphs/dep_graph_server_domain_tag.svg new file mode 100644 index 00000000..bb37cf21 --- /dev/null +++ b/docs/images/graphs/dep_graph_server_domain_tag.svg @@ -0,0 +1,33 @@ + + + + + + :server:domain:tag + + + + :server:core:model + + + + + + + + :common:exception + + + + + + + + :library:kotlin + + + + + + + diff --git a/docs/images/graphs/dep_graph_server_feature_account.svg b/docs/images/graphs/dep_graph_server_feature_account.svg index bd36fe75..e69de29b 100644 --- a/docs/images/graphs/dep_graph_server_feature_account.svg +++ b/docs/images/graphs/dep_graph_server_feature_account.svg @@ -1,57 +0,0 @@ - - - - - - :server:feature:account - - - - :server:core:model - - - - - - - - :common:model - - - - - - - - :common:exception - - - - - - - - :server:domain:account - - - - - - - - - - - - - - - - :library:kotlin - - - - - - - diff --git a/docs/images/graphs/dep_graph_server_feature_fcm.svg b/docs/images/graphs/dep_graph_server_feature_fcm.svg index 012aef53..e69de29b 100644 --- a/docs/images/graphs/dep_graph_server_feature_fcm.svg +++ b/docs/images/graphs/dep_graph_server_feature_fcm.svg @@ -1,57 +0,0 @@ - - - - - - :server:feature:fcm - - - - :server:core:model - - - - - - - - :common:model - - - - - - - - :common:exception - - - - - - - - :server:domain:fcm - - - - - - - - - - - - - - - - :library:kotlin - - - - - - - diff --git a/docs/images/graphs/dep_graph_server_feature_home.svg b/docs/images/graphs/dep_graph_server_feature_home.svg index 0123a03c..e69de29b 100644 --- a/docs/images/graphs/dep_graph_server_feature_home.svg +++ b/docs/images/graphs/dep_graph_server_feature_home.svg @@ -1,33 +0,0 @@ - - - - - - :server:feature:home - - - - :server:core:model - - - - - - - - :common:model - - - - - - - - :common:exception - - - - - - - diff --git a/docs/images/graphs/dep_graph_server_feature_memo.svg b/docs/images/graphs/dep_graph_server_feature_memo.svg index 74c26d31..e69de29b 100644 --- a/docs/images/graphs/dep_graph_server_feature_memo.svg +++ b/docs/images/graphs/dep_graph_server_feature_memo.svg @@ -1,57 +0,0 @@ - - - - - - :server:feature:memo - - - - :server:core:model - - - - - - - - :common:model - - - - - - - - :common:exception - - - - - - - - :server:domain:memo - - - - - - - - - - - - - - - - :library:kotlin - - - - - - - diff --git a/docs/images/graphs/dep_graph_server_feature_tag.svg b/docs/images/graphs/dep_graph_server_feature_tag.svg new file mode 100644 index 00000000..221848ca --- /dev/null +++ b/docs/images/graphs/dep_graph_server_feature_tag.svg @@ -0,0 +1,57 @@ + + + + + + :server:feature:tag + + + + :server:core:model + + + + + + + + :common:model + + + + + + + + :common:exception + + + + + + + + :server:domain:tag + + + + + + + + + + + + + + + + :library:kotlin + + + + + + + diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 291ce091..1137765c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -18,8 +18,8 @@ androidx-lifecycle = "2.8.5" compose-markdown = "0.27.0" # https://github.com/mikepenz/multiplatform-markdown-renderer/releases -koin = "4.0.0" # https://github.com/InsertKoinIO/koin/releases -koin-annotations = "2.0.0-Beta1" # https://github.com/InsertKoinIO/koin-annotations/releases +koin = "4.1.0-Beta1" # https://github.com/InsertKoinIO/koin/releases +koin-annotations = "2.0.0-Beta2" # https://github.com/InsertKoinIO/koin-annotations/releases datastore = "1.1.1" # https://developer.android.com/jetpack/androidx/releases/datastore?hl=en room = "2.7.0-alpha11" # https://developer.android.com/jetpack/androidx/releases/room?hl=en sqlite = "2.5.0-alpha11" # https://developer.android.com/jetpack/androidx/releases/sqlite?hl=en diff --git a/kotlin-js-store/yarn.lock b/kotlin-js-store/yarn.lock deleted file mode 100644 index 92beced3..00000000 --- a/kotlin-js-store/yarn.lock +++ /dev/null @@ -1,2845 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@colors/colors@1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" - integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== - -"@discoveryjs/json-ext@^0.5.0": - version "0.5.7" - resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" - integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== - -"@jridgewell/gen-mapping@^0.3.5": - version "0.3.5" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" - integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== - dependencies: - "@jridgewell/set-array" "^1.2.1" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/trace-mapping" "^0.3.24" - -"@jridgewell/resolve-uri@^3.1.0": - version "3.1.2" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" - integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== - -"@jridgewell/set-array@^1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" - integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== - -"@jridgewell/source-map@^0.3.3": - version "0.3.6" - resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.6.tgz#9d71ca886e32502eb9362c9a74a46787c36df81a" - integrity sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ== - dependencies: - "@jridgewell/gen-mapping" "^0.3.5" - "@jridgewell/trace-mapping" "^0.3.25" - -"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" - integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== - -"@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": - version "0.3.25" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" - integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== - dependencies: - "@jridgewell/resolve-uri" "^3.1.0" - "@jridgewell/sourcemap-codec" "^1.4.14" - -"@js-joda/core@3.2.0": - version "3.2.0" - resolved "https://registry.yarnpkg.com/@js-joda/core/-/core-3.2.0.tgz#3e61e21b7b2b8a6be746df1335cf91d70db2a273" - integrity sha512-PMqgJ0sw5B7FKb2d5bWYIoxjri+QlW/Pys7+Rw82jSH0QN3rB05jZ/VrrsUdh1w4+i2kw9JOejXGq/KhDOX7Kg== - -"@leichtgewicht/ip-codec@^2.0.1": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz#4fc56c15c580b9adb7dc3c333a134e540b44bfb1" - integrity sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw== - -"@socket.io/component-emitter@~3.1.0": - version "3.1.2" - resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz#821f8442f4175d8f0467b9daf26e3a18e2d02af2" - integrity sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA== - -"@types/body-parser@*": - version "1.19.5" - resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.5.tgz#04ce9a3b677dc8bd681a17da1ab9835dc9d3ede4" - integrity sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg== - dependencies: - "@types/connect" "*" - "@types/node" "*" - -"@types/bonjour@^3.5.9": - version "3.5.13" - resolved "https://registry.yarnpkg.com/@types/bonjour/-/bonjour-3.5.13.tgz#adf90ce1a105e81dd1f9c61fdc5afda1bfb92956" - integrity sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ== - dependencies: - "@types/node" "*" - -"@types/connect-history-api-fallback@^1.3.5": - version "1.5.4" - resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz#7de71645a103056b48ac3ce07b3520b819c1d5b3" - integrity sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw== - dependencies: - "@types/express-serve-static-core" "*" - "@types/node" "*" - -"@types/connect@*": - version "3.4.38" - resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858" - integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== - dependencies: - "@types/node" "*" - -"@types/cookie@^0.4.1": - version "0.4.1" - resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.1.tgz#bfd02c1f2224567676c1545199f87c3a861d878d" - integrity sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q== - -"@types/cors@^2.8.12": - version "2.8.17" - resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.17.tgz#5d718a5e494a8166f569d986794e49c48b216b2b" - integrity sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA== - dependencies: - "@types/node" "*" - -"@types/eslint-scope@^3.7.3": - version "3.7.7" - resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" - integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg== - dependencies: - "@types/eslint" "*" - "@types/estree" "*" - -"@types/eslint@*": - version "9.6.1" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-9.6.1.tgz#d5795ad732ce81715f27f75da913004a56751584" - integrity sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag== - dependencies: - "@types/estree" "*" - "@types/json-schema" "*" - -"@types/estree@*", "@types/estree@^1.0.5": - version "1.0.6" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" - integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== - -"@types/express-serve-static-core@*", "@types/express-serve-static-core@^5.0.0": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-5.0.1.tgz#3c9997ae9d00bc236e45c6374e84f2596458d9db" - integrity sha512-CRICJIl0N5cXDONAdlTv5ShATZ4HEwk6kDDIW2/w9qOWKg+NU/5F8wYRWCrONad0/UKkloNSmmyN/wX4rtpbVA== - dependencies: - "@types/node" "*" - "@types/qs" "*" - "@types/range-parser" "*" - "@types/send" "*" - -"@types/express-serve-static-core@^4.17.33": - version "4.19.6" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz#e01324c2a024ff367d92c66f48553ced0ab50267" - integrity sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A== - dependencies: - "@types/node" "*" - "@types/qs" "*" - "@types/range-parser" "*" - "@types/send" "*" - -"@types/express@*": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@types/express/-/express-5.0.0.tgz#13a7d1f75295e90d19ed6e74cab3678488eaa96c" - integrity sha512-DvZriSMehGHL1ZNLzi6MidnsDhUZM/x2pRdDIKdwbUNqqwHxMlRdkxtn6/EPKyqKpHqTl/4nRZsRNLpZxZRpPQ== - dependencies: - "@types/body-parser" "*" - "@types/express-serve-static-core" "^5.0.0" - "@types/qs" "*" - "@types/serve-static" "*" - -"@types/express@^4.17.13": - version "4.17.21" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.21.tgz#c26d4a151e60efe0084b23dc3369ebc631ed192d" - integrity sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ== - dependencies: - "@types/body-parser" "*" - "@types/express-serve-static-core" "^4.17.33" - "@types/qs" "*" - "@types/serve-static" "*" - -"@types/http-errors@*": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f" - integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA== - -"@types/http-proxy@^1.17.8": - version "1.17.15" - resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.15.tgz#12118141ce9775a6499ecb4c01d02f90fc839d36" - integrity sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ== - dependencies: - "@types/node" "*" - -"@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": - version "7.0.15" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" - integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== - -"@types/mime@^1": - version "1.3.5" - resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" - integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== - -"@types/node-forge@^1.3.0": - version "1.3.11" - resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-1.3.11.tgz#0972ea538ddb0f4d9c2fa0ec5db5724773a604da" - integrity sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ== - dependencies: - "@types/node" "*" - -"@types/node@*", "@types/node@>=10.0.0": - version "22.8.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.8.1.tgz#b39d4b98165e2ae792ce213f610c7c6108ccfa16" - integrity sha512-k6Gi8Yyo8EtrNtkHXutUu2corfDf9su95VYVP10aGYMMROM6SAItZi0w1XszA6RtWTHSVp5OeFof37w0IEqCQg== - dependencies: - undici-types "~6.19.8" - -"@types/qs@*": - version "6.9.16" - resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.16.tgz#52bba125a07c0482d26747d5d4947a64daf8f794" - integrity sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A== - -"@types/range-parser@*": - version "1.2.7" - resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" - integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== - -"@types/retry@0.12.0": - version "0.12.0" - resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" - integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== - -"@types/send@*": - version "0.17.4" - resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.4.tgz#6619cd24e7270793702e4e6a4b958a9010cfc57a" - integrity sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA== - dependencies: - "@types/mime" "^1" - "@types/node" "*" - -"@types/serve-index@^1.9.1": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.4.tgz#e6ae13d5053cb06ed36392110b4f9a49ac4ec898" - integrity sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug== - dependencies: - "@types/express" "*" - -"@types/serve-static@*", "@types/serve-static@^1.13.10": - version "1.15.7" - resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.7.tgz#22174bbd74fb97fe303109738e9b5c2f3064f714" - integrity sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw== - dependencies: - "@types/http-errors" "*" - "@types/node" "*" - "@types/send" "*" - -"@types/sockjs@^0.3.33": - version "0.3.36" - resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.36.tgz#ce322cf07bcc119d4cbf7f88954f3a3bd0f67535" - integrity sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q== - dependencies: - "@types/node" "*" - -"@types/ws@^8.5.5": - version "8.5.12" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.12.tgz#619475fe98f35ccca2a2f6c137702d85ec247b7e" - integrity sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ== - dependencies: - "@types/node" "*" - -"@webassemblyjs/ast@1.12.1", "@webassemblyjs/ast@^1.12.1": - version "1.12.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.12.1.tgz#bb16a0e8b1914f979f45864c23819cc3e3f0d4bb" - integrity sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg== - dependencies: - "@webassemblyjs/helper-numbers" "1.11.6" - "@webassemblyjs/helper-wasm-bytecode" "1.11.6" - -"@webassemblyjs/floating-point-hex-parser@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz#dacbcb95aff135c8260f77fa3b4c5fea600a6431" - integrity sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw== - -"@webassemblyjs/helper-api-error@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz#6132f68c4acd59dcd141c44b18cbebbd9f2fa768" - integrity sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q== - -"@webassemblyjs/helper-buffer@1.12.1": - version "1.12.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz#6df20d272ea5439bf20ab3492b7fb70e9bfcb3f6" - integrity sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw== - -"@webassemblyjs/helper-numbers@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz#cbce5e7e0c1bd32cf4905ae444ef64cea919f1b5" - integrity sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g== - dependencies: - "@webassemblyjs/floating-point-hex-parser" "1.11.6" - "@webassemblyjs/helper-api-error" "1.11.6" - "@xtuc/long" "4.2.2" - -"@webassemblyjs/helper-wasm-bytecode@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz#bb2ebdb3b83aa26d9baad4c46d4315283acd51e9" - integrity sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA== - -"@webassemblyjs/helper-wasm-section@1.12.1": - version "1.12.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz#3da623233ae1a60409b509a52ade9bc22a37f7bf" - integrity sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g== - dependencies: - "@webassemblyjs/ast" "1.12.1" - "@webassemblyjs/helper-buffer" "1.12.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.6" - "@webassemblyjs/wasm-gen" "1.12.1" - -"@webassemblyjs/ieee754@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz#bb665c91d0b14fffceb0e38298c329af043c6e3a" - integrity sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg== - dependencies: - "@xtuc/ieee754" "^1.2.0" - -"@webassemblyjs/leb128@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.6.tgz#70e60e5e82f9ac81118bc25381a0b283893240d7" - integrity sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ== - dependencies: - "@xtuc/long" "4.2.2" - -"@webassemblyjs/utf8@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.6.tgz#90f8bc34c561595fe156603be7253cdbcd0fab5a" - integrity sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA== - -"@webassemblyjs/wasm-edit@^1.12.1": - version "1.12.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz#9f9f3ff52a14c980939be0ef9d5df9ebc678ae3b" - integrity sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g== - dependencies: - "@webassemblyjs/ast" "1.12.1" - "@webassemblyjs/helper-buffer" "1.12.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.6" - "@webassemblyjs/helper-wasm-section" "1.12.1" - "@webassemblyjs/wasm-gen" "1.12.1" - "@webassemblyjs/wasm-opt" "1.12.1" - "@webassemblyjs/wasm-parser" "1.12.1" - "@webassemblyjs/wast-printer" "1.12.1" - -"@webassemblyjs/wasm-gen@1.12.1": - version "1.12.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz#a6520601da1b5700448273666a71ad0a45d78547" - integrity sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w== - dependencies: - "@webassemblyjs/ast" "1.12.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.6" - "@webassemblyjs/ieee754" "1.11.6" - "@webassemblyjs/leb128" "1.11.6" - "@webassemblyjs/utf8" "1.11.6" - -"@webassemblyjs/wasm-opt@1.12.1": - version "1.12.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz#9e6e81475dfcfb62dab574ac2dda38226c232bc5" - integrity sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg== - dependencies: - "@webassemblyjs/ast" "1.12.1" - "@webassemblyjs/helper-buffer" "1.12.1" - "@webassemblyjs/wasm-gen" "1.12.1" - "@webassemblyjs/wasm-parser" "1.12.1" - -"@webassemblyjs/wasm-parser@1.12.1", "@webassemblyjs/wasm-parser@^1.12.1": - version "1.12.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz#c47acb90e6f083391e3fa61d113650eea1e95937" - integrity sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ== - dependencies: - "@webassemblyjs/ast" "1.12.1" - "@webassemblyjs/helper-api-error" "1.11.6" - "@webassemblyjs/helper-wasm-bytecode" "1.11.6" - "@webassemblyjs/ieee754" "1.11.6" - "@webassemblyjs/leb128" "1.11.6" - "@webassemblyjs/utf8" "1.11.6" - -"@webassemblyjs/wast-printer@1.12.1": - version "1.12.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz#bcecf661d7d1abdaf989d8341a4833e33e2b31ac" - integrity sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA== - dependencies: - "@webassemblyjs/ast" "1.12.1" - "@xtuc/long" "4.2.2" - -"@webpack-cli/configtest@^2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-2.1.1.tgz#3b2f852e91dac6e3b85fb2a314fb8bef46d94646" - integrity sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw== - -"@webpack-cli/info@^2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-2.0.2.tgz#cc3fbf22efeb88ff62310cf885c5b09f44ae0fdd" - integrity sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A== - -"@webpack-cli/serve@^2.0.5": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-2.0.5.tgz#325db42395cd49fe6c14057f9a900e427df8810e" - integrity sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ== - -"@xtuc/ieee754@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" - integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== - -"@xtuc/long@4.2.2": - version "4.2.2" - resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" - integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== - -accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: - version "1.3.8" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" - integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== - dependencies: - mime-types "~2.1.34" - negotiator "0.6.3" - -acorn-import-attributes@^1.9.5: - version "1.9.5" - resolved "https://registry.yarnpkg.com/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz#7eb1557b1ba05ef18b5ed0ec67591bfab04688ef" - integrity sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ== - -acorn@^8.7.1, acorn@^8.8.2: - version "8.13.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.13.0.tgz#2a30d670818ad16ddd6a35d3842dacec9e5d7ca3" - integrity sha512-8zSiw54Oxrdym50NlZ9sUusyO1Z1ZchgRLWRaK6c86XJFClyCgFKetdowBg5bKxyp/u+CDBJG4Mpp0m3HLZl9w== - -ajv-formats@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" - integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== - dependencies: - ajv "^8.0.0" - -ajv-keywords@^3.5.2: - version "3.5.2" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" - integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== - -ajv-keywords@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" - integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== - dependencies: - fast-deep-equal "^3.1.3" - -ajv@^6.12.5: - version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - -ajv@^8.0.0, ajv@^8.9.0: - version "8.17.1" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" - integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== - dependencies: - fast-deep-equal "^3.1.3" - fast-uri "^3.0.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - -ansi-colors@^4.1.3: - version "4.1.3" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" - integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== - -ansi-html-community@^0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41" - integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw== - -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -ansi-styles@^4.0.0, ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -anymatch@~3.1.2: - version "3.1.3" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" - integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - -argparse@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" - integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== - -array-flatten@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" - integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== - -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - -base64id@2.0.0, base64id@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6" - integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog== - -batch@0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" - integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw== - -binary-extensions@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" - integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== - -body-parser@1.20.3, body-parser@^1.19.0: - version "1.20.3" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6" - integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g== - dependencies: - bytes "3.1.2" - content-type "~1.0.5" - debug "2.6.9" - depd "2.0.0" - destroy "1.2.0" - http-errors "2.0.0" - iconv-lite "0.4.24" - on-finished "2.4.1" - qs "6.13.0" - raw-body "2.5.2" - type-is "~1.6.18" - unpipe "1.0.0" - -bonjour-service@^1.0.11: - version "1.2.1" - resolved "https://registry.yarnpkg.com/bonjour-service/-/bonjour-service-1.2.1.tgz#eb41b3085183df3321da1264719fbada12478d02" - integrity sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw== - dependencies: - fast-deep-equal "^3.1.3" - multicast-dns "^7.2.5" - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -brace-expansion@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" - integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== - dependencies: - balanced-match "^1.0.0" - -braces@^3.0.2, braces@^3.0.3, braces@~3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" - integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== - dependencies: - fill-range "^7.1.1" - -browser-stdout@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" - integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== - -browserslist@^4.21.10: - version "4.24.2" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.2.tgz#f5845bc91069dbd55ee89faf9822e1d885d16580" - integrity sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg== - dependencies: - caniuse-lite "^1.0.30001669" - electron-to-chromium "^1.5.41" - node-releases "^2.0.18" - update-browserslist-db "^1.1.1" - -buffer-from@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" - integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== - -bytes@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" - integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw== - -bytes@3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" - integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== - -call-bind@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" - integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== - dependencies: - es-define-property "^1.0.0" - es-errors "^1.3.0" - function-bind "^1.1.2" - get-intrinsic "^1.2.4" - set-function-length "^1.2.1" - -camelcase@^6.0.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" - integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== - -caniuse-lite@^1.0.30001669: - version "1.0.30001671" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001671.tgz#c660a8a0bf6bb8eedaac683d29074e455e84e3f1" - integrity sha512-jocyVaSSfXg2faluE6hrWkMgDOiULBMca4QLtDT39hw1YxaIPHWc1CcTCKkPmHgGH6tKji6ZNbMSmUAvENf2/A== - -chalk@^4.1.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chokidar@^3.5.1, chokidar@^3.5.3: - version "3.6.0" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" - integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== - dependencies: - anymatch "~3.1.2" - braces "~3.0.2" - glob-parent "~5.1.2" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.6.0" - optionalDependencies: - fsevents "~2.3.2" - -chrome-trace-event@^1.0.2: - version "1.0.4" - resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz#05bffd7ff928465093314708c93bdfa9bd1f0f5b" - integrity sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ== - -cliui@^7.0.2: - version "7.0.4" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" - integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^7.0.0" - -clone-deep@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" - integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== - dependencies: - is-plain-object "^2.0.4" - kind-of "^6.0.2" - shallow-clone "^3.0.0" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -colorette@^2.0.10, colorette@^2.0.14: - version "2.0.20" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" - integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== - -commander@^10.0.1: - version "10.0.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" - integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== - -commander@^2.20.0: - version "2.20.3" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" - integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== - -compressible@~2.0.16: - version "2.0.18" - resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" - integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== - dependencies: - mime-db ">= 1.43.0 < 2" - -compression@^1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" - integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== - dependencies: - accepts "~1.3.5" - bytes "3.0.0" - compressible "~2.0.16" - debug "2.6.9" - on-headers "~1.0.2" - safe-buffer "5.1.2" - vary "~1.1.2" - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== - -connect-history-api-fallback@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz#647264845251a0daf25b97ce87834cace0f5f1c8" - integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA== - -connect@^3.7.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8" - integrity sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ== - dependencies: - debug "2.6.9" - finalhandler "1.1.2" - parseurl "~1.3.3" - utils-merge "1.0.1" - -content-disposition@0.5.4: - version "0.5.4" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" - integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== - dependencies: - safe-buffer "5.2.1" - -content-type@~1.0.4, content-type@~1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" - integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== - -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" - integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== - -cookie@0.7.1: - version "0.7.1" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.1.tgz#2f73c42142d5d5cf71310a74fc4ae61670e5dbc9" - integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w== - -cookie@~0.7.2: - version "0.7.2" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7" - integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== - -core-util-is@~1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" - integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== - -cors@~2.8.5: - version "2.8.5" - resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" - integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== - dependencies: - object-assign "^4" - vary "^1" - -cross-spawn@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -custom-event@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425" - integrity sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg== - -date-format@^4.0.14: - version "4.0.14" - resolved "https://registry.yarnpkg.com/date-format/-/date-format-4.0.14.tgz#7a8e584434fb169a521c8b7aa481f355810d9400" - integrity sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg== - -debug@2.6.9: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@^4.1.0, debug@^4.3.4, debug@^4.3.5, debug@~4.3.1, debug@~4.3.2, debug@~4.3.4: - version "4.3.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" - integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== - dependencies: - ms "^2.1.3" - -decamelize@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" - integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== - -default-gateway@^6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-6.0.3.tgz#819494c888053bdb743edbf343d6cdf7f2943a71" - integrity sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg== - dependencies: - execa "^5.0.0" - -define-data-property@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" - integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== - dependencies: - es-define-property "^1.0.0" - es-errors "^1.3.0" - gopd "^1.0.1" - -define-lazy-prop@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" - integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== - -depd@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" - integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== - -depd@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== - -destroy@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" - integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== - -detect-node@^2.0.4: - version "2.1.0" - resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" - integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== - -di@^0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c" - integrity sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA== - -diff@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-5.2.0.tgz#26ded047cd1179b78b9537d5ef725503ce1ae531" - integrity sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A== - -dns-packet@^5.2.2: - version "5.6.1" - resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-5.6.1.tgz#ae888ad425a9d1478a0674256ab866de1012cf2f" - integrity sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw== - dependencies: - "@leichtgewicht/ip-codec" "^2.0.1" - -dom-serialize@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz#562ae8999f44be5ea3076f5419dcd59eb43ac95b" - integrity sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ== - dependencies: - custom-event "~1.0.0" - ent "~2.2.0" - extend "^3.0.0" - void-elements "^2.0.0" - -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== - -electron-to-chromium@^1.5.41: - version "1.5.47" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.47.tgz#ef0751bc19b28be8ee44cd8405309de3bf3b20c7" - integrity sha512-zS5Yer0MOYw4rtK2iq43cJagHZ8sXN0jDHDKzB+86gSBSAI4v07S97mcq+Gs2vclAxSh1j7vOAHxSVgduiiuVQ== - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -encodeurl@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== - -encodeurl@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" - integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== - -engine.io-parser@~5.2.1: - version "5.2.3" - resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.2.3.tgz#00dc5b97b1f233a23c9398d0209504cf5f94d92f" - integrity sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q== - -engine.io@~6.6.0: - version "6.6.2" - resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.6.2.tgz#32bd845b4db708f8c774a4edef4e5c8a98b3da72" - integrity sha512-gmNvsYi9C8iErnZdVcJnvCpSKbWTt1E8+JZo8b+daLninywUWi5NQ5STSHZ9rFjFO7imNcvb8Pc5pe/wMR5xEw== - dependencies: - "@types/cookie" "^0.4.1" - "@types/cors" "^2.8.12" - "@types/node" ">=10.0.0" - accepts "~1.3.4" - base64id "2.0.0" - cookie "~0.7.2" - cors "~2.8.5" - debug "~4.3.1" - engine.io-parser "~5.2.1" - ws "~8.17.1" - -enhanced-resolve@^5.17.0: - version "5.17.1" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15" - integrity sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg== - dependencies: - graceful-fs "^4.2.4" - tapable "^2.2.0" - -ent@~2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.1.tgz#68dc99a002f115792c26239baedaaea9e70c0ca2" - integrity sha512-QHuXVeZx9d+tIQAz/XztU0ZwZf2Agg9CcXcgE1rurqvdBeDBrpSwjl8/6XUqMg7tw2Y7uAdKb2sRv+bSEFqQ5A== - dependencies: - punycode "^1.4.1" - -envinfo@^7.7.3: - version "7.14.0" - resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.14.0.tgz#26dac5db54418f2a4c1159153a0b2ae980838aae" - integrity sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg== - -es-define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" - integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== - dependencies: - get-intrinsic "^1.2.4" - -es-errors@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" - integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== - -es-module-lexer@^1.2.1: - version "1.5.4" - resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.5.4.tgz#a8efec3a3da991e60efa6b633a7cad6ab8d26b78" - integrity sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw== - -escalade@^3.1.1, escalade@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" - integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== - -escape-html@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== - -escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -eslint-scope@5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" - integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== - dependencies: - esrecurse "^4.3.0" - estraverse "^4.1.1" - -esrecurse@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" - integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== - dependencies: - estraverse "^5.2.0" - -estraverse@^4.1.1: - version "4.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== - -estraverse@^5.2.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" - integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== - -etag@~1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" - integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== - -eventemitter3@^4.0.0: - version "4.0.7" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" - integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== - -events@^3.2.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" - integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== - -execa@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" - integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== - dependencies: - cross-spawn "^7.0.3" - get-stream "^6.0.0" - human-signals "^2.1.0" - is-stream "^2.0.0" - merge-stream "^2.0.0" - npm-run-path "^4.0.1" - onetime "^5.1.2" - signal-exit "^3.0.3" - strip-final-newline "^2.0.0" - -express@^4.17.3: - version "4.21.1" - resolved "https://registry.yarnpkg.com/express/-/express-4.21.1.tgz#9dae5dda832f16b4eec941a4e44aa89ec481b281" - integrity sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ== - dependencies: - accepts "~1.3.8" - array-flatten "1.1.1" - body-parser "1.20.3" - content-disposition "0.5.4" - content-type "~1.0.4" - cookie "0.7.1" - cookie-signature "1.0.6" - debug "2.6.9" - depd "2.0.0" - encodeurl "~2.0.0" - escape-html "~1.0.3" - etag "~1.8.1" - finalhandler "1.3.1" - fresh "0.5.2" - http-errors "2.0.0" - merge-descriptors "1.0.3" - methods "~1.1.2" - on-finished "2.4.1" - parseurl "~1.3.3" - path-to-regexp "0.1.10" - proxy-addr "~2.0.7" - qs "6.13.0" - range-parser "~1.2.1" - safe-buffer "5.2.1" - send "0.19.0" - serve-static "1.16.2" - setprototypeof "1.2.0" - statuses "2.0.1" - type-is "~1.6.18" - utils-merge "1.0.1" - vary "~1.1.2" - -extend@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - -fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" - integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== - -fast-json-stable-stringify@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== - -fast-uri@^3.0.1: - version "3.0.3" - resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.3.tgz#892a1c91802d5d7860de728f18608a0573142241" - integrity sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw== - -fastest-levenshtein@^1.0.12: - version "1.0.16" - resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" - integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== - -faye-websocket@^0.11.3: - version "0.11.4" - resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da" - integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== - dependencies: - websocket-driver ">=0.5.1" - -fill-range@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" - integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== - dependencies: - to-regex-range "^5.0.1" - -finalhandler@1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" - integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== - dependencies: - debug "2.6.9" - encodeurl "~1.0.2" - escape-html "~1.0.3" - on-finished "~2.3.0" - parseurl "~1.3.3" - statuses "~1.5.0" - unpipe "~1.0.0" - -finalhandler@1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.3.1.tgz#0c575f1d1d324ddd1da35ad7ece3df7d19088019" - integrity sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ== - dependencies: - debug "2.6.9" - encodeurl "~2.0.0" - escape-html "~1.0.3" - on-finished "2.4.1" - parseurl "~1.3.3" - statuses "2.0.1" - unpipe "~1.0.0" - -find-up@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" - integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== - dependencies: - locate-path "^5.0.0" - path-exists "^4.0.0" - -find-up@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" - integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== - dependencies: - locate-path "^6.0.0" - path-exists "^4.0.0" - -flat@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" - integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== - -flatted@^3.2.7: - version "3.3.1" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" - integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== - -follow-redirects@^1.0.0: - version "1.15.9" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" - integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== - -format-util@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/format-util/-/format-util-1.0.5.tgz#1ffb450c8a03e7bccffe40643180918cc297d271" - integrity sha512-varLbTj0e0yVyRpqQhuWV+8hlePAgaoFRhNFj50BNjEIrw1/DphHSObtqwskVCPWNgzwPoQrZAbfa/SBiicNeg== - -forwarded@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" - integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== - -fresh@0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== - -fs-extra@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" - integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^4.0.0" - universalify "^0.1.0" - -fs-monkey@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.6.tgz#8ead082953e88d992cf3ff844faa907b26756da2" - integrity sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg== - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== - -fsevents@~2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" - integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== - -function-bind@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" - integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== - -get-caller-file@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" - integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== - -get-intrinsic@^1.1.3, get-intrinsic@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" - integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== - dependencies: - es-errors "^1.3.0" - function-bind "^1.1.2" - has-proto "^1.0.1" - has-symbols "^1.0.3" - hasown "^2.0.0" - -get-stream@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" - integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== - -glob-parent@~5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== - dependencies: - is-glob "^4.0.1" - -glob-to-regexp@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" - integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== - -glob@^7.1.3, glob@^7.1.7: - version "7.2.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" - integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.1.1" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" - integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^5.0.1" - once "^1.3.0" - -gopd@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" - integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== - dependencies: - get-intrinsic "^1.1.3" - -graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.10, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.6: - version "4.2.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" - integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== - -handle-thing@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" - integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -has-property-descriptors@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" - integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== - dependencies: - es-define-property "^1.0.0" - -has-proto@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" - integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== - -has-symbols@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" - integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== - -hasown@^2.0.0, hasown@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" - integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== - dependencies: - function-bind "^1.1.2" - -he@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" - integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== - -hpack.js@^2.1.6: - version "2.1.6" - resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" - integrity sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ== - dependencies: - inherits "^2.0.1" - obuf "^1.0.0" - readable-stream "^2.0.1" - wbuf "^1.1.0" - -html-entities@^2.3.2: - version "2.5.2" - resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.5.2.tgz#201a3cf95d3a15be7099521620d19dfb4f65359f" - integrity sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA== - -http-deceiver@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" - integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== - -http-errors@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" - integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== - dependencies: - depd "2.0.0" - inherits "2.0.4" - setprototypeof "1.2.0" - statuses "2.0.1" - toidentifier "1.0.1" - -http-errors@~1.6.2: - version "1.6.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" - integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.0" - statuses ">= 1.4.0 < 2" - -http-parser-js@>=0.5.1: - version "0.5.8" - resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.8.tgz#af23090d9ac4e24573de6f6aecc9d84a48bf20e3" - integrity sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q== - -http-proxy-middleware@^2.0.3: - version "2.0.7" - resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz#915f236d92ae98ef48278a95dedf17e991936ec6" - integrity sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA== - dependencies: - "@types/http-proxy" "^1.17.8" - http-proxy "^1.18.1" - is-glob "^4.0.1" - is-plain-obj "^3.0.0" - micromatch "^4.0.2" - -http-proxy@^1.18.1: - version "1.18.1" - resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" - integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== - dependencies: - eventemitter3 "^4.0.0" - follow-redirects "^1.0.0" - requires-port "^1.0.0" - -human-signals@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" - integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== - -iconv-lite@0.4.24: - version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - -iconv-lite@^0.6.3: - version "0.6.3" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" - integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== - dependencies: - safer-buffer ">= 2.1.2 < 3.0.0" - -import-local@^3.0.2: - version "3.2.0" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.2.0.tgz#c3d5c745798c02a6f8b897726aba5100186ee260" - integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA== - dependencies: - pkg-dir "^4.2.0" - resolve-cwd "^3.0.0" - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== - -interpret@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-3.1.1.tgz#5be0ceed67ca79c6c4bc5cf0d7ee843dcea110c4" - integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== - -ipaddr.js@1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" - integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== - -ipaddr.js@^2.0.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.2.0.tgz#d33fa7bac284f4de7af949638c9d68157c6b92e8" - integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== - -is-binary-path@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== - dependencies: - binary-extensions "^2.0.0" - -is-core-module@^2.13.0: - version "2.15.1" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.15.1.tgz#a7363a25bee942fefab0de13bf6aa372c82dcc37" - integrity sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ== - dependencies: - hasown "^2.0.2" - -is-docker@^2.0.0, is-docker@^2.1.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" - integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== - -is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -is-glob@^4.0.1, is-glob@~4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" - integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== - dependencies: - is-extglob "^2.1.1" - -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - -is-plain-obj@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" - integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== - -is-plain-obj@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" - integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== - -is-plain-object@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" - integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== - dependencies: - isobject "^3.0.1" - -is-stream@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" - integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== - -is-unicode-supported@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" - integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== - -is-wsl@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" - integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== - dependencies: - is-docker "^2.0.0" - -isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== - -isbinaryfile@^4.0.8: - version "4.0.10" - resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.10.tgz#0c5b5e30c2557a2f06febd37b7322946aaee42b3" - integrity sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw== - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== - -isobject@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" - integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== - -jest-worker@^27.4.5: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" - integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== - dependencies: - "@types/node" "*" - merge-stream "^2.0.0" - supports-color "^8.0.0" - -js-yaml@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== - dependencies: - argparse "^2.0.1" - -json-parse-even-better-errors@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" - integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== - -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - -json-schema-traverse@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" - integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== - -jsonfile@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" - integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== - optionalDependencies: - graceful-fs "^4.1.6" - -karma-chrome-launcher@3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-3.2.0.tgz#eb9c95024f2d6dfbb3748d3415ac9b381906b9a9" - integrity sha512-rE9RkUPI7I9mAxByQWkGJFXfFD6lE4gC5nPuZdobf/QdTEJI6EU4yIay/cfU/xV4ZxlM5JiTv7zWYgA64NpS5Q== - dependencies: - which "^1.2.1" - -karma-mocha@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/karma-mocha/-/karma-mocha-2.0.1.tgz#4b0254a18dfee71bdbe6188d9a6861bf86b0cd7d" - integrity sha512-Tzd5HBjm8his2OA4bouAsATYEpZrp9vC7z5E5j4C5Of5Rrs1jY67RAwXNcVmd/Bnk1wgvQRou0zGVLey44G4tQ== - dependencies: - minimist "^1.2.3" - -karma-sourcemap-loader@0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/karma-sourcemap-loader/-/karma-sourcemap-loader-0.4.0.tgz#b01d73f8f688f533bcc8f5d273d43458e13b5488" - integrity sha512-xCRL3/pmhAYF3I6qOrcn0uhbQevitc2DERMPH82FMnG+4WReoGcGFZb1pURf2a5apyrOHRdvD+O6K7NljqKHyA== - dependencies: - graceful-fs "^4.2.10" - -karma-webpack@5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-5.0.1.tgz#4eafd31bbe684a747a6e8f3e4ad373e53979ced4" - integrity sha512-oo38O+P3W2mSPCSUrQdySSPv1LvPpXP+f+bBimNomS5sW+1V4SuhCuW8TfJzV+rDv921w2fDSDw0xJbPe6U+kQ== - dependencies: - glob "^7.1.3" - minimatch "^9.0.3" - webpack-merge "^4.1.5" - -karma@6.4.3: - version "6.4.3" - resolved "https://registry.yarnpkg.com/karma/-/karma-6.4.3.tgz#763e500f99597218bbb536de1a14acc4ceea7ce8" - integrity sha512-LuucC/RE92tJ8mlCwqEoRWXP38UMAqpnq98vktmS9SznSoUPPUJQbc91dHcxcunROvfQjdORVA/YFviH+Xci9Q== - dependencies: - "@colors/colors" "1.5.0" - body-parser "^1.19.0" - braces "^3.0.2" - chokidar "^3.5.1" - connect "^3.7.0" - di "^0.0.1" - dom-serialize "^2.2.1" - glob "^7.1.7" - graceful-fs "^4.2.6" - http-proxy "^1.18.1" - isbinaryfile "^4.0.8" - lodash "^4.17.21" - log4js "^6.4.1" - mime "^2.5.2" - minimatch "^3.0.4" - mkdirp "^0.5.5" - qjobs "^1.2.0" - range-parser "^1.2.1" - rimraf "^3.0.2" - socket.io "^4.7.2" - source-map "^0.6.1" - tmp "^0.2.1" - ua-parser-js "^0.7.30" - yargs "^16.1.1" - -kind-of@^6.0.2: - version "6.0.3" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" - integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== - -launch-editor@^2.6.0: - version "2.9.1" - resolved "https://registry.yarnpkg.com/launch-editor/-/launch-editor-2.9.1.tgz#253f173bd441e342d4344b4dae58291abb425047" - integrity sha512-Gcnl4Bd+hRO9P9icCP/RVVT2o8SFlPXofuCxvA2SaZuH45whSvf5p8x5oih5ftLiVhEI4sp5xDY+R+b3zJBh5w== - dependencies: - picocolors "^1.0.0" - shell-quote "^1.8.1" - -loader-runner@^4.2.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" - integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== - -locate-path@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" - integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== - dependencies: - p-locate "^4.1.0" - -locate-path@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" - integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== - dependencies: - p-locate "^5.0.0" - -lodash@^4.17.15, lodash@^4.17.21: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -log-symbols@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" - integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== - dependencies: - chalk "^4.1.0" - is-unicode-supported "^0.1.0" - -log4js@^6.4.1: - version "6.9.1" - resolved "https://registry.yarnpkg.com/log4js/-/log4js-6.9.1.tgz#aba5a3ff4e7872ae34f8b4c533706753709e38b6" - integrity sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g== - dependencies: - date-format "^4.0.14" - debug "^4.3.4" - flatted "^3.2.7" - rfdc "^1.3.0" - streamroller "^3.1.5" - -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== - -memfs@^3.4.3: - version "3.6.0" - resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.6.0.tgz#d7a2110f86f79dd950a8b6df6d57bc984aa185f6" - integrity sha512-EGowvkkgbMcIChjMTMkESFDbZeSh8xZ7kNSF0hAiAN4Jh6jgHCRS0Ga/+C8y6Au+oqpezRHCfPsmJ2+DwAgiwQ== - dependencies: - fs-monkey "^1.0.4" - -merge-descriptors@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" - integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== - -merge-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" - integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== - -methods@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== - -micromatch@^4.0.2: - version "4.0.8" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" - integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== - dependencies: - braces "^3.0.3" - picomatch "^2.3.1" - -mime-db@1.52.0: - version "1.52.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" - integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== - -"mime-db@>= 1.43.0 < 2": - version "1.53.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.53.0.tgz#3cb63cd820fc29896d9d4e8c32ab4fcd74ccb447" - integrity sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg== - -mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: - version "2.1.35" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" - integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== - dependencies: - mime-db "1.52.0" - -mime@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" - integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== - -mime@^2.5.2: - version "2.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" - integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== - -mimic-fn@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" - integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== - -minimalistic-assert@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" - integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== - -minimatch@^3.0.4, minimatch@^3.1.1: - version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== - dependencies: - brace-expansion "^1.1.7" - -minimatch@^5.0.1, minimatch@^5.1.6: - version "5.1.6" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" - integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== - dependencies: - brace-expansion "^2.0.1" - -minimatch@^9.0.3: - version "9.0.5" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" - integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== - dependencies: - brace-expansion "^2.0.1" - -minimist@^1.2.3, minimist@^1.2.6: - version "1.2.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" - integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== - -mkdirp@^0.5.5: - version "0.5.6" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" - integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== - dependencies: - minimist "^1.2.6" - -mocha@10.7.0: - version "10.7.0" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.7.0.tgz#9e5cbed8fa9b37537a25bd1f7fb4f6fc45458b9a" - integrity sha512-v8/rBWr2VO5YkspYINnvu81inSz2y3ODJrhO175/Exzor1RcEZZkizgE2A+w/CAXXoESS8Kys5E62dOHGHzULA== - dependencies: - ansi-colors "^4.1.3" - browser-stdout "^1.3.1" - chokidar "^3.5.3" - debug "^4.3.5" - diff "^5.2.0" - escape-string-regexp "^4.0.0" - find-up "^5.0.0" - glob "^8.1.0" - he "^1.2.0" - js-yaml "^4.1.0" - log-symbols "^4.1.0" - minimatch "^5.1.6" - ms "^2.1.3" - serialize-javascript "^6.0.2" - strip-json-comments "^3.1.1" - supports-color "^8.1.1" - workerpool "^6.5.1" - yargs "^16.2.0" - yargs-parser "^20.2.9" - yargs-unparser "^2.0.0" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== - -ms@2.1.3, ms@^2.1.3: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - -multicast-dns@^7.2.5: - version "7.2.5" - resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-7.2.5.tgz#77eb46057f4d7adbd16d9290fa7299f6fa64cced" - integrity sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg== - dependencies: - dns-packet "^5.2.2" - thunky "^1.0.2" - -negotiator@0.6.3: - version "0.6.3" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" - integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== - -neo-async@^2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" - integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== - -node-forge@^1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" - integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== - -node-releases@^2.0.18: - version "2.0.18" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f" - integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== - -normalize-path@^3.0.0, normalize-path@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== - -npm-run-path@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" - integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== - dependencies: - path-key "^3.0.0" - -object-assign@^4: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== - -object-inspect@^1.13.1: - version "1.13.2" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff" - integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== - -obuf@^1.0.0, obuf@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" - integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== - -on-finished@2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" - integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== - dependencies: - ee-first "1.1.1" - -on-finished@~2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" - integrity sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww== - dependencies: - ee-first "1.1.1" - -on-headers@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" - integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== - -once@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== - dependencies: - wrappy "1" - -onetime@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" - integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== - dependencies: - mimic-fn "^2.1.0" - -open@^8.0.9: - version "8.4.2" - resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" - integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== - dependencies: - define-lazy-prop "^2.0.0" - is-docker "^2.1.1" - is-wsl "^2.2.0" - -p-limit@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== - dependencies: - p-try "^2.0.0" - -p-limit@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== - dependencies: - yocto-queue "^0.1.0" - -p-locate@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" - integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== - dependencies: - p-limit "^2.2.0" - -p-locate@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" - integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== - dependencies: - p-limit "^3.0.2" - -p-retry@^4.5.0: - version "4.6.2" - resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.2.tgz#9baae7184057edd4e17231cee04264106e092a16" - integrity sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ== - dependencies: - "@types/retry" "0.12.0" - retry "^0.13.1" - -p-try@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" - integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== - -parseurl@~1.3.2, parseurl@~1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" - integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== - -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== - -path-key@^3.0.0, path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - -path-parse@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" - integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== - -path-to-regexp@0.1.10: - version "0.1.10" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.10.tgz#67e9108c5c0551b9e5326064387de4763c4d5f8b" - integrity sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w== - -picocolors@^1.0.0, picocolors@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" - integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== - -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" - integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== - -pkg-dir@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" - integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== - dependencies: - find-up "^4.0.0" - -process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== - -proxy-addr@~2.0.7: - version "2.0.7" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" - integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== - dependencies: - forwarded "0.2.0" - ipaddr.js "1.9.1" - -punycode@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== - -punycode@^2.1.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" - integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== - -qjobs@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.2.0.tgz#c45e9c61800bd087ef88d7e256423bdd49e5d071" - integrity sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg== - -qs@6.13.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" - integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== - dependencies: - side-channel "^1.0.6" - -randombytes@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" - integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== - dependencies: - safe-buffer "^5.1.0" - -range-parser@^1.2.1, range-parser@~1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" - integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== - -raw-body@2.5.2: - version "2.5.2" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" - integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== - dependencies: - bytes "3.1.2" - http-errors "2.0.0" - iconv-lite "0.4.24" - unpipe "1.0.0" - -readable-stream@^2.0.1: - version "2.3.8" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" - integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -readable-stream@^3.0.6: - version "3.6.2" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" - integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -readdirp@~3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" - integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== - dependencies: - picomatch "^2.2.1" - -rechoir@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22" - integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ== - dependencies: - resolve "^1.20.0" - -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== - -require-from-string@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" - integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== - -requires-port@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" - integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== - -resolve-cwd@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" - integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== - dependencies: - resolve-from "^5.0.0" - -resolve-from@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" - integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== - -resolve@^1.20.0: - version "1.22.8" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" - integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== - dependencies: - is-core-module "^2.13.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - -retry@^0.13.1: - version "0.13.1" - resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" - integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== - -rfdc@^1.3.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" - integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== - -rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - -safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -schema-utils@^3.1.1, schema-utils@^3.2.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" - integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== - dependencies: - "@types/json-schema" "^7.0.8" - ajv "^6.12.5" - ajv-keywords "^3.5.2" - -schema-utils@^4.0.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.2.0.tgz#70d7c93e153a273a805801882ebd3bff20d89c8b" - integrity sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw== - dependencies: - "@types/json-schema" "^7.0.9" - ajv "^8.9.0" - ajv-formats "^2.1.1" - ajv-keywords "^5.1.0" - -select-hose@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" - integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg== - -selfsigned@^2.1.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.4.1.tgz#560d90565442a3ed35b674034cec4e95dceb4ae0" - integrity sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q== - dependencies: - "@types/node-forge" "^1.3.0" - node-forge "^1" - -send@0.19.0: - version "0.19.0" - resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8" - integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw== - dependencies: - debug "2.6.9" - depd "2.0.0" - destroy "1.2.0" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "2.0.0" - mime "1.6.0" - ms "2.1.3" - on-finished "2.4.1" - range-parser "~1.2.1" - statuses "2.0.1" - -serialize-javascript@^6.0.1, serialize-javascript@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" - integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== - dependencies: - randombytes "^2.1.0" - -serve-index@^1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" - integrity sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw== - dependencies: - accepts "~1.3.4" - batch "0.6.1" - debug "2.6.9" - escape-html "~1.0.3" - http-errors "~1.6.2" - mime-types "~2.1.17" - parseurl "~1.3.2" - -serve-static@1.16.2: - version "1.16.2" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.2.tgz#b6a5343da47f6bdd2673848bf45754941e803296" - integrity sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw== - dependencies: - encodeurl "~2.0.0" - escape-html "~1.0.3" - parseurl "~1.3.3" - send "0.19.0" - -set-function-length@^1.2.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" - integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== - dependencies: - define-data-property "^1.1.4" - es-errors "^1.3.0" - function-bind "^1.1.2" - get-intrinsic "^1.2.4" - gopd "^1.0.1" - has-property-descriptors "^1.0.2" - -setprototypeof@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" - integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== - -setprototypeof@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" - integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== - -shallow-clone@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" - integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== - dependencies: - kind-of "^6.0.2" - -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - -shell-quote@^1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680" - integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== - -side-channel@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" - integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== - dependencies: - call-bind "^1.0.7" - es-errors "^1.3.0" - get-intrinsic "^1.2.4" - object-inspect "^1.13.1" - -signal-exit@^3.0.3: - version "3.0.7" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" - integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== - -socket.io-adapter@~2.5.2: - version "2.5.5" - resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz#c7a1f9c703d7756844751b6ff9abfc1780664082" - integrity sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg== - dependencies: - debug "~4.3.4" - ws "~8.17.1" - -socket.io-parser@~4.2.4: - version "4.2.4" - resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.4.tgz#c806966cf7270601e47469ddeec30fbdfda44c83" - integrity sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew== - dependencies: - "@socket.io/component-emitter" "~3.1.0" - debug "~4.3.1" - -socket.io@^4.7.2: - version "4.8.1" - resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.8.1.tgz#fa0eaff965cc97fdf4245e8d4794618459f7558a" - integrity sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg== - dependencies: - accepts "~1.3.4" - base64id "~2.0.0" - cors "~2.8.5" - debug "~4.3.2" - engine.io "~6.6.0" - socket.io-adapter "~2.5.2" - socket.io-parser "~4.2.4" - -sockjs@^0.3.24: - version "0.3.24" - resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce" - integrity sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ== - dependencies: - faye-websocket "^0.11.3" - uuid "^8.3.2" - websocket-driver "^0.7.4" - -source-map-js@^1.0.2: - version "1.2.1" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" - integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== - -source-map-loader@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-5.0.0.tgz#f593a916e1cc54471cfc8851b905c8a845fc7e38" - integrity sha512-k2Dur7CbSLcAH73sBcIkV5xjPV4SzqO1NJ7+XaQl8if3VODDUj3FNchNGpqgJSKbvUfJuhVdv8K2Eu8/TNl2eA== - dependencies: - iconv-lite "^0.6.3" - source-map-js "^1.0.2" - -source-map-support@~0.5.20: - version "0.5.21" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" - integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map@^0.6.0, source-map@^0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -spdy-transport@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" - integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== - dependencies: - debug "^4.1.0" - detect-node "^2.0.4" - hpack.js "^2.1.6" - obuf "^1.1.2" - readable-stream "^3.0.6" - wbuf "^1.7.3" - -spdy@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.2.tgz#b74f466203a3eda452c02492b91fb9e84a27677b" - integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA== - dependencies: - debug "^4.1.0" - handle-thing "^2.0.0" - http-deceiver "^1.2.7" - select-hose "^2.0.0" - spdy-transport "^3.0.0" - -statuses@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" - integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== - -"statuses@>= 1.4.0 < 2", statuses@~1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" - integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== - -streamroller@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-3.1.5.tgz#1263182329a45def1ffaef58d31b15d13d2ee7ff" - integrity sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw== - dependencies: - date-format "^4.0.14" - debug "^4.3.4" - fs-extra "^8.1.0" - -string-width@^4.1.0, string-width@^4.2.0: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-final-newline@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" - integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== - -strip-json-comments@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" - integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== - -supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - -supports-color@^8.0.0, supports-color@^8.1.1: - version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== - dependencies: - has-flag "^4.0.0" - -supports-preserve-symlinks-flag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" - integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== - -tapable@^2.1.1, tapable@^2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" - integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== - -terser-webpack-plugin@^5.3.10: - version "5.3.10" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz#904f4c9193c6fd2a03f693a2150c62a92f40d199" - integrity sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w== - dependencies: - "@jridgewell/trace-mapping" "^0.3.20" - jest-worker "^27.4.5" - schema-utils "^3.1.1" - serialize-javascript "^6.0.1" - terser "^5.26.0" - -terser@^5.26.0: - version "5.36.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.36.0.tgz#8b0dbed459ac40ff7b4c9fd5a3a2029de105180e" - integrity sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w== - dependencies: - "@jridgewell/source-map" "^0.3.3" - acorn "^8.8.2" - commander "^2.20.0" - source-map-support "~0.5.20" - -thunky@^1.0.2: - version "1.1.0" - resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" - integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== - -tmp@^0.2.1: - version "0.2.3" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.3.tgz#eb783cc22bc1e8bebd0671476d46ea4eb32a79ae" - integrity sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w== - -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" - -toidentifier@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" - integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== - -type-is@~1.6.18: - version "1.6.18" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" - integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== - dependencies: - media-typer "0.3.0" - mime-types "~2.1.24" - -typescript@5.5.4: - version "5.5.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.4.tgz#d9852d6c82bad2d2eda4fd74a5762a8f5909e9ba" - integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q== - -ua-parser-js@^0.7.30: - version "0.7.39" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.39.tgz#c71efb46ebeabc461c4612d22d54f88880fabe7e" - integrity sha512-IZ6acm6RhQHNibSt7+c09hhvsKy9WUr4DVbeq9U8o71qxyYtJpQeDxQnMrVqnIFMLcQjHO0I9wgfO2vIahht4w== - -undici-types@~6.19.8: - version "6.19.8" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" - integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== - -universalify@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" - integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== - -unpipe@1.0.0, unpipe@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" - integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== - -update-browserslist-db@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz#80846fba1d79e82547fb661f8d141e0945755fe5" - integrity sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A== - dependencies: - escalade "^3.2.0" - picocolors "^1.1.0" - -uri-js@^4.2.2: - version "4.4.1" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" - integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== - dependencies: - punycode "^2.1.0" - -util-deprecate@^1.0.1, util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== - -utils-merge@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" - integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== - -uuid@^8.3.2: - version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - -vary@^1, vary@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== - -void-elements@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" - integrity sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung== - -watchpack@^2.4.1: - version "2.4.2" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.2.tgz#2feeaed67412e7c33184e5a79ca738fbd38564da" - integrity sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw== - dependencies: - glob-to-regexp "^0.4.1" - graceful-fs "^4.1.2" - -wbuf@^1.1.0, wbuf@^1.7.3: - version "1.7.3" - resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" - integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== - dependencies: - minimalistic-assert "^1.0.0" - -webpack-cli@5.1.4: - version "5.1.4" - resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-5.1.4.tgz#c8e046ba7eaae4911d7e71e2b25b776fcc35759b" - integrity sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg== - dependencies: - "@discoveryjs/json-ext" "^0.5.0" - "@webpack-cli/configtest" "^2.1.1" - "@webpack-cli/info" "^2.0.2" - "@webpack-cli/serve" "^2.0.5" - colorette "^2.0.14" - commander "^10.0.1" - cross-spawn "^7.0.3" - envinfo "^7.7.3" - fastest-levenshtein "^1.0.12" - import-local "^3.0.2" - interpret "^3.1.1" - rechoir "^0.8.0" - webpack-merge "^5.7.3" - -webpack-dev-middleware@^5.3.4: - version "5.3.4" - resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz#eb7b39281cbce10e104eb2b8bf2b63fce49a3517" - integrity sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q== - dependencies: - colorette "^2.0.10" - memfs "^3.4.3" - mime-types "^2.1.31" - range-parser "^1.2.1" - schema-utils "^4.0.0" - -webpack-dev-server@4.15.2: - version "4.15.2" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz#9e0c70a42a012560860adb186986da1248333173" - integrity sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g== - dependencies: - "@types/bonjour" "^3.5.9" - "@types/connect-history-api-fallback" "^1.3.5" - "@types/express" "^4.17.13" - "@types/serve-index" "^1.9.1" - "@types/serve-static" "^1.13.10" - "@types/sockjs" "^0.3.33" - "@types/ws" "^8.5.5" - ansi-html-community "^0.0.8" - bonjour-service "^1.0.11" - chokidar "^3.5.3" - colorette "^2.0.10" - compression "^1.7.4" - connect-history-api-fallback "^2.0.0" - default-gateway "^6.0.3" - express "^4.17.3" - graceful-fs "^4.2.6" - html-entities "^2.3.2" - http-proxy-middleware "^2.0.3" - ipaddr.js "^2.0.1" - launch-editor "^2.6.0" - open "^8.0.9" - p-retry "^4.5.0" - rimraf "^3.0.2" - schema-utils "^4.0.0" - selfsigned "^2.1.1" - serve-index "^1.9.1" - sockjs "^0.3.24" - spdy "^4.0.2" - webpack-dev-middleware "^5.3.4" - ws "^8.13.0" - -webpack-merge@^4.1.5: - version "4.2.2" - resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.2.tgz#a27c52ea783d1398afd2087f547d7b9d2f43634d" - integrity sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g== - dependencies: - lodash "^4.17.15" - -webpack-merge@^5.7.3: - version "5.10.0" - resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.10.0.tgz#a3ad5d773241e9c682803abf628d4cd62b8a4177" - integrity sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA== - dependencies: - clone-deep "^4.0.1" - flat "^5.0.2" - wildcard "^2.0.0" - -webpack-sources@^3.2.3: - version "3.2.3" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" - integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== - -webpack@5.93.0: - version "5.93.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.93.0.tgz#2e89ec7035579bdfba9760d26c63ac5c3462a5e5" - integrity sha512-Y0m5oEY1LRuwly578VqluorkXbvXKh7U3rLoQCEO04M97ScRr44afGVkI0FQFsXzysk5OgFAxjZAb9rsGQVihA== - dependencies: - "@types/eslint-scope" "^3.7.3" - "@types/estree" "^1.0.5" - "@webassemblyjs/ast" "^1.12.1" - "@webassemblyjs/wasm-edit" "^1.12.1" - "@webassemblyjs/wasm-parser" "^1.12.1" - acorn "^8.7.1" - acorn-import-attributes "^1.9.5" - browserslist "^4.21.10" - chrome-trace-event "^1.0.2" - enhanced-resolve "^5.17.0" - es-module-lexer "^1.2.1" - eslint-scope "5.1.1" - events "^3.2.0" - glob-to-regexp "^0.4.1" - graceful-fs "^4.2.11" - json-parse-even-better-errors "^2.3.1" - loader-runner "^4.2.0" - mime-types "^2.1.27" - neo-async "^2.6.2" - schema-utils "^3.2.0" - tapable "^2.1.1" - terser-webpack-plugin "^5.3.10" - watchpack "^2.4.1" - webpack-sources "^3.2.3" - -websocket-driver@>=0.5.1, websocket-driver@^0.7.4: - version "0.7.4" - resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" - integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== - dependencies: - http-parser-js ">=0.5.1" - safe-buffer ">=5.1.0" - websocket-extensions ">=0.1.1" - -websocket-extensions@>=0.1.1: - version "0.1.4" - resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" - integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== - -which@^1.2.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== - dependencies: - isexe "^2.0.0" - -which@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - -wildcard@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" - integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== - -workerpool@^6.5.1: - version "6.5.1" - resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544" - integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA== - -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== - -ws@8.5.0: - version "8.5.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f" - integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg== - -ws@^8.13.0: - version "8.18.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" - integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== - -ws@~8.17.1: - version "8.17.1" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b" - integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== - -y18n@^5.0.5: - version "5.0.8" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" - integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== - -yargs-parser@^20.2.2, yargs-parser@^20.2.9: - version "20.2.9" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" - integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== - -yargs-unparser@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" - integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== - dependencies: - camelcase "^6.0.0" - decamelize "^4.0.0" - flat "^5.0.2" - is-plain-obj "^2.1.0" - -yargs@^16.1.1, yargs@^16.2.0: - version "16.2.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" - integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== - dependencies: - cliui "^7.0.2" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.0" - y18n "^5.0.5" - yargs-parser "^20.2.2" - -yocto-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git a/library/coroutines/src/commonMain/kotlin/io/github/taetae98coding/diary/library/coroutines/FlowExt.kt b/library/coroutines/src/commonMain/kotlin/io/github/taetae98coding/diary/library/coroutines/FlowExt.kt index 73e8fee8..72189f87 100644 --- a/library/coroutines/src/commonMain/kotlin/io/github/taetae98coding/diary/library/coroutines/FlowExt.kt +++ b/library/coroutines/src/commonMain/kotlin/io/github/taetae98coding/diary/library/coroutines/FlowExt.kt @@ -1,10 +1,12 @@ package io.github.taetae98coding.diary.library.coroutines +import kotlin.jvm.JvmName import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.mapLatest +@JvmName("mapCollectionLatest") @OptIn(ExperimentalCoroutinesApi::class) public fun Flow>.mapCollectionLatest( transform: suspend (T) -> R, @@ -14,6 +16,16 @@ public fun Flow>.mapCollectionLatest( } } +@JvmName("mapNullableCollectionLatest") +@OptIn(ExperimentalCoroutinesApi::class) +public fun Flow?>.mapCollectionLatest( + transform: suspend (T) -> R, +): Flow?> { + return mapLatest { collection -> + collection?.map { transform(it) } + } +} + @OptIn(ExperimentalCoroutinesApi::class) public fun Flow>.filterCollectionLatest( predicate: suspend (T) -> Boolean diff --git a/library/firebase-messaging/build.gradle.kts b/library/firebase-messaging/build.gradle.kts index daad9238..3eead0b9 100644 --- a/library/firebase-messaging/build.gradle.kts +++ b/library/firebase-messaging/build.gradle.kts @@ -39,7 +39,6 @@ kotlin { nonSupportMain.dependsOn(commonMain.get()) jvmMain.get().dependsOn(nonSupportMain) - wasmJsMain.get().dependsOn(nonSupportMain) } } diff --git a/library/room/src/commonMain/kotlin/io/github/taetae98coding/diary/library/room/dao/EntityDao.kt b/library/room/src/commonMain/kotlin/io/github/taetae98coding/diary/library/room/dao/EntityDao.kt new file mode 100644 index 00000000..7eb899db --- /dev/null +++ b/library/room/src/commonMain/kotlin/io/github/taetae98coding/diary/library/room/dao/EntityDao.kt @@ -0,0 +1,19 @@ +package io.github.taetae98coding.diary.library.room.dao + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Transaction +import androidx.room.Upsert + +@Dao +public abstract class EntityDao { + @Upsert + public abstract suspend fun upsert(entity: T) + + @Transaction + @Upsert + public abstract suspend fun upsert(entityList: List) + + @Delete + public abstract suspend fun delete(entity: T) +} diff --git a/server/app/build.gradle.kts b/server/app/build.gradle.kts index 7876ac9e..34d8cf18 100644 --- a/server/app/build.gradle.kts +++ b/server/app/build.gradle.kts @@ -13,15 +13,18 @@ dependencies { implementation(project(":server:data:account")) implementation(project(":server:data:memo")) + implementation(project(":server:data:tag")) implementation(project(":server:data:fcm")) implementation(project(":server:domain:account")) implementation(project(":server:domain:memo")) + implementation(project(":server:domain:tag")) implementation(project(":server:domain:fcm")) implementation(project(":server:feature:home")) implementation(project(":server:feature:account")) implementation(project(":server:feature:memo")) + implementation(project(":server:feature:tag")) implementation(project(":server:feature:fcm")) implementation(project(":common:model")) diff --git a/server/app/dependencies/runtimeClasspath.txt b/server/app/dependencies/runtimeClasspath.txt index b0f2bb2d..f89e6851 100644 --- a/server/app/dependencies/runtimeClasspath.txt +++ b/server/app/dependencies/runtimeClasspath.txt @@ -79,13 +79,13 @@ io.grpc:grpc-services:1.68.0 io.grpc:grpc-stub:1.68.0 io.grpc:grpc-util:1.68.0 io.grpc:grpc-xds:1.68.0 -io.insert-koin:koin-annotations-bom:2.0.0-Beta1 -io.insert-koin:koin-annotations-jvm:2.0.0-Beta1 -io.insert-koin:koin-annotations:2.0.0-Beta1 -io.insert-koin:koin-bom:4.0.0 -io.insert-koin:koin-core-jvm:4.0.0 -io.insert-koin:koin-core:4.0.0 -io.insert-koin:koin-ktor:4.0.0 +io.insert-koin:koin-annotations-bom:2.0.0-Beta2 +io.insert-koin:koin-annotations-jvm:2.0.0-Beta2 +io.insert-koin:koin-annotations:2.0.0-Beta2 +io.insert-koin:koin-bom:4.1.0-Beta1 +io.insert-koin:koin-core-jvm:4.1.0-Beta1 +io.insert-koin:koin-core:4.1.0-Beta1 +io.insert-koin:koin-ktor:4.1.0-Beta1 io.ktor:ktor-bom:3.0.1 io.ktor:ktor-client-core-jvm:3.0.1 io.ktor:ktor-client-core:3.0.1 diff --git a/server/app/src/main/kotlin/io/github/taetae98coding/diary/plugin/DatabasePlugin.kt b/server/app/src/main/kotlin/io/github/taetae98coding/diary/plugin/DatabasePlugin.kt index 1dff6c0b..06f5b0f1 100644 --- a/server/app/src/main/kotlin/io/github/taetae98coding/diary/plugin/DatabasePlugin.kt +++ b/server/app/src/main/kotlin/io/github/taetae98coding/diary/plugin/DatabasePlugin.kt @@ -3,6 +3,8 @@ package io.github.taetae98coding.diary.plugin import io.github.taetae98coding.diary.core.database.AccountTable import io.github.taetae98coding.diary.core.database.FCMTokenTable import io.github.taetae98coding.diary.core.database.MemoTable +import io.github.taetae98coding.diary.core.database.MemoTagTable +import io.github.taetae98coding.diary.core.database.TagTable import io.ktor.server.application.Application import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.SchemaUtils @@ -18,6 +20,12 @@ internal fun Application.installDatabase() { ) transaction(database) { - SchemaUtils.createMissingTablesAndColumns(AccountTable, MemoTable, FCMTokenTable) + SchemaUtils.createMissingTablesAndColumns( + AccountTable, + TagTable, + MemoTable, + MemoTagTable, + FCMTokenTable, + ) } } diff --git a/server/app/src/main/kotlin/io/github/taetae98coding/diary/plugin/KoinPlugin.kt b/server/app/src/main/kotlin/io/github/taetae98coding/diary/plugin/KoinPlugin.kt index a2ef0776..5c0dd715 100644 --- a/server/app/src/main/kotlin/io/github/taetae98coding/diary/plugin/KoinPlugin.kt +++ b/server/app/src/main/kotlin/io/github/taetae98coding/diary/plugin/KoinPlugin.kt @@ -3,9 +3,11 @@ package io.github.taetae98coding.diary.plugin import io.github.taetae98coding.diary.data.account.AccountDataModule import io.github.taetae98coding.diary.data.fcm.FCMDataModule import io.github.taetae98coding.diary.data.memo.MemoDataModule +import io.github.taetae98coding.diary.data.tag.TagDataModule import io.github.taetae98coding.diary.domain.account.AccountDomainModule import io.github.taetae98coding.diary.domain.fcm.FCMDomainModule import io.github.taetae98coding.diary.domain.memo.MemoDomainModule +import io.github.taetae98coding.diary.domain.tag.TagDomainModule import io.ktor.server.application.Application import io.ktor.server.application.install import org.koin.ksp.generated.module @@ -16,9 +18,11 @@ internal fun Application.installKoin() { modules( AccountDataModule().module, MemoDataModule().module, + TagDataModule().module, FCMDataModule().module, AccountDomainModule().module, MemoDomainModule().module, + TagDomainModule().module, FCMDomainModule().module, ) } diff --git a/server/app/src/main/kotlin/io/github/taetae98coding/diary/plugin/RoutingPlugin.kt b/server/app/src/main/kotlin/io/github/taetae98coding/diary/plugin/RoutingPlugin.kt index 30bfa969..1b26a381 100644 --- a/server/app/src/main/kotlin/io/github/taetae98coding/diary/plugin/RoutingPlugin.kt +++ b/server/app/src/main/kotlin/io/github/taetae98coding/diary/plugin/RoutingPlugin.kt @@ -4,6 +4,7 @@ import io.github.taetae98coding.diary.feature.account.accountRouting import io.github.taetae98coding.diary.feature.fcm.fcmRouting import io.github.taetae98coding.diary.feature.home.homeRouting import io.github.taetae98coding.diary.feature.memo.memoRouting +import io.github.taetae98coding.diary.feature.tag.tagRouting import io.ktor.server.application.Application import io.ktor.server.routing.routing @@ -12,6 +13,7 @@ internal fun Application.installRouting() { homeRouting() accountRouting() memoRouting() + tagRouting() fcmRouting() } } diff --git a/server/core/database/src/main/kotlin/io/github/taetae98coding/diary/core/database/AccountTable.kt b/server/core/database/src/main/kotlin/io/github/taetae98coding/diary/core/database/AccountTable.kt index 9c01a70a..f5ea4abd 100644 --- a/server/core/database/src/main/kotlin/io/github/taetae98coding/diary/core/database/AccountTable.kt +++ b/server/core/database/src/main/kotlin/io/github/taetae98coding/diary/core/database/AccountTable.kt @@ -6,7 +6,6 @@ import org.jetbrains.exposed.sql.Table import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.insert import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction public data object AccountTable : Table(name = "Account") { private val EMAIL = varchar("email", 255).default("") @@ -16,30 +15,24 @@ public data object AccountTable : Table(name = "Account") { override val primaryKey: PrimaryKey = PrimaryKey(EMAIL) - public suspend fun contains(email: String): Boolean = - newSuspendedTransaction { - selectAll() - .where { EMAIL eq email } - .any() - } + public fun contains(email: String): Boolean = + selectAll() + .where { EMAIL eq email } + .any() - public suspend fun insert(account: Account) { - newSuspendedTransaction { - insert { - it[EMAIL] = account.email - it[PASSWORD] = account.password - it[UID] = account.uid - } + public fun insert(account: Account) { + insert { + it[EMAIL] = account.email + it[PASSWORD] = account.password + it[UID] = account.uid } } - public suspend fun findByEmail(email: String, password: String): Account? = - newSuspendedTransaction { - selectAll() - .where { (EMAIL eq email) and (PASSWORD eq password) } - .firstOrNull() - ?.toAccount() - } + public fun findByEmail(email: String, password: String): Account? = + selectAll() + .where { (EMAIL eq email) and (PASSWORD eq password) } + .firstOrNull() + ?.toAccount() private fun ResultRow.toAccount(): Account = Account( diff --git a/server/core/database/src/main/kotlin/io/github/taetae98coding/diary/core/database/FCMTokenTable.kt b/server/core/database/src/main/kotlin/io/github/taetae98coding/diary/core/database/FCMTokenTable.kt index 77d9fb79..8570f609 100644 --- a/server/core/database/src/main/kotlin/io/github/taetae98coding/diary/core/database/FCMTokenTable.kt +++ b/server/core/database/src/main/kotlin/io/github/taetae98coding/diary/core/database/FCMTokenTable.kt @@ -6,7 +6,6 @@ import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.Table import org.jetbrains.exposed.sql.deleteWhere import org.jetbrains.exposed.sql.kotlin.datetime.timestamp -import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction import org.jetbrains.exposed.sql.upsert public data object FCMTokenTable : Table(name = "FCMToken") { @@ -23,19 +22,15 @@ public data object FCMTokenTable : Table(name = "FCMToken") { override val primaryKey: PrimaryKey = PrimaryKey(TOKEN) - public suspend fun upsert(token: String, owner: String) { - newSuspendedTransaction { - upsert { - it[TOKEN] = token - it[OWNER] = owner - it[UPDATE_AT] = Clock.System.now() - } + public fun upsert(token: String, owner: String) { + upsert { + it[TOKEN] = token + it[OWNER] = owner + it[UPDATE_AT] = Clock.System.now() } } - public suspend fun delete(token: String) { - newSuspendedTransaction { - deleteWhere { TOKEN eq token } - } + public fun delete(token: String) { + deleteWhere { TOKEN eq token } } } diff --git a/server/core/database/src/main/kotlin/io/github/taetae98coding/diary/core/database/MemoTable.kt b/server/core/database/src/main/kotlin/io/github/taetae98coding/diary/core/database/MemoTable.kt index e6b4e907..1f6634e8 100644 --- a/server/core/database/src/main/kotlin/io/github/taetae98coding/diary/core/database/MemoTable.kt +++ b/server/core/database/src/main/kotlin/io/github/taetae98coding/diary/core/database/MemoTable.kt @@ -10,11 +10,10 @@ import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.kotlin.datetime.date import org.jetbrains.exposed.sql.kotlin.datetime.timestamp import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction import org.jetbrains.exposed.sql.upsert public data object MemoTable : Table(name = "Memo") { - private val ID = varchar("id", 255).default("") + internal val ID = varchar("id", 255).default("") private val TITLE = varchar("title", 255).default("") private val DESCRIPTION = text("description") private val START = date("start").nullable().default(null) @@ -27,45 +26,45 @@ public data object MemoTable : Table(name = "Memo") { onDelete = ReferenceOption.CASCADE, onUpdate = ReferenceOption.CASCADE, ) + private val PRIMARY_TAG = + reference( + name = "primaryTag", + refColumn = TagTable.ID, + onDelete = ReferenceOption.CASCADE, + onUpdate = ReferenceOption.CASCADE, + ).nullable() private val IS_FINISH = bool("isFinish").default(false) private val IS_DELETE = bool("isDelete").default(false) private val UPDATE_AT = timestamp("updateAt").default(Clock.System.now()) override val primaryKey: PrimaryKey = PrimaryKey(ID) - public suspend fun upsert(list: List) { - newSuspendedTransaction { - list.forEach { memo -> - upsert { - it[ID] = memo.id - it[TITLE] = memo.title - it[DESCRIPTION] = memo.description - it[START] = memo.start - it[END_INCLUSIVE] = memo.endInclusive - it[COLOR] = memo.color - it[OWNER] = memo.owner - it[IS_FINISH] = memo.isFinish - it[IS_DELETE] = memo.isDelete - it[UPDATE_AT] = memo.updateAt - } - } + public fun upsert(memo: Memo) { + upsert { + it[ID] = memo.id + it[TITLE] = memo.title + it[DESCRIPTION] = memo.description + it[START] = memo.start + it[END_INCLUSIVE] = memo.endInclusive + it[COLOR] = memo.color + it[OWNER] = memo.owner + it[PRIMARY_TAG] = memo.primaryTag + it[IS_FINISH] = memo.isFinish + it[IS_DELETE] = memo.isDelete + it[UPDATE_AT] = memo.updateAt } } - public suspend fun findByIds(list: List): List = - newSuspendedTransaction { - selectAll() - .where { ID.inList(list) } - .map { it.toMemo() } - } + public fun findByIds(ids: Set): List = + selectAll() + .where { ID.inList(ids) } + .map { it.toMemo() } - public suspend fun findByUpdateAt(uid: String, updateAt: Instant): List = - newSuspendedTransaction { - selectAll() - .where { (OWNER eq uid) and (UPDATE_AT greater updateAt) } - .limit(50) - .map { it.toMemo() } - } + public fun findByUpdateAt(uid: String, updateAt: Instant): List = + selectAll() + .where { (OWNER eq uid) and (UPDATE_AT greater updateAt) } + .limit(50) + .map { it.toMemo() } private fun ResultRow.toMemo(): Memo = Memo( @@ -76,6 +75,7 @@ public data object MemoTable : Table(name = "Memo") { endInclusive = get(END_INCLUSIVE), color = get(COLOR), owner = get(OWNER), + primaryTag = get(PRIMARY_TAG), isFinish = get(IS_FINISH), isDelete = get(IS_DELETE), updateAt = get(UPDATE_AT), diff --git a/server/core/database/src/main/kotlin/io/github/taetae98coding/diary/core/database/MemoTagTable.kt b/server/core/database/src/main/kotlin/io/github/taetae98coding/diary/core/database/MemoTagTable.kt new file mode 100644 index 00000000..21e71885 --- /dev/null +++ b/server/core/database/src/main/kotlin/io/github/taetae98coding/diary/core/database/MemoTagTable.kt @@ -0,0 +1,44 @@ +package io.github.taetae98coding.diary.core.database + +import org.jetbrains.exposed.sql.ReferenceOption +import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq +import org.jetbrains.exposed.sql.Table +import org.jetbrains.exposed.sql.deleteWhere +import org.jetbrains.exposed.sql.selectAll +import org.jetbrains.exposed.sql.upsert + +public data object MemoTagTable : Table(name = "MemoTag") { + private val MEMO_ID = + reference( + name = "memoId", + refColumn = MemoTable.ID, + onDelete = ReferenceOption.CASCADE, + onUpdate = ReferenceOption.CASCADE, + ) + + private val TAG_ID = + reference( + name = "tagId", + refColumn = TagTable.ID, + onDelete = ReferenceOption.CASCADE, + onUpdate = ReferenceOption.CASCADE, + ) + + override val primaryKey: PrimaryKey = PrimaryKey(MEMO_ID, TAG_ID) + + public fun upsert(memoId: String, tagId: String) { + upsert { + it[MEMO_ID] = memoId + it[TAG_ID] = tagId + } + } + + public fun deleteByMemoId(memoId: String) { + deleteWhere { MEMO_ID eq memoId } + } + + public fun findTagIdsByMemoId(memoId: String): List = + selectAll() + .where { MEMO_ID eq memoId } + .map { it[TAG_ID] } +} diff --git a/server/core/database/src/main/kotlin/io/github/taetae98coding/diary/core/database/TagTable.kt b/server/core/database/src/main/kotlin/io/github/taetae98coding/diary/core/database/TagTable.kt new file mode 100644 index 00000000..3ddd9886 --- /dev/null +++ b/server/core/database/src/main/kotlin/io/github/taetae98coding/diary/core/database/TagTable.kt @@ -0,0 +1,70 @@ +package io.github.taetae98coding.diary.core.database + +import io.github.taetae98coding.diary.core.model.Tag +import kotlinx.datetime.Clock +import kotlinx.datetime.Instant +import org.jetbrains.exposed.sql.ReferenceOption +import org.jetbrains.exposed.sql.ResultRow +import org.jetbrains.exposed.sql.Table +import org.jetbrains.exposed.sql.and +import org.jetbrains.exposed.sql.kotlin.datetime.timestamp +import org.jetbrains.exposed.sql.selectAll +import org.jetbrains.exposed.sql.upsert + +public data object TagTable : Table(name = "Tag") { + internal val ID = varchar("id", 255).default("") + + private val TITLE = varchar("title", 255).default("") + private val DESCRIPTION = text("description") + private val COLOR = integer("color").default(0xFFFFFFFF.toInt()) + private val OWNER = + reference( + name = "owner", + refColumn = AccountTable.UID, + onDelete = ReferenceOption.CASCADE, + onUpdate = ReferenceOption.CASCADE, + ) + private val IS_FINISH = bool("isFinish").default(false) + private val IS_DELETE = bool("isDelete").default(false) + private val UPDATE_AT = timestamp("updateAt").default(Clock.System.now()) + + override val primaryKey: PrimaryKey = PrimaryKey(ID) + + public fun upsert(list: List) { + list.forEach { tag -> + upsert { + it[ID] = tag.id + it[TITLE] = tag.title + it[DESCRIPTION] = tag.description + it[COLOR] = tag.color + it[OWNER] = tag.owner + it[IS_FINISH] = tag.isFinish + it[IS_DELETE] = tag.isDelete + it[UPDATE_AT] = tag.updateAt + } + } + } + + public fun findByIds(ids: Set): List = + selectAll() + .where { ID.inList(ids) } + .map { it.toTag() } + + public fun findByUpdateAt(uid: String, updateAt: Instant): List = + selectAll() + .where { (OWNER eq uid) and (UPDATE_AT greater updateAt) } + .limit(50) + .map { it.toTag() } + + private fun ResultRow.toTag(): Tag = + Tag( + id = get(ID), + title = get(TITLE), + description = get(DESCRIPTION), + color = get(COLOR), + owner = get(OWNER), + isFinish = get(IS_FINISH), + isDelete = get(IS_DELETE), + updateAt = get(UPDATE_AT), + ) +} diff --git a/server/core/model/src/main/kotlin/io/github/taetae98coding/diary/core/model/Memo.kt b/server/core/model/src/main/kotlin/io/github/taetae98coding/diary/core/model/Memo.kt index cc491c2a..5b934687 100644 --- a/server/core/model/src/main/kotlin/io/github/taetae98coding/diary/core/model/Memo.kt +++ b/server/core/model/src/main/kotlin/io/github/taetae98coding/diary/core/model/Memo.kt @@ -11,6 +11,7 @@ public data class Memo( val endInclusive: LocalDate?, val color: Int, val owner: String, + val primaryTag: String?, val isFinish: Boolean, val isDelete: Boolean, val updateAt: Instant, diff --git a/server/core/model/src/main/kotlin/io/github/taetae98coding/diary/core/model/MemoAndTagIds.kt b/server/core/model/src/main/kotlin/io/github/taetae98coding/diary/core/model/MemoAndTagIds.kt new file mode 100644 index 00000000..3a96067a --- /dev/null +++ b/server/core/model/src/main/kotlin/io/github/taetae98coding/diary/core/model/MemoAndTagIds.kt @@ -0,0 +1,6 @@ +package io.github.taetae98coding.diary.core.model + +public data class MemoAndTagIds( + val memo: Memo, + val tagIds: Set, +) diff --git a/server/core/model/src/main/kotlin/io/github/taetae98coding/diary/core/model/Tag.kt b/server/core/model/src/main/kotlin/io/github/taetae98coding/diary/core/model/Tag.kt new file mode 100644 index 00000000..c585e522 --- /dev/null +++ b/server/core/model/src/main/kotlin/io/github/taetae98coding/diary/core/model/Tag.kt @@ -0,0 +1,14 @@ +package io.github.taetae98coding.diary.core.model + +import kotlinx.datetime.Instant + +public data class Tag( + val id: String, + val title: String, + val description: String, + val color: Int, + val owner: String, + val isFinish: Boolean, + val isDelete: Boolean, + val updateAt: Instant, +) diff --git a/server/data/account/src/main/kotlin/io/github/taetae98coding/diary/data/account/repository/AccountRepositoryImpl.kt b/server/data/account/src/main/kotlin/io/github/taetae98coding/diary/data/account/repository/AccountRepositoryImpl.kt index b3a4b2e1..78843895 100644 --- a/server/data/account/src/main/kotlin/io/github/taetae98coding/diary/data/account/repository/AccountRepositoryImpl.kt +++ b/server/data/account/src/main/kotlin/io/github/taetae98coding/diary/data/account/repository/AccountRepositoryImpl.kt @@ -3,15 +3,18 @@ package io.github.taetae98coding.diary.data.account.repository import io.github.taetae98coding.diary.core.database.AccountTable import io.github.taetae98coding.diary.core.model.Account import io.github.taetae98coding.diary.domain.account.repository.AccountRepository +import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction import org.koin.core.annotation.Singleton @Singleton internal class AccountRepositoryImpl : AccountRepository { - override suspend fun contains(email: String): Boolean = AccountTable.contains(email) + override suspend fun contains(email: String): Boolean = newSuspendedTransaction { AccountTable.contains(email) } override suspend fun upsert(account: Account) { - AccountTable.insert(account) + newSuspendedTransaction { + AccountTable.insert(account) + } } - override suspend fun findByEmail(email: String, password: String): Account? = AccountTable.findByEmail(email, password) + override suspend fun findByEmail(email: String, password: String): Account? = newSuspendedTransaction { AccountTable.findByEmail(email, password) } } diff --git a/server/data/fcm/src/main/kotlin/io/github/taetae98coding/diary/data/fcm/repository/FCMRepositoryImpl.kt b/server/data/fcm/src/main/kotlin/io/github/taetae98coding/diary/data/fcm/repository/FCMRepositoryImpl.kt index 9b4433fa..f3e76645 100644 --- a/server/data/fcm/src/main/kotlin/io/github/taetae98coding/diary/data/fcm/repository/FCMRepositoryImpl.kt +++ b/server/data/fcm/src/main/kotlin/io/github/taetae98coding/diary/data/fcm/repository/FCMRepositoryImpl.kt @@ -2,15 +2,20 @@ package io.github.taetae98coding.diary.data.fcm.repository import io.github.taetae98coding.diary.core.database.FCMTokenTable import io.github.taetae98coding.diary.domain.fcm.repository.FCMRepository +import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction import org.koin.core.annotation.Factory @Factory internal class FCMRepositoryImpl : FCMRepository { override suspend fun upsert(token: String, owner: String) { - FCMTokenTable.upsert(token, owner) + newSuspendedTransaction { + FCMTokenTable.upsert(token, owner) + } } override suspend fun delete(token: String) { - FCMTokenTable.delete(token) + newSuspendedTransaction { + FCMTokenTable.delete(token) + } } } diff --git a/server/data/memo/src/main/kotlin/io/github/taetae98coding/diary/data/memo/repository/MemoRepositoryImpl.kt b/server/data/memo/src/main/kotlin/io/github/taetae98coding/diary/data/memo/repository/MemoRepositoryImpl.kt index 2b0fc26f..c07ac2fc 100644 --- a/server/data/memo/src/main/kotlin/io/github/taetae98coding/diary/data/memo/repository/MemoRepositoryImpl.kt +++ b/server/data/memo/src/main/kotlin/io/github/taetae98coding/diary/data/memo/repository/MemoRepositoryImpl.kt @@ -1,20 +1,43 @@ package io.github.taetae98coding.diary.data.memo.repository import io.github.taetae98coding.diary.core.database.MemoTable +import io.github.taetae98coding.diary.core.database.MemoTagTable import io.github.taetae98coding.diary.core.model.Memo +import io.github.taetae98coding.diary.core.model.MemoAndTagIds import io.github.taetae98coding.diary.domain.memo.repository.MemoRepository import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import kotlinx.datetime.Instant +import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction import org.koin.core.annotation.Factory @Factory internal class MemoRepositoryImpl : MemoRepository { - override suspend fun upsert(list: List) { - MemoTable.upsert(list) + override suspend fun upsert(list: List) { + newSuspendedTransaction { + list.forEach { memoAndTagIds -> + MemoTable.upsert(memoAndTagIds.memo) + MemoTagTable.deleteByMemoId(memoAndTagIds.memo.id) + memoAndTagIds.tagIds.forEach { + MemoTagTable.upsert(memoAndTagIds.memo.id, it) + } + } + } } - override fun findByIds(list: List): Flow> = flow { emit(MemoTable.findByIds(list)) } + override fun findByIds(ids: Set): Flow> = flow { emit(newSuspendedTransaction { MemoTable.findByIds(ids) }) } - override fun findByUpdateAt(uid: String, updateAt: Instant): Flow> = flow { emit(MemoTable.findByUpdateAt(uid, updateAt)) } + override fun findMemoAndTagIdsByUpdateAt(uid: String, updateAt: Instant): Flow> = + flow { + newSuspendedTransaction { + MemoTable + .findByUpdateAt(uid, updateAt) + .map { memo -> + MemoAndTagIds( + memo = memo, + tagIds = MemoTagTable.findTagIdsByMemoId(memo.id).toSet(), + ) + } + }.also { emit(it) } + } } diff --git a/server/data/tag/README.md b/server/data/tag/README.md new file mode 100644 index 00000000..17e35a6c --- /dev/null +++ b/server/data/tag/README.md @@ -0,0 +1,3 @@ +# :server:data:tag module +## Dependency graph +![Dependency graph](../../../docs/images/graphs/dep_graph_server_data_tag.svg) diff --git a/server/data/tag/build.gradle.kts b/server/data/tag/build.gradle.kts new file mode 100644 index 00000000..1e850689 --- /dev/null +++ b/server/data/tag/build.gradle.kts @@ -0,0 +1,11 @@ +plugins { + id("diary.server.data") +} + +dependencies { + implementation(project(":server:core:database")) + implementation(project(":server:domain:tag")) + + implementation(platform(libs.exposed.bom)) + implementation(libs.exposed.core) +} diff --git a/server/data/tag/src/main/kotlin/io/github/taetae98coding/diary/data/tag/TagDataModule.kt b/server/data/tag/src/main/kotlin/io/github/taetae98coding/diary/data/tag/TagDataModule.kt new file mode 100644 index 00000000..e7d81c0a --- /dev/null +++ b/server/data/tag/src/main/kotlin/io/github/taetae98coding/diary/data/tag/TagDataModule.kt @@ -0,0 +1,8 @@ +package io.github.taetae98coding.diary.data.tag + +import org.koin.core.annotation.ComponentScan +import org.koin.core.annotation.Module + +@Module +@ComponentScan +public class TagDataModule diff --git a/server/data/tag/src/main/kotlin/io/github/taetae98coding/diary/data/tag/repository/TagRepositoryImpl.kt b/server/data/tag/src/main/kotlin/io/github/taetae98coding/diary/data/tag/repository/TagRepositoryImpl.kt new file mode 100644 index 00000000..a6a09e9e --- /dev/null +++ b/server/data/tag/src/main/kotlin/io/github/taetae98coding/diary/data/tag/repository/TagRepositoryImpl.kt @@ -0,0 +1,23 @@ +package io.github.taetae98coding.diary.data.tag.repository + +import io.github.taetae98coding.diary.core.database.TagTable +import io.github.taetae98coding.diary.core.model.Tag +import io.github.taetae98coding.diary.domain.tag.repository.TagRepository +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import kotlinx.datetime.Instant +import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction +import org.koin.core.annotation.Factory + +@Factory +internal class TagRepositoryImpl : TagRepository { + override suspend fun upsert(list: List) { + newSuspendedTransaction { + TagTable.upsert(list) + } + } + + override fun findByIds(ids: Set): Flow> = flow { emit(newSuspendedTransaction { TagTable.findByIds(ids) }) } + + override fun findByUpdateAt(uid: String, updateAt: Instant): Flow> = flow { emit(newSuspendedTransaction { TagTable.findByUpdateAt(uid, updateAt) }) } +} diff --git a/server/domain/memo/src/main/kotlin/io/github/taetae98coding/diary/domain/memo/repository/MemoRepository.kt b/server/domain/memo/src/main/kotlin/io/github/taetae98coding/diary/domain/memo/repository/MemoRepository.kt index 93f62a53..2d8709a3 100644 --- a/server/domain/memo/src/main/kotlin/io/github/taetae98coding/diary/domain/memo/repository/MemoRepository.kt +++ b/server/domain/memo/src/main/kotlin/io/github/taetae98coding/diary/domain/memo/repository/MemoRepository.kt @@ -1,13 +1,14 @@ package io.github.taetae98coding.diary.domain.memo.repository import io.github.taetae98coding.diary.core.model.Memo +import io.github.taetae98coding.diary.core.model.MemoAndTagIds import kotlinx.coroutines.flow.Flow import kotlinx.datetime.Instant public interface MemoRepository { - public suspend fun upsert(list: List) + public suspend fun upsert(list: List) - public fun findByIds(list: List): Flow> + public fun findByIds(ids: Set): Flow> - public fun findByUpdateAt(uid: String, updateAt: Instant): Flow> + public fun findMemoAndTagIdsByUpdateAt(uid: String, updateAt: Instant): Flow> } diff --git a/server/domain/memo/src/main/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/FetchMemoUseCase.kt b/server/domain/memo/src/main/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/FetchMemoUseCase.kt index 0dc3f4f0..78cc73bd 100644 --- a/server/domain/memo/src/main/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/FetchMemoUseCase.kt +++ b/server/domain/memo/src/main/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/FetchMemoUseCase.kt @@ -1,6 +1,6 @@ package io.github.taetae98coding.diary.domain.memo.usecase -import io.github.taetae98coding.diary.core.model.Memo +import io.github.taetae98coding.diary.core.model.MemoAndTagIds import io.github.taetae98coding.diary.domain.memo.repository.MemoRepository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow @@ -16,8 +16,8 @@ import org.koin.core.annotation.Factory public class FetchMemoUseCase internal constructor( private val repository: MemoRepository, ) { - public operator fun invoke(uid: String, updateAt: Instant): Flow>> = - flow { emitAll(repository.findByUpdateAt(uid, updateAt)) } + public operator fun invoke(uid: String, updateAt: Instant): Flow>> = + flow { emitAll(repository.findMemoAndTagIdsByUpdateAt(uid, updateAt)) } .mapLatest { Result.success(it) } .catch { emit(Result.failure(it)) } } diff --git a/server/domain/memo/src/main/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/UpsertMemoUseCase.kt b/server/domain/memo/src/main/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/UpsertMemoUseCase.kt index 42afe119..6f713f1a 100644 --- a/server/domain/memo/src/main/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/UpsertMemoUseCase.kt +++ b/server/domain/memo/src/main/kotlin/io/github/taetae98coding/diary/domain/memo/usecase/UpsertMemoUseCase.kt @@ -1,7 +1,7 @@ package io.github.taetae98coding.diary.domain.memo.usecase -import io.github.taetae98coding.diary.common.exception.memo.MemoTitleBlankException -import io.github.taetae98coding.diary.core.model.Memo +import io.github.taetae98coding.diary.common.exception.tag.TagTitleBlankException +import io.github.taetae98coding.diary.core.model.MemoAndTagIds import io.github.taetae98coding.diary.domain.memo.repository.MemoRepository import kotlinx.coroutines.flow.first import org.koin.core.annotation.Factory @@ -10,28 +10,31 @@ import org.koin.core.annotation.Factory public class UpsertMemoUseCase internal constructor( private val repository: MemoRepository, ) { - public suspend operator fun invoke(list: List): Result { + public suspend operator fun invoke(list: List): Result { return runCatching { - // TODO permission check - - val originMemoMap = + // TODO Permission Check + val ids = list.map { it.memo.id }.toSet() + val originMap = repository - .findByIds(list.map { it.id }) + .findByIds(ids) .first() .associateBy { it.id } val validList = list .filter { - val originMemo = originMemoMap[it.id] ?: return@filter true - it.updateAt >= originMemo.updateAt + val origin = originMap[it.memo.id] ?: return@filter true + it.memo.updateAt >= origin.updateAt }.map { it.copy( - title = - it.title.ifBlank { - val originMemo = originMemoMap[it.id] ?: throw MemoTitleBlankException() - originMemo.title - }, + memo = + it.memo.copy( + title = + it.memo.title.ifBlank { + val origin = originMap[it.memo.id] ?: throw TagTitleBlankException() + origin.title + }, + ), ) } diff --git a/server/domain/tag/README.md b/server/domain/tag/README.md new file mode 100644 index 00000000..67d72404 --- /dev/null +++ b/server/domain/tag/README.md @@ -0,0 +1,3 @@ +# :server:domain:tag module +## Dependency graph +![Dependency graph](../../../docs/images/graphs/dep_graph_server_domain_tag.svg) diff --git a/server/domain/tag/build.gradle.kts b/server/domain/tag/build.gradle.kts new file mode 100644 index 00000000..e10cade2 --- /dev/null +++ b/server/domain/tag/build.gradle.kts @@ -0,0 +1,3 @@ +plugins { + id("diary.server.domain") +} diff --git a/server/domain/tag/src/main/kotlin/io/github/taetae98coding/diary/domain/tag/TagDomainModule.kt b/server/domain/tag/src/main/kotlin/io/github/taetae98coding/diary/domain/tag/TagDomainModule.kt new file mode 100644 index 00000000..9e7c32f6 --- /dev/null +++ b/server/domain/tag/src/main/kotlin/io/github/taetae98coding/diary/domain/tag/TagDomainModule.kt @@ -0,0 +1,8 @@ +package io.github.taetae98coding.diary.domain.tag + +import org.koin.core.annotation.ComponentScan +import org.koin.core.annotation.Module + +@Module +@ComponentScan +public class TagDomainModule diff --git a/server/domain/tag/src/main/kotlin/io/github/taetae98coding/diary/domain/tag/repository/TagRepository.kt b/server/domain/tag/src/main/kotlin/io/github/taetae98coding/diary/domain/tag/repository/TagRepository.kt new file mode 100644 index 00000000..dd8ba5cf --- /dev/null +++ b/server/domain/tag/src/main/kotlin/io/github/taetae98coding/diary/domain/tag/repository/TagRepository.kt @@ -0,0 +1,13 @@ +package io.github.taetae98coding.diary.domain.tag.repository + +import io.github.taetae98coding.diary.core.model.Tag +import kotlinx.coroutines.flow.Flow +import kotlinx.datetime.Instant + +public interface TagRepository { + public suspend fun upsert(list: List) + + public fun findByIds(ids: Set): Flow> + + public fun findByUpdateAt(uid: String, updateAt: Instant): Flow> +} diff --git a/server/domain/tag/src/main/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/FetchTagUseCase.kt b/server/domain/tag/src/main/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/FetchTagUseCase.kt new file mode 100644 index 00000000..9c813172 --- /dev/null +++ b/server/domain/tag/src/main/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/FetchTagUseCase.kt @@ -0,0 +1,23 @@ +package io.github.taetae98coding.diary.domain.tag.usecase + +import io.github.taetae98coding.diary.core.model.Tag +import io.github.taetae98coding.diary.domain.tag.repository.TagRepository +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.emitAll +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.mapLatest +import kotlinx.datetime.Instant +import org.koin.core.annotation.Factory + +@OptIn(ExperimentalCoroutinesApi::class) +@Factory +public class FetchTagUseCase internal constructor( + private val repository: TagRepository, +) { + public operator fun invoke(uid: String, updateAt: Instant): Flow>> = + flow { emitAll(repository.findByUpdateAt(uid, updateAt)) } + .mapLatest { Result.success(it) } + .catch { emit(Result.failure(it)) } +} diff --git a/server/domain/tag/src/main/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/UpsertTagUseCase.kt b/server/domain/tag/src/main/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/UpsertTagUseCase.kt new file mode 100644 index 00000000..7363c101 --- /dev/null +++ b/server/domain/tag/src/main/kotlin/io/github/taetae98coding/diary/domain/tag/usecase/UpsertTagUseCase.kt @@ -0,0 +1,41 @@ +package io.github.taetae98coding.diary.domain.tag.usecase + +import io.github.taetae98coding.diary.common.exception.tag.TagTitleBlankException +import io.github.taetae98coding.diary.core.model.Tag +import io.github.taetae98coding.diary.domain.tag.repository.TagRepository +import kotlinx.coroutines.flow.first +import org.koin.core.annotation.Factory + +@Factory +public class UpsertTagUseCase internal constructor( + private val repository: TagRepository, +) { + public suspend operator fun invoke(list: List): Result { + return runCatching { + // TODO Permission Check + val ids = list.map { it.id }.toSet() + val originMap = + repository + .findByIds(ids) + .first() + .associateBy { it.id } + + val validList = + list + .filter { + val origin = originMap[it.id] ?: return@filter true + it.updateAt >= origin.updateAt + }.map { + it.copy( + title = + it.title.ifBlank { + val origin = originMap[it.id] ?: throw TagTitleBlankException() + origin.title + }, + ) + } + + repository.upsert(validList) + } + } +} diff --git a/server/feature/memo/src/main/kotlin/io/github/taetae98coding/diary/feature/memo/MemoRouting.kt b/server/feature/memo/src/main/kotlin/io/github/taetae98coding/diary/feature/memo/MemoRouting.kt index 3ed30933..ab86842a 100644 --- a/server/feature/memo/src/main/kotlin/io/github/taetae98coding/diary/feature/memo/MemoRouting.kt +++ b/server/feature/memo/src/main/kotlin/io/github/taetae98coding/diary/feature/memo/MemoRouting.kt @@ -3,6 +3,7 @@ package io.github.taetae98coding.diary.feature.memo import io.github.taetae98coding.diary.common.model.memo.MemoEntity import io.github.taetae98coding.diary.common.model.response.DiaryResponse import io.github.taetae98coding.diary.core.model.Memo +import io.github.taetae98coding.diary.core.model.MemoAndTagIds import io.github.taetae98coding.diary.domain.memo.usecase.FetchMemoUseCase import io.github.taetae98coding.diary.domain.memo.usecase.UpsertMemoUseCase import io.ktor.http.HttpStatusCode @@ -29,7 +30,13 @@ public fun Route.memoRouting() { } val useCase = call.scope.get() - val memoList = request.map(MemoEntity::toMemo) + val memoList = + request.map { + MemoAndTagIds( + memo = it.toMemo(), + tagIds = it.tagIds, + ) + } useCase(memoList).onSuccess { call.respond(DiaryResponse.Success) } } @@ -47,7 +54,7 @@ public fun Route.memoRouting() { useCase(uid, updateAt) .first() - .onSuccess { call.respond(DiaryResponse.success(it.map(Memo::toEntity))) } + .onSuccess { call.respond(DiaryResponse.success(it.map(MemoAndTagIds::toEntity))) } } } } @@ -62,21 +69,24 @@ private fun MemoEntity.toMemo(): Memo = endInclusive = endInclusive, color = color, owner = owner, + primaryTag = primaryTag, isFinish = isFinish, isDelete = isDelete, updateAt = updateAt, ) -private fun Memo.toEntity(): MemoEntity = +private fun MemoAndTagIds.toEntity(): MemoEntity = MemoEntity( - id = id, - title = title, - description = description, - start = start, - endInclusive = endInclusive, - color = color, - owner = owner, - isFinish = isFinish, - isDelete = isDelete, - updateAt = updateAt, + id = memo.id, + title = memo.title, + description = memo.description, + start = memo.start, + endInclusive = memo.endInclusive, + color = memo.color, + owner = memo.owner, + primaryTag = memo.primaryTag, + tagIds = tagIds, + isFinish = memo.isFinish, + isDelete = memo.isDelete, + updateAt = memo.updateAt, ) diff --git a/server/feature/tag/README.md b/server/feature/tag/README.md new file mode 100644 index 00000000..d38eb134 --- /dev/null +++ b/server/feature/tag/README.md @@ -0,0 +1,3 @@ +# :server:feature:tag module +## Dependency graph +![Dependency graph](../../../docs/images/graphs/dep_graph_server_feature_tag.svg) diff --git a/server/feature/tag/build.gradle.kts b/server/feature/tag/build.gradle.kts new file mode 100644 index 00000000..214d4787 --- /dev/null +++ b/server/feature/tag/build.gradle.kts @@ -0,0 +1,7 @@ +plugins { + id("diary.server.feature") +} + +dependencies { + implementation(project(":server:domain:tag")) +} diff --git a/server/feature/tag/src/main/kotlin/io/github/taetae98coding/diary/feature/tag/TagRouting.kt b/server/feature/tag/src/main/kotlin/io/github/taetae98coding/diary/feature/tag/TagRouting.kt new file mode 100644 index 00000000..98fbc956 --- /dev/null +++ b/server/feature/tag/src/main/kotlin/io/github/taetae98coding/diary/feature/tag/TagRouting.kt @@ -0,0 +1,78 @@ +package io.github.taetae98coding.diary.feature.tag + +import io.github.taetae98coding.diary.common.model.memo.TagEntity +import io.github.taetae98coding.diary.common.model.response.DiaryResponse +import io.github.taetae98coding.diary.core.model.Tag +import io.github.taetae98coding.diary.domain.tag.usecase.FetchTagUseCase +import io.github.taetae98coding.diary.domain.tag.usecase.UpsertTagUseCase +import io.ktor.http.HttpStatusCode +import io.ktor.server.auth.authenticate +import io.ktor.server.auth.jwt.JWTPrincipal +import io.ktor.server.auth.principal +import io.ktor.server.response.respond +import io.ktor.server.routing.Route +import io.ktor.server.routing.get +import io.ktor.server.routing.post +import io.ktor.server.routing.route +import kotlinx.coroutines.flow.first +import kotlinx.datetime.Instant +import org.koin.ktor.plugin.scope + +public fun Route.tagRouting() { + route("/tag") { + authenticate("account") { + post>("/upsert") { request -> + val principal = call.principal() + if (principal == null) { + call.respond(HttpStatusCode.Unauthorized, DiaryResponse.Unauthorized) + return@post + } + + val useCase = call.scope.get() + val memoList = request.map(TagEntity::toTag) + + useCase(memoList).onSuccess { call.respond(DiaryResponse.Success) } + } + + get("/fetch") { + val principal = call.principal() + if (principal == null) { + call.respond(HttpStatusCode.Unauthorized, DiaryResponse.Unauthorized) + return@get + } + + val uid = principal.payload.getClaim("uid").asString() + val updateAt = call.parameters["updateAt"]?.let { Instant.parse(it) } ?: return@get + val useCase = call.scope.get() + + useCase(uid, updateAt) + .first() + .onSuccess { call.respond(DiaryResponse.success(it.map(Tag::toEntity))) } + } + } + } +} + +private fun TagEntity.toTag(): Tag = + Tag( + id = id, + title = title, + description = description, + color = color, + owner = owner, + isFinish = isFinish, + isDelete = isDelete, + updateAt = updateAt, + ) + +private fun Tag.toEntity(): TagEntity = + TagEntity( + id = id, + title = title, + description = description, + color = color, + owner = owner, + isFinish = isFinish, + isDelete = isDelete, + updateAt = updateAt, + ) diff --git a/settings.gradle.kts b/settings.gradle.kts index 0031627f..ab23eeed 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -30,37 +30,36 @@ dependencyResolutionManagement { rootProject.name = "Diary" include(":app:platform:jvm") -include(":app:platform:wasm") include(":app:platform:android") include(":app:platform:ios") include(":app:platform:common") include(":app:core:diary-service") include(":app:core:diary-database") -include(":app:core:diary-database-memory") include(":app:core:diary-database-room") include(":app:core:account-preferences") -include(":app:core:account-preferences-memory") include(":app:core:account-preferences-datastore") include(":app:core:holiday-service") include(":app:core:holiday-database") -include(":app:core:holiday-database-memory") include(":app:core:holiday-database-room") include(":app:core:holiday-preferences") -include(":app:core:holiday-preferences-memory") include(":app:core:holiday-preferences-datastore") +include(":app:core:backup-database") +include(":app:core:backup-database-room") + +include(":app:core:compose") include(":app:core:calendar-compose") include(":app:core:design-system") include(":app:core:coroutines") include(":app:core:model") include(":app:core:navigation") -include(":app:core:resources") include(":app:data:memo") +include(":app:data:tag") include(":app:data:account") include(":app:data:holiday") include(":app:data:backup") @@ -69,14 +68,17 @@ include(":app:data:fcm") include(":app:data:credential") include(":app:domain:memo") +include(":app:domain:tag") include(":app:domain:account") include(":app:domain:holiday") include(":app:domain:backup") include(":app:domain:fetch") include(":app:domain:fcm") include(":app:domain:credential") +include(":app:domain:calendar") include(":app:feature:memo") +include(":app:feature:tag") include(":app:feature:calendar") include(":app:feature:more") include(":app:feature:account") @@ -86,16 +88,19 @@ include(":server:core:model") include(":server:data:account") include(":server:data:memo") +include(":server:data:tag") include(":server:data:fcm") include(":server:domain:account") include(":server:domain:memo") +include(":server:domain:tag") include(":server:domain:fcm") include(":server:app") include(":server:feature:home") include(":server:feature:account") include(":server:feature:memo") +include(":server:feature:tag") include(":server:feature:fcm") include(":common:exception")