From c01338da2a334c0b1b33e9cf970b68fb4de2cc25 Mon Sep 17 00:00:00 2001 From: eastshine2741 Date: Sun, 20 Oct 2024 15:50:26 +0900 Subject: [PATCH 01/35] =?UTF-8?q?ThemeDto=EC=9D=98=20=ED=95=84=EB=93=9C?= =?UTF-8?q?=EB=A5=BC=20nullable=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../snutt2/data/themes/ThemeRepositoryImpl.kt | 2 +- .../snutt2/lib/network/dto/core/ThemeDto.kt | 20 +++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/data/themes/ThemeRepositoryImpl.kt b/app/src/main/java/com/wafflestudio/snutt2/data/themes/ThemeRepositoryImpl.kt index 1e249a32f..697718921 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/data/themes/ThemeRepositoryImpl.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/data/themes/ThemeRepositoryImpl.kt @@ -39,7 +39,7 @@ class ThemeRepositoryImpl @Inject constructor( override suspend fun fetchThemes() { api._getThemes().let { themes -> - _customThemes.value = themes.filter { it.isCustom }.map { it.toTableTheme() as CustomTheme } + _customThemes.value = themes.filter { it.isCustom == true }.map { it.toTableTheme() as CustomTheme } _builtInThemes.value = (0..5).map { code -> BuiltInTheme.fromCode(code) } diff --git a/app/src/main/java/com/wafflestudio/snutt2/lib/network/dto/core/ThemeDto.kt b/app/src/main/java/com/wafflestudio/snutt2/lib/network/dto/core/ThemeDto.kt index 8d57fe44d..6a89f832d 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/lib/network/dto/core/ThemeDto.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/lib/network/dto/core/ThemeDto.kt @@ -7,24 +7,24 @@ import com.wafflestudio.snutt2.model.TableTheme @JsonClass(generateAdapter = true) data class ThemeDto( - val id: String? = null, - val theme: Int = 0, - val name: String = "", - val colors: List = emptyList(), - val isCustom: Boolean = false, + val id: String?, + val theme: Int?, + val name: String?, + val colors: List?, + val isCustom: Boolean?, ) { fun toTableTheme(): TableTheme { - return if (isCustom) { + return if (isCustom != false) { CustomTheme( id = id!!, - name = name, - colors = colors, + name = name ?: "", + colors = colors ?: emptyList(), ) } else { BuiltInTheme( - code = theme, - name = name, + code = theme ?: 0, + name = name ?: "", ) } } From 5b26aaf57e34b7be11cabbf34ca11c627438451c Mon Sep 17 00:00:00 2001 From: eastshine2741 Date: Sun, 20 Oct 2024 16:29:52 +0900 Subject: [PATCH 02/35] =?UTF-8?q?GtCurrentTableThemeUseCase=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../snutt2/data/themes/ThemeRepository.kt | 3 --- .../snutt2/data/themes/ThemeRepositoryImpl.kt | 15 ------------ .../domain/GetCurrentTableThemeUseCase.kt | 24 +++++++++++++++++++ .../home/timetable/TimetableViewModel.kt | 10 +++++--- .../lecture_detail/LectureDetailViewModel.kt | 8 ++++--- 5 files changed, 36 insertions(+), 24 deletions(-) create mode 100644 app/src/main/java/com/wafflestudio/snutt2/domain/GetCurrentTableThemeUseCase.kt diff --git a/app/src/main/java/com/wafflestudio/snutt2/data/themes/ThemeRepository.kt b/app/src/main/java/com/wafflestudio/snutt2/data/themes/ThemeRepository.kt index 0820ea7da..9d0121af2 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/data/themes/ThemeRepository.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/data/themes/ThemeRepository.kt @@ -3,7 +3,6 @@ package com.wafflestudio.snutt2.data.themes import com.wafflestudio.snutt2.lib.network.dto.core.ColorDto import com.wafflestudio.snutt2.model.BuiltInTheme import com.wafflestudio.snutt2.model.CustomTheme -import com.wafflestudio.snutt2.model.TableTheme import kotlinx.coroutines.flow.StateFlow interface ThemeRepository { @@ -12,8 +11,6 @@ interface ThemeRepository { val builtInThemes: StateFlow> - val currentTableTheme: StateFlow // FIXME: 이게 ThemeRepository에 있는 게 맞나... ThemeRepository와 CurrentTableRepository에 의존하는 UseCase를 만들어야 할까? - suspend fun fetchThemes() fun getTheme(themeId: String): CustomTheme diff --git a/app/src/main/java/com/wafflestudio/snutt2/data/themes/ThemeRepositoryImpl.kt b/app/src/main/java/com/wafflestudio/snutt2/data/themes/ThemeRepositoryImpl.kt index 697718921..a46bc88e9 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/data/themes/ThemeRepositoryImpl.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/data/themes/ThemeRepositoryImpl.kt @@ -1,26 +1,19 @@ package com.wafflestudio.snutt2.data.themes -import com.wafflestudio.snutt2.data.SNUTTStorage import com.wafflestudio.snutt2.lib.network.SNUTTRestApi import com.wafflestudio.snutt2.lib.network.dto.PatchThemeParams import com.wafflestudio.snutt2.lib.network.dto.PostThemeParams import com.wafflestudio.snutt2.lib.network.dto.core.ColorDto import com.wafflestudio.snutt2.model.BuiltInTheme import com.wafflestudio.snutt2.model.CustomTheme -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.stateIn import javax.inject.Inject import javax.inject.Singleton @Singleton class ThemeRepositoryImpl @Inject constructor( private val api: SNUTTRestApi, - private val storage: SNUTTStorage, - externalScope: CoroutineScope, ) : ThemeRepository { private val _customThemes = MutableStateFlow>(emptyList()) @@ -29,14 +22,6 @@ class ThemeRepositoryImpl @Inject constructor( private val _builtInThemes = MutableStateFlow>(emptyList()) override val builtInThemes: StateFlow> = _builtInThemes - override val currentTableTheme = combine(storage.lastViewedTable.asStateFlow(), _customThemes) { table, _ -> - table.value?.themeId?.let { themeId -> - getTheme(themeId) - } ?: table.value?.theme?.let { - BuiltInTheme.fromCode(it) - } ?: BuiltInTheme.SNUTT - }.stateIn(externalScope, SharingStarted.Eagerly, BuiltInTheme.SNUTT) - override suspend fun fetchThemes() { api._getThemes().let { themes -> _customThemes.value = themes.filter { it.isCustom == true }.map { it.toTableTheme() as CustomTheme } diff --git a/app/src/main/java/com/wafflestudio/snutt2/domain/GetCurrentTableThemeUseCase.kt b/app/src/main/java/com/wafflestudio/snutt2/domain/GetCurrentTableThemeUseCase.kt new file mode 100644 index 000000000..14e3c5d4f --- /dev/null +++ b/app/src/main/java/com/wafflestudio/snutt2/domain/GetCurrentTableThemeUseCase.kt @@ -0,0 +1,24 @@ +package com.wafflestudio.snutt2.domain + +import com.wafflestudio.snutt2.data.current_table.CurrentTableRepository +import com.wafflestudio.snutt2.data.themes.ThemeRepository +import com.wafflestudio.snutt2.model.BuiltInTheme +import com.wafflestudio.snutt2.model.TableTheme +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import javax.inject.Inject + +class GetCurrentTableThemeUseCase @Inject constructor( + private val themeRepository: ThemeRepository, + private val currentTableRepository: CurrentTableRepository, +) { + operator fun invoke(): Flow { + return combine(currentTableRepository.currentTable, themeRepository.customThemes) { table, _ -> + table?.themeId?.let { themeId -> + themeRepository.getTheme(themeId) + } ?: table?.theme?.let { + BuiltInTheme.fromCode(it) + } ?: BuiltInTheme.SNUTT + } + } +} diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/timetable/TimetableViewModel.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/timetable/TimetableViewModel.kt index 6e93a72ba..145e256bc 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/timetable/TimetableViewModel.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/timetable/TimetableViewModel.kt @@ -1,9 +1,10 @@ package com.wafflestudio.snutt2.views.logged_in.home.timetable import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import com.wafflestudio.snutt2.data.current_table.CurrentTableRepository import com.wafflestudio.snutt2.data.tables.TableRepository -import com.wafflestudio.snutt2.data.themes.ThemeRepository +import com.wafflestudio.snutt2.domain.GetCurrentTableThemeUseCase import com.wafflestudio.snutt2.lib.isLectureNumberEquals import com.wafflestudio.snutt2.lib.network.dto.core.LectureDto import com.wafflestudio.snutt2.lib.network.dto.core.TableDto @@ -12,21 +13,24 @@ import com.wafflestudio.snutt2.model.CustomTheme import com.wafflestudio.snutt2.model.TableTheme import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.stateIn import javax.inject.Inject @HiltViewModel class TimetableViewModel @Inject constructor( private val currentTableRepository: CurrentTableRepository, private val tableRepository: TableRepository, - private val themeRepository: ThemeRepository, + getCurrentTableThemeUseCase: GetCurrentTableThemeUseCase, ) : ViewModel() { val currentTable: StateFlow = currentTableRepository.currentTable private val _previewTheme = MutableStateFlow(null) val previewTheme: StateFlow = _previewTheme - val tableTheme: StateFlow = themeRepository.currentTableTheme + val tableTheme: StateFlow = getCurrentTableThemeUseCase() + .stateIn(viewModelScope, SharingStarted.Eagerly, BuiltInTheme.SNUTT) suspend fun addLecture(lecture: LectureDto, is_force: Boolean) { currentTableRepository diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/lecture_detail/LectureDetailViewModel.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/lecture_detail/LectureDetailViewModel.kt index 5e19955b2..95ca2e176 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/lecture_detail/LectureDetailViewModel.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/lecture_detail/LectureDetailViewModel.kt @@ -4,13 +4,14 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.wafflestudio.snutt2.data.current_table.CurrentTableRepository import com.wafflestudio.snutt2.data.lecture_search.LectureSearchRepository -import com.wafflestudio.snutt2.data.themes.ThemeRepository +import com.wafflestudio.snutt2.domain.GetCurrentTableThemeUseCase import com.wafflestudio.snutt2.lib.network.ApiOnError import com.wafflestudio.snutt2.lib.network.dto.PostCustomLectureParams import com.wafflestudio.snutt2.lib.network.dto.PutLectureParams import com.wafflestudio.snutt2.lib.network.dto.core.LectureDto import com.wafflestudio.snutt2.lib.network.dto.core.LectureReviewDto import com.wafflestudio.snutt2.lib.network.dto.core.TableDto +import com.wafflestudio.snutt2.model.BuiltInTheme import com.wafflestudio.snutt2.model.TableTheme import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow @@ -32,12 +33,13 @@ sealed class ModeType { class LectureDetailViewModel @Inject constructor( private val currentTableRepository: CurrentTableRepository, private val lectureSearchRepository: LectureSearchRepository, - private val themeRepository: ThemeRepository, private val apiOnError: ApiOnError, + getCurrentTableThemeUseCase: GetCurrentTableThemeUseCase, ) : ViewModel() { val currentTable: StateFlow = currentTableRepository.currentTable - val currentTableTheme: StateFlow = themeRepository.currentTableTheme + val currentTableTheme: StateFlow = getCurrentTableThemeUseCase() + .stateIn(viewModelScope, SharingStarted.Eagerly, BuiltInTheme.SNUTT) private val _modeType = MutableStateFlow(ModeType.Normal) val modeType = _modeType.asStateFlow() From 812b80a54b7a496633b5b746addcfd895ee05cfb Mon Sep 17 00:00:00 2001 From: eastshine2741 Date: Sun, 20 Oct 2024 16:40:57 +0900 Subject: [PATCH 03/35] =?UTF-8?q?ThemeDetailViewModel=20init=20{}=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../settings/theme/ThemeDetailViewModel.kt | 63 +++++++++++-------- 1 file changed, 38 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailViewModel.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailViewModel.kt index 45ef578f8..8bd0c9e09 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailViewModel.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailViewModel.kt @@ -19,11 +19,11 @@ import javax.inject.Inject @HiltViewModel class ThemeDetailViewModel @Inject constructor( - savedStateHandle: SavedStateHandle, + private val savedStateHandle: SavedStateHandle, private val themeRepository: ThemeRepository, private val tableRepository: TableRepository, currentTableRepository: CurrentTableRepository, - apiOnError: ApiOnError, + private val apiOnError: ApiOnError, ) : ViewModel() { private val _editingTheme = MutableStateFlow(CustomTheme.Default) @@ -38,33 +38,46 @@ class ThemeDetailViewModel @Inject constructor( var isNewTheme = false init { + initEditingTheme() + } + + private fun initEditingTheme() { val themeId = savedStateHandle.get("themeId") val theme = savedStateHandle.get("theme") - if (theme != null && themeId != null) { - if (theme != -1) { - try { - _editingTheme.value = BuiltInTheme.fromCode(theme) - } catch (e: Exception) { - apiOnError(e) - } - } else { - _editingTheme.value = if (themeId.isEmpty()) { - isNewTheme = true - CustomTheme.Default - } else { - try { - themeRepository.getTheme(themeId) - } catch (e: Exception) { - apiOnError(e) - CustomTheme.Default - } - } - _editingColors.value = - (_editingTheme.value as CustomTheme).colors.mapIndexed { idx, color -> - color.toDataWithState(idx == 0) - } + + if (theme == null || themeId == null) return + + if (theme != -1) { // 기본 제공 테마 + initBuiltInTheme(theme) + } else { // 커스텀 테마 + initCustomTheme(themeId) + } + } + + private fun initBuiltInTheme(theme: Int) { + try { + _editingTheme.value = BuiltInTheme.fromCode(theme) + } catch (e: Exception) { + apiOnError(e) + } + } + + private fun initCustomTheme(themeId: String) { + _editingTheme.value = if (themeId.isEmpty()) { // 새로 생성한 커스텀 테마 + isNewTheme = true + CustomTheme.Default + } else { // 이미 존재하는 커스텀 테마 + try { + themeRepository.getTheme(themeId) + } catch (e: Exception) { + apiOnError(e) + CustomTheme.Default } } + _editingColors.value = + (_editingTheme.value as CustomTheme).colors.mapIndexed { idx, color -> + color.toDataWithState(idx == 0) + } } fun addColor() { From db22d6ce0b5af332b3d0cc4b3e549c69fe84b891 Mon Sep 17 00:00:00 2001 From: eastshine2741 Date: Sun, 20 Oct 2024 16:46:28 +0900 Subject: [PATCH 04/35] =?UTF-8?q?ThemeDetailViewModel.isNewTheme=EC=9D=98?= =?UTF-8?q?=20backing=20field=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../logged_in/home/settings/theme/ThemeDetailViewModel.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailViewModel.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailViewModel.kt index 8bd0c9e09..f0c4d0ee2 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailViewModel.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailViewModel.kt @@ -35,7 +35,10 @@ class ThemeDetailViewModel @Inject constructor( val currentTable = currentTableRepository.currentTable - var isNewTheme = false + val isNewTheme: Boolean get() { + val customTheme = _editingTheme.value as? CustomTheme ?: return false + return customTheme.id.isEmpty() + } init { initEditingTheme() From 8e1b8e38c14a222733cee3b6838318802e0e5122 Mon Sep 17 00:00:00 2001 From: eastshine2741 Date: Sun, 20 Oct 2024 16:50:20 +0900 Subject: [PATCH 05/35] =?UTF-8?q?null=20check=EC=9A=A9=20depth=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../settings/theme/ThemeDetailViewModel.kt | 50 +++++++++---------- .../home/settings/theme/ThemeListViewModel.kt | 8 +-- 2 files changed, 27 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailViewModel.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailViewModel.kt index f0c4d0ee2..22600d1f5 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailViewModel.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailViewModel.kt @@ -67,7 +67,6 @@ class ThemeDetailViewModel @Inject constructor( private fun initCustomTheme(themeId: String) { _editingTheme.value = if (themeId.isEmpty()) { // 새로 생성한 커스텀 테마 - isNewTheme = true CustomTheme.Default } else { // 이미 존재하는 커스텀 테마 try { @@ -119,42 +118,39 @@ class ThemeDetailViewModel @Inject constructor( } suspend fun saveTheme(name: String) { - if (_editingTheme.value is CustomTheme) { - _editingTheme.value = (_editingTheme.value as CustomTheme).id.let { id -> - if (id.isEmpty()) { - themeRepository.createTheme(name, _editingColors.value.map { it.item }) - } else { - themeRepository.updateTheme(id, name, _editingColors.value.map { it.item }) - } + val customTheme = _editingTheme.value as? CustomTheme ?: return + _editingTheme.value = customTheme.id.let { id -> + if (id.isEmpty()) { + themeRepository.createTheme(name, _editingColors.value.map { it.item }) + } else { + themeRepository.updateTheme(id, name, _editingColors.value.map { it.item }) } } } suspend fun applyThemeToCurrentTable() { - currentTable.value?.let { table -> - when (_editingTheme.value) { - is CustomTheme -> { - tableRepository.updateTableTheme( - table.id, - (_editingTheme.value as CustomTheme).id, - ) - } - - is BuiltInTheme -> { - tableRepository.updateTableTheme( - table.id, - (_editingTheme.value as BuiltInTheme).code, - ) - } + val currentTable = currentTable.value ?: return + when (_editingTheme.value) { + is CustomTheme -> { + tableRepository.updateTableTheme( + currentTable.id, + (_editingTheme.value as CustomTheme).id, + ) + } + + is BuiltInTheme -> { + tableRepository.updateTableTheme( + currentTable.id, + (_editingTheme.value as BuiltInTheme).code, + ) } } } suspend fun refreshCurrentTableIfNeeded() { // 현재 선택된 시간표의 테마라면 새로고침 - currentTable.value?.let { - if (it.themeId != null && it.themeId == (_editingTheme.value as? CustomTheme)?.id) { - tableRepository.fetchTableById(it.id) - } + val currentTable = currentTable.value ?: return + if (currentTable.themeId != null && currentTable.themeId == (_editingTheme.value as? CustomTheme)?.id) { + tableRepository.fetchTableById(currentTable.id) } } } diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeListViewModel.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeListViewModel.kt index 36a0a5c36..e97dd2dea 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeListViewModel.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeListViewModel.kt @@ -28,10 +28,10 @@ class ThemeListViewModel @Inject constructor( suspend fun deleteThemeAndRefreshTableIfNeeded(themeId: String) { // 현재 선택된 시간표의 테마라면 서버에서 변경된 색 배치를 불러옴 themeRepository.deleteTheme(themeId) - currentTable.value?.let { - if (it.themeId != null && it.themeId == themeId) { - tableRepository.fetchTableById(it.id) - } + + val currentTable = currentTable.value ?: return + if (currentTable.themeId != null && currentTable.themeId == themeId) { + tableRepository.fetchTableById(currentTable.id) } } From e073af31217950c684daa29e56600e991142dfba Mon Sep 17 00:00:00 2001 From: eastshine2741 Date: Sun, 20 Oct 2024 17:03:02 +0900 Subject: [PATCH 06/35] =?UTF-8?q?=EC=8B=9C=EA=B0=84=ED=91=9C=20=EC=83=89?= =?UTF-8?q?=EC=83=81=20=EC=83=88=EB=A1=9C=EA=B3=A0=EC=B9=A8=20=EC=A1=B0?= =?UTF-8?q?=EA=B1=B4=EB=AC=B8=20=EB=8B=A8=EC=88=9C=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../logged_in/home/settings/theme/ThemeDetailViewModel.kt | 3 ++- .../views/logged_in/home/settings/theme/ThemeListViewModel.kt | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailViewModel.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailViewModel.kt index 22600d1f5..93a1479a2 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailViewModel.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailViewModel.kt @@ -149,7 +149,8 @@ class ThemeDetailViewModel @Inject constructor( suspend fun refreshCurrentTableIfNeeded() { // 현재 선택된 시간표의 테마라면 새로고침 val currentTable = currentTable.value ?: return - if (currentTable.themeId != null && currentTable.themeId == (_editingTheme.value as? CustomTheme)?.id) { + val editingCustomTheme = _editingTheme.value as? CustomTheme ?: return + if (currentTable.themeId == editingCustomTheme.id) { tableRepository.fetchTableById(currentTable.id) } } diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeListViewModel.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeListViewModel.kt index e97dd2dea..fc09ab5f1 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeListViewModel.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeListViewModel.kt @@ -30,7 +30,7 @@ class ThemeListViewModel @Inject constructor( themeRepository.deleteTheme(themeId) val currentTable = currentTable.value ?: return - if (currentTable.themeId != null && currentTable.themeId == themeId) { + if (currentTable.themeId == themeId) { tableRepository.fetchTableById(currentTable.id) } } From c337ea9f4ce796a71b47dead97904f4d10c2550a Mon Sep 17 00:00:00 2001 From: eastshine2741 Date: Sun, 20 Oct 2024 19:40:35 +0900 Subject: [PATCH 07/35] =?UTF-8?q?=EB=8F=84=EB=A9=94=EC=9D=B8=EB=AA=A8?= =?UTF-8?q?=EB=8D=B8=20v2=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../snutt2/data/themes/ThemeRepository.kt | 7 +- .../wafflestudio/snutt2/model/TableTheme.kt | 264 ++++++++++++++++++ .../settings/theme/ThemeDetailViewModel.kt | 131 +++++---- 3 files changed, 343 insertions(+), 59 deletions(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/data/themes/ThemeRepository.kt b/app/src/main/java/com/wafflestudio/snutt2/data/themes/ThemeRepository.kt index 9d0121af2..ca70ff037 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/data/themes/ThemeRepository.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/data/themes/ThemeRepository.kt @@ -3,6 +3,7 @@ package com.wafflestudio.snutt2.data.themes import com.wafflestudio.snutt2.lib.network.dto.core.ColorDto import com.wafflestudio.snutt2.model.BuiltInTheme import com.wafflestudio.snutt2.model.CustomTheme +import com.wafflestudio.snutt2.model.CustomTheme1 import kotlinx.coroutines.flow.StateFlow interface ThemeRepository { @@ -13,11 +14,11 @@ interface ThemeRepository { suspend fun fetchThemes() - fun getTheme(themeId: String): CustomTheme + fun getTheme(themeId: String): CustomTheme1 - suspend fun createTheme(name: String, colors: List): CustomTheme + suspend fun createTheme(name: String, colors: List): CustomTheme1 - suspend fun updateTheme(themeId: String, name: String, colors: List): CustomTheme + suspend fun updateTheme(themeId: String, name: String, colors: List): CustomTheme1 suspend fun copyTheme(themeId: String) diff --git a/app/src/main/java/com/wafflestudio/snutt2/model/TableTheme.kt b/app/src/main/java/com/wafflestudio/snutt2/model/TableTheme.kt index 75dec8d41..2c5811a4e 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/model/TableTheme.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/model/TableTheme.kt @@ -4,9 +4,273 @@ import android.content.Context import androidx.compose.runtime.Composable import androidx.compose.ui.res.colorResource import com.wafflestudio.snutt2.R +import com.wafflestudio.snutt2.lib.Selectable import com.wafflestudio.snutt2.lib.network.dto.core.ColorDto import com.wafflestudio.snutt2.ui.isDarkMode +sealed class TableTheme1( + open val name: String, + private val lightColors: List, + private val darkColors: List, +) { + fun getColors(isDarkMode: Boolean) = if (isDarkMode) { + darkColors + } else { + lightColors + } + + val isEditable: Boolean get() { + return when(this) { + is CustomTheme1 -> isFromMarket + is BuiltInTheme1 -> true + } + } + + val isNew: Boolean get() { + return when(this) { + is CustomTheme1 -> id.isEmpty() + is BuiltInTheme1 -> false + } + } +} + +class CustomTheme1( + val id: String, + override val name: String, + val isFromMarket: Boolean, + colors: List, +): TableTheme1( + name = name, + lightColors = colors, + darkColors = colors, +) { + companion object { + val Default = CustomTheme1( + id = "", + name = "새 커스텀 테마", + isFromMarket = false, + colors = listOf(ColorDto(fgColor = 0xffffff, bgColor = 0x1bd0c8)) + ) + } +} + +class BuiltInTheme1( + val code: Int, + override val name: String, + lightColors: List, + darkColors: List, +): TableTheme1( + name = name, + lightColors = lightColors, + darkColors = darkColors, +) { + companion object { // FIXME: SNUTT 외 테마들에 색깔 옮겨오기 + val SNUTT = BuiltInTheme1( + code = 0, + name = "SNUTT", + lightColors = listOf( + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#E54459"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#F58D3D"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#FAC42D"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#A6D930"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#2BC267"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#1BD0C8"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#1D99E8"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#4F48C4"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#AF56B3"), + ), + darkColors = listOf( + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#D95F71"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#DF6E3C"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#E68937"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#95B03E"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#419343"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#5BA0D7"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#58C1B7"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#3E35A7"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#783891"), + ) + ) + val MODERN = BuiltInTheme1( + code = 1, + name = "모던", + lightColors = listOf( + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#E54459"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#F58D3D"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#FAC42D"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#A6D930"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#2BC267"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#1BD0C8"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#1D99E8"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#4F48C4"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#AF56B3"), + ), + darkColors = listOf( + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#D95F71"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#DF6E3C"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#E68937"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#95B03E"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#419343"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#5BA0D7"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#58C1B7"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#3E35A7"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#783891"), + ) + ) + val AUTUMN = BuiltInTheme1( + code = 2, + name = "가을", + lightColors = listOf( + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#E54459"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#F58D3D"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#FAC42D"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#A6D930"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#2BC267"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#1BD0C8"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#1D99E8"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#4F48C4"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#AF56B3"), + ), + darkColors = listOf( + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#D95F71"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#DF6E3C"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#E68937"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#95B03E"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#419343"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#5BA0D7"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#58C1B7"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#3E35A7"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#783891"), + ) + ) + val CHERRY = BuiltInTheme1( + code = 3, + name = "벚꽃", + lightColors = listOf( + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#E54459"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#F58D3D"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#FAC42D"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#A6D930"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#2BC267"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#1BD0C8"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#1D99E8"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#4F48C4"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#AF56B3"), + ), + darkColors = listOf( + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#D95F71"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#DF6E3C"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#E68937"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#95B03E"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#419343"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#5BA0D7"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#58C1B7"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#3E35A7"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#783891"), + ) + ) + val ICE = BuiltInTheme1( + code = 4, + name = "얼음", + lightColors = listOf( + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#E54459"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#F58D3D"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#FAC42D"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#A6D930"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#2BC267"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#1BD0C8"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#1D99E8"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#4F48C4"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#AF56B3"), + ), + darkColors = listOf( + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#D95F71"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#DF6E3C"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#E68937"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#95B03E"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#419343"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#5BA0D7"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#58C1B7"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#3E35A7"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#783891"), + ) + ) + val GRASS = BuiltInTheme1( + code = 5, + name = "잔디", + lightColors = listOf( + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#E54459"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#F58D3D"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#FAC42D"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#A6D930"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#2BC267"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#1BD0C8"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#1D99E8"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#4F48C4"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#AF56B3"), + ), + darkColors = listOf( + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#D95F71"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#DF6E3C"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#E68937"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#95B03E"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#419343"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#5BA0D7"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#58C1B7"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#3E35A7"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#783891"), + ) + ) + + fun fromCode(code: Int): BuiltInTheme1 { + return when (code) { + 0 -> SNUTT + 1 -> MODERN + 2 -> AUTUMN + 3 -> CHERRY + 4 -> ICE + 5 -> GRASS + else -> SNUTT + } + } + } +} + +data class EditingTheme( + val name: String, + val colors: List>, + val isEditable: Boolean, + val isNew: Boolean, +) { + fun toCustomTheme(id: String): CustomTheme1 { + if (isEditable.not()) { + throw RuntimeException("Attempting to edit a theme that cannot be edited.") + } + return CustomTheme1( + id = id, + name = name, + isFromMarket = false, + colors = colors.map { it.item }, + ) + } + + companion object { + fun fromTableTheme(tableTheme: TableTheme1, isDarkMode: Boolean): EditingTheme { + return EditingTheme( + name = tableTheme.name, + colors = tableTheme.getColors(isDarkMode).mapIndexed { index, colorDto -> + Selectable(colorDto, tableTheme.isEditable && index == 0) + }, + isEditable = tableTheme.isEditable, + isNew = tableTheme.isNew + ) + } + } +} + + +// ====== old ====== + abstract class TableTheme( open val name: String, ) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailViewModel.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailViewModel.kt index 93a1479a2..33c2e26f8 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailViewModel.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailViewModel.kt @@ -10,8 +10,11 @@ import com.wafflestudio.snutt2.lib.network.ApiOnError import com.wafflestudio.snutt2.lib.network.dto.core.ColorDto import com.wafflestudio.snutt2.lib.toDataWithState import com.wafflestudio.snutt2.model.BuiltInTheme +import com.wafflestudio.snutt2.model.BuiltInTheme1 import com.wafflestudio.snutt2.model.CustomTheme -import com.wafflestudio.snutt2.model.TableTheme +import com.wafflestudio.snutt2.model.CustomTheme1 +import com.wafflestudio.snutt2.model.EditingTheme +import com.wafflestudio.snutt2.model.TableTheme1 import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -26,8 +29,8 @@ class ThemeDetailViewModel @Inject constructor( private val apiOnError: ApiOnError, ) : ViewModel() { - private val _editingTheme = MutableStateFlow(CustomTheme.Default) - val editingTheme: StateFlow get() = _editingTheme + private val _editingTheme = MutableStateFlow() + val editingTheme: StateFlow get() = _editingTheme // 색상별 id가 없어 expanded 여부를 설정할 수 없으므로 List>로 따로 관리한다 private val _editingColors = MutableStateFlow>>(emptyList()) @@ -35,122 +38,138 @@ class ThemeDetailViewModel @Inject constructor( val currentTable = currentTableRepository.currentTable - val isNewTheme: Boolean get() { - val customTheme = _editingTheme.value as? CustomTheme ?: return false - return customTheme.id.isEmpty() - } - - init { - initEditingTheme() - } + private lateinit var originalTheme: TableTheme1 - private fun initEditingTheme() { + fun initEditingTheme(isDarkMode: Boolean) { val themeId = savedStateHandle.get("themeId") val theme = savedStateHandle.get("theme") if (theme == null || themeId == null) return if (theme != -1) { // 기본 제공 테마 - initBuiltInTheme(theme) + initBuiltInTheme(theme, isDarkMode) } else { // 커스텀 테마 - initCustomTheme(themeId) + initCustomTheme(themeId, isDarkMode) } } - private fun initBuiltInTheme(theme: Int) { + private fun initBuiltInTheme(theme: Int, isDarkMode: Boolean) { try { - _editingTheme.value = BuiltInTheme.fromCode(theme) + originalTheme = BuiltInTheme1.fromCode(theme) + _editingTheme.value = EditingTheme.fromTableTheme(originalTheme, isDarkMode) } catch (e: Exception) { apiOnError(e) } } - private fun initCustomTheme(themeId: String) { - _editingTheme.value = if (themeId.isEmpty()) { // 새로 생성한 커스텀 테마 - CustomTheme.Default + private fun initCustomTheme(themeId: String, isDarkMode: Boolean) { + originalTheme = if (themeId.isEmpty()) { // 새로 생성한 커스텀 테마 + CustomTheme1.Default } else { // 이미 존재하는 커스텀 테마 try { themeRepository.getTheme(themeId) } catch (e: Exception) { apiOnError(e) - CustomTheme.Default + CustomTheme1.Default } } - _editingColors.value = - (_editingTheme.value as CustomTheme).colors.mapIndexed { idx, color -> - color.toDataWithState(idx == 0) - } + _editingTheme.value = EditingTheme.fromTableTheme(originalTheme, isDarkMode) } fun addColor() { - _editingColors.value = _editingColors.value.toMutableList().apply { + if (editingTheme.value.isEditable.not()) return + + val newColors = _editingTheme.value.colors.toMutableList().apply { add(ColorDto(fgColor = 0xffffff, bgColor = 0x1bd0c8).toDataWithState(true)) } + _editingTheme.value = editingTheme.value.copy( + colors = newColors + ) } fun removeColor(index: Int) { - _editingColors.value = _editingColors.value.toMutableList().apply { + if (editingTheme.value.isEditable.not()) return + + val newColors = editingTheme.value.colors.toMutableList().apply { removeAt(index) } + _editingTheme.value = editingTheme.value.copy( + colors = newColors + ) } fun updateColor(index: Int, fgColor: Int, bgColor: Int) { - _editingColors.value = _editingColors.value.toMutableList().apply { + if (editingTheme.value.isEditable.not()) return + + val newColors = editingTheme.value.colors.toMutableList().apply { set(index, ColorDto(fgColor, bgColor).toDataWithState(get(index).state)) } + _editingTheme.value = editingTheme.value.copy( + colors = newColors + ) } fun duplicateColor(index: Int) { - _editingColors.value = _editingColors.value.toMutableList().apply { + if (editingTheme.value.isEditable.not()) return + + val newColors = editingTheme.value.colors.toMutableList().apply { add(index + 1, get(index).copy(state = false)) } + _editingTheme.value = editingTheme.value.copy( + colors = newColors + ) } fun toggleColorExpanded(index: Int) { - _editingColors.value = _editingColors.value.toMutableList().apply { + if (editingTheme.value.isEditable.not()) return + + val newColors = editingTheme.value.colors.toMutableList().apply { set(index, get(index).run { copy(state = !state) }) } + _editingTheme.value = editingTheme.value.copy( + colors = newColors + ) } - fun hasChange(name: String): Boolean { - return name != _editingTheme.value.name || - (_editingTheme.value is CustomTheme && _editingColors.value.map { it.item } != (_editingTheme.value as CustomTheme).colors) + fun hasChange(): Boolean { + return if (originalTheme.isEditable) { + val originalCustomTheme = originalTheme as? CustomTheme1 ?: return false + val editedCustomTheme = editingTheme.value.toCustomTheme(originalCustomTheme.id) + + return originalTheme.name != editedCustomTheme.name || + originalTheme.getColors(false) != editedCustomTheme.getColors(false) + } else { + false + } } - suspend fun saveTheme(name: String) { - val customTheme = _editingTheme.value as? CustomTheme ?: return - _editingTheme.value = customTheme.id.let { id -> - if (id.isEmpty()) { - themeRepository.createTheme(name, _editingColors.value.map { it.item }) - } else { - themeRepository.updateTheme(id, name, _editingColors.value.map { it.item }) - } + suspend fun saveTheme() { + if (editingTheme.value.isEditable.not()) return + val originalCustomTheme = originalTheme as? CustomTheme1 ?: return + val editedCustomTheme = editingTheme.value.toCustomTheme(originalCustomTheme.id) + + originalTheme = if (originalCustomTheme.isNew) { + themeRepository.createTheme(editedCustomTheme.name, editedCustomTheme.getColors(false)) + } else { + themeRepository.updateTheme(editedCustomTheme.id, editedCustomTheme.name, editedCustomTheme.getColors(false)) } } suspend fun applyThemeToCurrentTable() { val currentTable = currentTable.value ?: return - when (_editingTheme.value) { - is CustomTheme -> { - tableRepository.updateTableTheme( - currentTable.id, - (_editingTheme.value as CustomTheme).id, - ) - } + val originalCustomTheme = originalTheme as? CustomTheme1 ?: return - is BuiltInTheme -> { - tableRepository.updateTableTheme( - currentTable.id, - (_editingTheme.value as BuiltInTheme).code, - ) - } - } + tableRepository.updateTableTheme( + currentTable.id, + originalCustomTheme.id + ) } suspend fun refreshCurrentTableIfNeeded() { // 현재 선택된 시간표의 테마라면 새로고침 val currentTable = currentTable.value ?: return - val editingCustomTheme = _editingTheme.value as? CustomTheme ?: return - if (currentTable.themeId == editingCustomTheme.id) { + val originalCustomTheme = originalTheme as? CustomTheme1 ?: return + + if (currentTable.themeId == originalCustomTheme.id) { tableRepository.fetchTableById(currentTable.id) } } From 22476392cfcf7119f515f78fad8fbf99dcfbdfae Mon Sep 17 00:00:00 2001 From: eastshine2741 Date: Sat, 16 Nov 2024 16:26:50 +0900 Subject: [PATCH 08/35] =?UTF-8?q?=EB=B9=84=EC=A6=88=EB=8B=88=EC=8A=A4=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wafflestudio/snutt2/model/TableTheme.kt | 85 ++++++---- .../settings/theme/ThemeDetailViewModel.kt | 149 +++++++++++------- .../logged_in/home/timetable/TableState.kt | 4 +- 3 files changed, 146 insertions(+), 92 deletions(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/model/TableTheme.kt b/app/src/main/java/com/wafflestudio/snutt2/model/TableTheme.kt index 2c5811a4e..2531d855b 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/model/TableTheme.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/model/TableTheme.kt @@ -6,6 +6,7 @@ import androidx.compose.ui.res.colorResource import com.wafflestudio.snutt2.R import com.wafflestudio.snutt2.lib.Selectable import com.wafflestudio.snutt2.lib.network.dto.core.ColorDto +import com.wafflestudio.snutt2.lib.network.dto.core.TableDto import com.wafflestudio.snutt2.ui.isDarkMode sealed class TableTheme1( @@ -19,19 +20,21 @@ sealed class TableTheme1( lightColors } - val isEditable: Boolean get() { - return when(this) { - is CustomTheme1 -> isFromMarket - is BuiltInTheme1 -> true + val isEditable: Boolean + get() { + return when (this) { + is CustomTheme1 -> isFromMarket + is BuiltInTheme1 -> true + } } - } - val isNew: Boolean get() { - return when(this) { - is CustomTheme1 -> id.isEmpty() - is BuiltInTheme1 -> false + val isNew: Boolean + get() { + return when (this) { + is CustomTheme1 -> id.isEmpty() + is BuiltInTheme1 -> false + } } - } } class CustomTheme1( @@ -39,17 +42,19 @@ class CustomTheme1( override val name: String, val isFromMarket: Boolean, colors: List, -): TableTheme1( +) : TableTheme1( name = name, lightColors = colors, darkColors = colors, ) { + fun isAppliedToTable(table: TableDto): Boolean = table.themeId == this.id + companion object { val Default = CustomTheme1( id = "", name = "새 커스텀 테마", isFromMarket = false, - colors = listOf(ColorDto(fgColor = 0xffffff, bgColor = 0x1bd0c8)) + colors = listOf(ColorDto(fgColor = 0xffffff, bgColor = 0x1bd0c8)), ) } } @@ -59,7 +64,7 @@ class BuiltInTheme1( override val name: String, lightColors: List, darkColors: List, -): TableTheme1( +) : TableTheme1( name = name, lightColors = lightColors, darkColors = darkColors, @@ -89,7 +94,7 @@ class BuiltInTheme1( ColorDto(fgRaw = "#FFFFFF", bgRaw = "#58C1B7"), ColorDto(fgRaw = "#FFFFFF", bgRaw = "#3E35A7"), ColorDto(fgRaw = "#FFFFFF", bgRaw = "#783891"), - ) + ), ) val MODERN = BuiltInTheme1( code = 1, @@ -115,7 +120,7 @@ class BuiltInTheme1( ColorDto(fgRaw = "#FFFFFF", bgRaw = "#58C1B7"), ColorDto(fgRaw = "#FFFFFF", bgRaw = "#3E35A7"), ColorDto(fgRaw = "#FFFFFF", bgRaw = "#783891"), - ) + ), ) val AUTUMN = BuiltInTheme1( code = 2, @@ -141,7 +146,7 @@ class BuiltInTheme1( ColorDto(fgRaw = "#FFFFFF", bgRaw = "#58C1B7"), ColorDto(fgRaw = "#FFFFFF", bgRaw = "#3E35A7"), ColorDto(fgRaw = "#FFFFFF", bgRaw = "#783891"), - ) + ), ) val CHERRY = BuiltInTheme1( code = 3, @@ -167,7 +172,7 @@ class BuiltInTheme1( ColorDto(fgRaw = "#FFFFFF", bgRaw = "#58C1B7"), ColorDto(fgRaw = "#FFFFFF", bgRaw = "#3E35A7"), ColorDto(fgRaw = "#FFFFFF", bgRaw = "#783891"), - ) + ), ) val ICE = BuiltInTheme1( code = 4, @@ -193,7 +198,7 @@ class BuiltInTheme1( ColorDto(fgRaw = "#FFFFFF", bgRaw = "#58C1B7"), ColorDto(fgRaw = "#FFFFFF", bgRaw = "#3E35A7"), ColorDto(fgRaw = "#FFFFFF", bgRaw = "#783891"), - ) + ), ) val GRASS = BuiltInTheme1( code = 5, @@ -219,7 +224,7 @@ class BuiltInTheme1( ColorDto(fgRaw = "#FFFFFF", bgRaw = "#58C1B7"), ColorDto(fgRaw = "#FFFFFF", bgRaw = "#3E35A7"), ColorDto(fgRaw = "#FFFFFF", bgRaw = "#783891"), - ) + ), ) fun fromCode(code: Int): BuiltInTheme1 { @@ -239,19 +244,35 @@ class BuiltInTheme1( data class EditingTheme( val name: String, val colors: List>, - val isEditable: Boolean, - val isNew: Boolean, + private val originalTheme: TableTheme1, + private val isDarkMode: Boolean, ) { - fun toCustomTheme(id: String): CustomTheme1 { - if (isEditable.not()) { - throw RuntimeException("Attempting to edit a theme that cannot be edited.") + val isEditable get() = originalTheme.isEditable + val isNew get() = originalTheme.isNew + val isCustomTheme get() = originalTheme is CustomTheme1 + + fun hasChange(): Boolean { + return if (originalTheme.isEditable) { + name != originalTheme.name || + colors.map { it.item } != originalTheme.getColors(isDarkMode) + } else { + false + } + } + + fun toTableTheme(): TableTheme1 { + return when (originalTheme) { + is CustomTheme1 -> { + CustomTheme1( + id = originalTheme.id, + name = name, + isFromMarket = originalTheme.isFromMarket, + colors = colors.map { it.item }, + ) + } + + is BuiltInTheme1 -> originalTheme } - return CustomTheme1( - id = id, - name = name, - isFromMarket = false, - colors = colors.map { it.item }, - ) } companion object { @@ -261,8 +282,8 @@ data class EditingTheme( colors = tableTheme.getColors(isDarkMode).mapIndexed { index, colorDto -> Selectable(colorDto, tableTheme.isEditable && index == 0) }, - isEditable = tableTheme.isEditable, - isNew = tableTheme.isNew + originalTheme = tableTheme, + isDarkMode = isDarkMode, ) } } diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailViewModel.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailViewModel.kt index 33c2e26f8..5f203d8d4 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailViewModel.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailViewModel.kt @@ -2,22 +2,23 @@ package com.wafflestudio.snutt2.views.logged_in.home.settings.theme import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import com.wafflestudio.snutt2.data.current_table.CurrentTableRepository import com.wafflestudio.snutt2.data.tables.TableRepository import com.wafflestudio.snutt2.data.themes.ThemeRepository -import com.wafflestudio.snutt2.lib.Selectable +import com.wafflestudio.snutt2.data.user.UserRepository import com.wafflestudio.snutt2.lib.network.ApiOnError import com.wafflestudio.snutt2.lib.network.dto.core.ColorDto import com.wafflestudio.snutt2.lib.toDataWithState -import com.wafflestudio.snutt2.model.BuiltInTheme import com.wafflestudio.snutt2.model.BuiltInTheme1 -import com.wafflestudio.snutt2.model.CustomTheme import com.wafflestudio.snutt2.model.CustomTheme1 import com.wafflestudio.snutt2.model.EditingTheme -import com.wafflestudio.snutt2.model.TableTheme1 +import com.wafflestudio.snutt2.views.logged_in.home.timetable.TableState import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.stateIn import javax.inject.Inject @HiltViewModel @@ -26,23 +27,42 @@ class ThemeDetailViewModel @Inject constructor( private val themeRepository: ThemeRepository, private val tableRepository: TableRepository, currentTableRepository: CurrentTableRepository, + userRepository: UserRepository, private val apiOnError: ApiOnError, ) : ViewModel() { - private val _editingTheme = MutableStateFlow() - val editingTheme: StateFlow get() = _editingTheme + private val editingTheme = MutableStateFlow(null) + private var isDarkMode = false - // 색상별 id가 없어 expanded 여부를 설정할 수 없으므로 List>로 따로 관리한다 - private val _editingColors = MutableStateFlow>>(emptyList()) - val editingColors: StateFlow>> get() = _editingColors + val themeDetailUiState = combine( + currentTableRepository.currentTable, + userRepository.tableTrimParam, + editingTheme, + ) { table, trimParam, editingTheme -> + if (editingTheme == null) { + return@combine ThemeDetailUiState.Loading + } + if (table == null) { + return@combine ThemeDetailUiState.Error + } - val currentTable = currentTableRepository.currentTable + val tableState = TableState( + table, + trimParam, + editingTheme.toTableTheme(), + ) + ThemeDetailUiState.Success( + editingTheme = editingTheme, + tableState = tableState, + ) + }.stateIn(viewModelScope, SharingStarted.Eagerly, ThemeDetailUiState.Loading) - private lateinit var originalTheme: TableTheme1 + val currentTable = currentTableRepository.currentTable fun initEditingTheme(isDarkMode: Boolean) { val themeId = savedStateHandle.get("themeId") val theme = savedStateHandle.get("theme") + this.isDarkMode = isDarkMode if (theme == null || themeId == null) return @@ -55,15 +75,15 @@ class ThemeDetailViewModel @Inject constructor( private fun initBuiltInTheme(theme: Int, isDarkMode: Boolean) { try { - originalTheme = BuiltInTheme1.fromCode(theme) - _editingTheme.value = EditingTheme.fromTableTheme(originalTheme, isDarkMode) + val originalTheme = BuiltInTheme1.fromCode(theme) + editingTheme.value = EditingTheme.fromTableTheme(originalTheme, isDarkMode) } catch (e: Exception) { apiOnError(e) } } private fun initCustomTheme(themeId: String, isDarkMode: Boolean) { - originalTheme = if (themeId.isEmpty()) { // 새로 생성한 커스텀 테마 + val originalTheme = if (themeId.isEmpty()) { // 새로 생성한 커스텀 테마 CustomTheme1.Default } else { // 이미 존재하는 커스텀 테마 try { @@ -73,104 +93,117 @@ class ThemeDetailViewModel @Inject constructor( CustomTheme1.Default } } - _editingTheme.value = EditingTheme.fromTableTheme(originalTheme, isDarkMode) + editingTheme.value = EditingTheme.fromTableTheme(originalTheme, isDarkMode) } fun addColor() { - if (editingTheme.value.isEditable.not()) return + val theme = editingTheme.value ?: return + if (theme.isEditable.not()) return - val newColors = _editingTheme.value.colors.toMutableList().apply { + val newColors = theme.colors.toMutableList().apply { add(ColorDto(fgColor = 0xffffff, bgColor = 0x1bd0c8).toDataWithState(true)) } - _editingTheme.value = editingTheme.value.copy( - colors = newColors + editingTheme.value = theme.copy( + colors = newColors, ) } fun removeColor(index: Int) { - if (editingTheme.value.isEditable.not()) return + val theme = editingTheme.value ?: return + if (theme.isEditable.not()) return - val newColors = editingTheme.value.colors.toMutableList().apply { + val newColors = theme.colors.toMutableList().apply { removeAt(index) } - _editingTheme.value = editingTheme.value.copy( - colors = newColors + editingTheme.value = theme.copy( + colors = newColors, ) } fun updateColor(index: Int, fgColor: Int, bgColor: Int) { - if (editingTheme.value.isEditable.not()) return + val theme = editingTheme.value ?: return + if (theme.isEditable.not()) return - val newColors = editingTheme.value.colors.toMutableList().apply { + val newColors = theme.colors.toMutableList().apply { set(index, ColorDto(fgColor, bgColor).toDataWithState(get(index).state)) } - _editingTheme.value = editingTheme.value.copy( - colors = newColors + editingTheme.value = theme.copy( + colors = newColors, ) } fun duplicateColor(index: Int) { - if (editingTheme.value.isEditable.not()) return + val theme = editingTheme.value ?: return + if (theme.isEditable.not()) return - val newColors = editingTheme.value.colors.toMutableList().apply { + val newColors = theme.colors.toMutableList().apply { add(index + 1, get(index).copy(state = false)) } - _editingTheme.value = editingTheme.value.copy( - colors = newColors + editingTheme.value = theme.copy( + colors = newColors, ) } fun toggleColorExpanded(index: Int) { - if (editingTheme.value.isEditable.not()) return + val theme = editingTheme.value ?: return + if (theme.isEditable.not()) return - val newColors = editingTheme.value.colors.toMutableList().apply { + val newColors = theme.colors.toMutableList().apply { set(index, get(index).run { copy(state = !state) }) } - _editingTheme.value = editingTheme.value.copy( - colors = newColors + editingTheme.value = theme.copy( + colors = newColors, ) } - fun hasChange(): Boolean { - return if (originalTheme.isEditable) { - val originalCustomTheme = originalTheme as? CustomTheme1 ?: return false - val editedCustomTheme = editingTheme.value.toCustomTheme(originalCustomTheme.id) - - return originalTheme.name != editedCustomTheme.name || - originalTheme.getColors(false) != editedCustomTheme.getColors(false) - } else { - false - } - } - suspend fun saveTheme() { - if (editingTheme.value.isEditable.not()) return - val originalCustomTheme = originalTheme as? CustomTheme1 ?: return - val editedCustomTheme = editingTheme.value.toCustomTheme(originalCustomTheme.id) + val theme = editingTheme.value?.toTableTheme() as? CustomTheme1 ?: return + if (theme.isEditable.not()) return - originalTheme = if (originalCustomTheme.isNew) { - themeRepository.createTheme(editedCustomTheme.name, editedCustomTheme.getColors(false)) + val newTheme = if (theme.isNew) { + themeRepository.createTheme(theme.name, theme.getColors(isDarkMode)) } else { - themeRepository.updateTheme(editedCustomTheme.id, editedCustomTheme.name, editedCustomTheme.getColors(false)) + themeRepository.updateTheme(theme.id, theme.name, theme.getColors(isDarkMode)) } + + editingTheme.value = EditingTheme.fromTableTheme(newTheme, isDarkMode) } suspend fun applyThemeToCurrentTable() { val currentTable = currentTable.value ?: return - val originalCustomTheme = originalTheme as? CustomTheme1 ?: return + val theme = editingTheme.value?.toTableTheme() as? CustomTheme1 ?: return tableRepository.updateTableTheme( currentTable.id, - originalCustomTheme.id + theme.id, ) } suspend fun refreshCurrentTableIfNeeded() { // 현재 선택된 시간표의 테마라면 새로고침 val currentTable = currentTable.value ?: return - val originalCustomTheme = originalTheme as? CustomTheme1 ?: return + val theme = editingTheme.value?.toTableTheme() as? CustomTheme1 ?: return - if (currentTable.themeId == originalCustomTheme.id) { + if (theme.isAppliedToTable(currentTable)) { tableRepository.fetchTableById(currentTable.id) } } + + fun updateName(name: String) { + val theme = editingTheme.value ?: return + if (theme.isEditable.not()) return + + editingTheme.value = theme.copy( + name = name, + ) + } +} + +sealed interface ThemeDetailUiState { + class Success( + val editingTheme: EditingTheme, + val tableState: TableState, + ) : ThemeDetailUiState + + data object Error : ThemeDetailUiState + data object Loading : ThemeDetailUiState } diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/timetable/TableState.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/timetable/TableState.kt index 8f84efa3f..d7e5b87fe 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/timetable/TableState.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/timetable/TableState.kt @@ -2,12 +2,12 @@ package com.wafflestudio.snutt2.views.logged_in.home.timetable import androidx.compose.runtime.Stable import com.wafflestudio.snutt2.lib.network.dto.core.TableDto -import com.wafflestudio.snutt2.model.TableTheme +import com.wafflestudio.snutt2.model.TableTheme1 import com.wafflestudio.snutt2.model.TableTrimParam @Stable data class TableState( val table: TableDto, val trimParam: TableTrimParam, - val previewTheme: TableTheme?, + val previewTheme: TableTheme1?, ) From c77b9045f175242730edb7234711dcd932c987cd Mon Sep 17 00:00:00 2001 From: eastshine2741 Date: Sat, 16 Nov 2024 17:50:00 +0900 Subject: [PATCH 09/35] =?UTF-8?q?v2=20=EB=8F=84=EB=A9=94=EC=9D=B8=20?= =?UTF-8?q?=EB=AA=A8=EB=8D=B8=EC=9C=BC=EB=A1=9C=20=EB=8C=80=EC=B2=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../snutt2/components/compose/ThemeIcon.kt | 6 +- .../snutt2/components/view/TimetableView.kt | 1 - .../snutt2/data/themes/ThemeRepository.kt | 7 +- .../snutt2/lib/network/dto/core/ThemeDto.kt | 6 +- .../wafflestudio/snutt2/model/TableTheme.kt | 393 ++---------------- .../settings/theme/ThemeDetailViewModel.kt | 16 +- .../logged_in/home/timetable/TableState.kt | 4 +- .../logged_in/home/timetable/TimeTable.kt | 3 +- .../LectureColorSelectorPage.kt | 10 +- .../lecture_detail/LectureDetailPage.kt | 1 - 10 files changed, 60 insertions(+), 387 deletions(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/components/compose/ThemeIcon.kt b/app/src/main/java/com/wafflestudio/snutt2/components/compose/ThemeIcon.kt index f35e5c166..d7f36e9c7 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/components/compose/ThemeIcon.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/components/compose/ThemeIcon.kt @@ -25,6 +25,7 @@ import com.wafflestudio.snutt2.model.BuiltInTheme import com.wafflestudio.snutt2.model.CustomTheme import com.wafflestudio.snutt2.model.TableTheme import com.wafflestudio.snutt2.ui.SNUTTColors +import com.wafflestudio.snutt2.ui.isDarkMode import kotlin.random.Random @Composable @@ -36,8 +37,8 @@ fun ThemeIcon( Row( modifier = modifier, ) { - val colors = theme.colors.map { Color(it.bgColor!!) } - when (theme.colors.size) { + val colors = theme.getColors(isDarkMode()).map { Color(it.bgColor!!) } + when (colors.size) { 1 -> { Box( modifier = Modifier @@ -311,6 +312,7 @@ fun ThemeIconPreview() { bgColor = Random.nextInt(0x0, 0xffffff), ) }, + isFromMarket = false ), modifier = Modifier.size(80.dp), ) diff --git a/app/src/main/java/com/wafflestudio/snutt2/components/view/TimetableView.kt b/app/src/main/java/com/wafflestudio/snutt2/components/view/TimetableView.kt index f54caeffe..d61d771ee 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/components/view/TimetableView.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/components/view/TimetableView.kt @@ -229,7 +229,6 @@ class TimetableView : View { lecture.color.bgColor!! } else { BuiltInTheme.fromCode(theme).getColorByIndex( - context, lecture.colorIndex, ) }, diff --git a/app/src/main/java/com/wafflestudio/snutt2/data/themes/ThemeRepository.kt b/app/src/main/java/com/wafflestudio/snutt2/data/themes/ThemeRepository.kt index ca70ff037..9d0121af2 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/data/themes/ThemeRepository.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/data/themes/ThemeRepository.kt @@ -3,7 +3,6 @@ package com.wafflestudio.snutt2.data.themes import com.wafflestudio.snutt2.lib.network.dto.core.ColorDto import com.wafflestudio.snutt2.model.BuiltInTheme import com.wafflestudio.snutt2.model.CustomTheme -import com.wafflestudio.snutt2.model.CustomTheme1 import kotlinx.coroutines.flow.StateFlow interface ThemeRepository { @@ -14,11 +13,11 @@ interface ThemeRepository { suspend fun fetchThemes() - fun getTheme(themeId: String): CustomTheme1 + fun getTheme(themeId: String): CustomTheme - suspend fun createTheme(name: String, colors: List): CustomTheme1 + suspend fun createTheme(name: String, colors: List): CustomTheme - suspend fun updateTheme(themeId: String, name: String, colors: List): CustomTheme1 + suspend fun updateTheme(themeId: String, name: String, colors: List): CustomTheme suspend fun copyTheme(themeId: String) diff --git a/app/src/main/java/com/wafflestudio/snutt2/lib/network/dto/core/ThemeDto.kt b/app/src/main/java/com/wafflestudio/snutt2/lib/network/dto/core/ThemeDto.kt index 6a89f832d..a4fcdf674 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/lib/network/dto/core/ThemeDto.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/lib/network/dto/core/ThemeDto.kt @@ -20,12 +20,10 @@ data class ThemeDto( id = id!!, name = name ?: "", colors = colors ?: emptyList(), + isFromMarket = false // FIXME: 서버 응답에 맞게 수정 ) } else { - BuiltInTheme( - code = theme ?: 0, - name = name ?: "", - ) + BuiltInTheme.fromCode(theme ?: 0) } } } diff --git a/app/src/main/java/com/wafflestudio/snutt2/model/TableTheme.kt b/app/src/main/java/com/wafflestudio/snutt2/model/TableTheme.kt index 2531d855b..bead2249a 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/model/TableTheme.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/model/TableTheme.kt @@ -1,15 +1,13 @@ package com.wafflestudio.snutt2.model -import android.content.Context import androidx.compose.runtime.Composable import androidx.compose.ui.res.colorResource -import com.wafflestudio.snutt2.R import com.wafflestudio.snutt2.lib.Selectable import com.wafflestudio.snutt2.lib.network.dto.core.ColorDto import com.wafflestudio.snutt2.lib.network.dto.core.TableDto import com.wafflestudio.snutt2.ui.isDarkMode -sealed class TableTheme1( +sealed class TableTheme( open val name: String, private val lightColors: List, private val darkColors: List, @@ -23,26 +21,26 @@ sealed class TableTheme1( val isEditable: Boolean get() { return when (this) { - is CustomTheme1 -> isFromMarket - is BuiltInTheme1 -> true + is CustomTheme -> isFromMarket + is BuiltInTheme -> true } } val isNew: Boolean get() { return when (this) { - is CustomTheme1 -> id.isEmpty() - is BuiltInTheme1 -> false + is CustomTheme -> id.isEmpty() + is BuiltInTheme -> false } } } -class CustomTheme1( +class CustomTheme( val id: String, override val name: String, val isFromMarket: Boolean, colors: List, -) : TableTheme1( +) : TableTheme( name = name, lightColors = colors, darkColors = colors, @@ -50,7 +48,7 @@ class CustomTheme1( fun isAppliedToTable(table: TableDto): Boolean = table.themeId == this.id companion object { - val Default = CustomTheme1( + val Default = CustomTheme( id = "", name = "새 커스텀 테마", isFromMarket = false, @@ -59,18 +57,27 @@ class CustomTheme1( } } -class BuiltInTheme1( +class BuiltInTheme( val code: Int, override val name: String, lightColors: List, darkColors: List, -) : TableTheme1( +) : TableTheme( name = name, lightColors = lightColors, darkColors = darkColors, ) { + fun getColorByIndex(colorIndex: Long): Int { + return getColors(false)[colorIndex.toInt() - 1].bgColor ?: 0xffffff + } + + @Composable + fun getColorByIndexComposable(colorIndex: Long): androidx.compose.ui.graphics.Color { + return androidx.compose.ui.graphics.Color(getColors(isDarkMode())[colorIndex.toInt() - 1].bgColor ?: 0xffffff) + } + companion object { // FIXME: SNUTT 외 테마들에 색깔 옮겨오기 - val SNUTT = BuiltInTheme1( + val SNUTT = BuiltInTheme( code = 0, name = "SNUTT", lightColors = listOf( @@ -96,7 +103,7 @@ class BuiltInTheme1( ColorDto(fgRaw = "#FFFFFF", bgRaw = "#783891"), ), ) - val MODERN = BuiltInTheme1( + val MODERN = BuiltInTheme( code = 1, name = "모던", lightColors = listOf( @@ -122,7 +129,7 @@ class BuiltInTheme1( ColorDto(fgRaw = "#FFFFFF", bgRaw = "#783891"), ), ) - val AUTUMN = BuiltInTheme1( + val AUTUMN = BuiltInTheme( code = 2, name = "가을", lightColors = listOf( @@ -148,7 +155,7 @@ class BuiltInTheme1( ColorDto(fgRaw = "#FFFFFF", bgRaw = "#783891"), ), ) - val CHERRY = BuiltInTheme1( + val CHERRY = BuiltInTheme( code = 3, name = "벚꽃", lightColors = listOf( @@ -174,7 +181,7 @@ class BuiltInTheme1( ColorDto(fgRaw = "#FFFFFF", bgRaw = "#783891"), ), ) - val ICE = BuiltInTheme1( + val ICE = BuiltInTheme( code = 4, name = "얼음", lightColors = listOf( @@ -200,7 +207,7 @@ class BuiltInTheme1( ColorDto(fgRaw = "#FFFFFF", bgRaw = "#783891"), ), ) - val GRASS = BuiltInTheme1( + val GRASS = BuiltInTheme( code = 5, name = "잔디", lightColors = listOf( @@ -227,7 +234,7 @@ class BuiltInTheme1( ), ) - fun fromCode(code: Int): BuiltInTheme1 { + fun fromCode(code: Int): BuiltInTheme { return when (code) { 0 -> SNUTT 1 -> MODERN @@ -244,12 +251,12 @@ class BuiltInTheme1( data class EditingTheme( val name: String, val colors: List>, - private val originalTheme: TableTheme1, + private val originalTheme: TableTheme, private val isDarkMode: Boolean, ) { val isEditable get() = originalTheme.isEditable val isNew get() = originalTheme.isNew - val isCustomTheme get() = originalTheme is CustomTheme1 + val isCustomTheme get() = originalTheme is CustomTheme fun hasChange(): Boolean { return if (originalTheme.isEditable) { @@ -260,10 +267,10 @@ data class EditingTheme( } } - fun toTableTheme(): TableTheme1 { + fun toTableTheme(): TableTheme { return when (originalTheme) { - is CustomTheme1 -> { - CustomTheme1( + is CustomTheme -> { + CustomTheme( id = originalTheme.id, name = name, isFromMarket = originalTheme.isFromMarket, @@ -271,12 +278,12 @@ data class EditingTheme( ) } - is BuiltInTheme1 -> originalTheme + is BuiltInTheme -> originalTheme } } companion object { - fun fromTableTheme(tableTheme: TableTheme1, isDarkMode: Boolean): EditingTheme { + fun fromTableTheme(tableTheme: TableTheme, isDarkMode: Boolean): EditingTheme { return EditingTheme( name = tableTheme.name, colors = tableTheme.getColors(isDarkMode).mapIndexed { index, colorDto -> @@ -288,337 +295,3 @@ data class EditingTheme( } } } - - -// ====== old ====== - -abstract class TableTheme( - open val name: String, -) - -data class CustomTheme( - val id: String, - override val name: String, - val colors: List, -) : TableTheme(name) { - - companion object { - val Default = CustomTheme( - id = "", - name = "새 커스텀 테마", - colors = listOf(ColorDto(fgColor = 0xffffff, bgColor = 0x1bd0c8)), - ) - } -} - -data class BuiltInTheme( - val code: Int, - override val name: String, -) : TableTheme(name) { - - companion object { - val SNUTT = BuiltInTheme( - code = 0, - name = "SNUTT", - ) - val MODERN = BuiltInTheme( - code = 1, - name = "모던", - ) - val AUTUMN = BuiltInTheme( - code = 2, - name = "가을", - ) - val CHERRY = BuiltInTheme( - code = 3, - name = "벚꽃", - ) - val ICE = BuiltInTheme( - code = 4, - name = "얼음", - ) - val GRASS = BuiltInTheme( - code = 5, - name = "잔디", - ) - - fun fromCode(code: Int): BuiltInTheme { - return when (code) { - 0 -> SNUTT - 1 -> MODERN - 2 -> AUTUMN - 3 -> CHERRY - 4 -> ICE - 5 -> GRASS - else -> SNUTT - } - } - } - - fun getColorByIndex(context: Context, colorIndex: Long): Int { - return when (code) { - SNUTT.code -> listOf( - context.getColor(R.color.theme_snutt_0), - context.getColor(R.color.theme_snutt_1), - context.getColor(R.color.theme_snutt_2), - context.getColor(R.color.theme_snutt_3), - context.getColor(R.color.theme_snutt_4), - context.getColor(R.color.theme_snutt_5), - context.getColor(R.color.theme_snutt_6), - context.getColor(R.color.theme_snutt_7), - context.getColor(R.color.theme_snutt_8), - ) - - MODERN.code -> listOf( - context.getColor(R.color.theme_modern_0), - context.getColor(R.color.theme_modern_1), - context.getColor(R.color.theme_modern_2), - context.getColor(R.color.theme_modern_3), - context.getColor(R.color.theme_modern_4), - context.getColor(R.color.theme_modern_5), - context.getColor(R.color.theme_modern_6), - context.getColor(R.color.theme_modern_7), - context.getColor(R.color.theme_modern_8), - ) - - AUTUMN.code -> listOf( - context.getColor(R.color.theme_autumn_0), - context.getColor(R.color.theme_autumn_1), - context.getColor(R.color.theme_autumn_2), - context.getColor(R.color.theme_autumn_3), - context.getColor(R.color.theme_autumn_4), - context.getColor(R.color.theme_autumn_5), - context.getColor(R.color.theme_autumn_6), - context.getColor(R.color.theme_autumn_7), - context.getColor(R.color.theme_autumn_8), - ) - - CHERRY.code -> listOf( - context.getColor(R.color.theme_cherry_0), - context.getColor(R.color.theme_cherry_1), - context.getColor(R.color.theme_cherry_2), - context.getColor(R.color.theme_cherry_3), - context.getColor(R.color.theme_cherry_4), - context.getColor(R.color.theme_cherry_5), - context.getColor(R.color.theme_cherry_6), - context.getColor(R.color.theme_cherry_7), - context.getColor(R.color.theme_cherry_8), - ) - - ICE.code -> listOf( - context.getColor(R.color.theme_ice_0), - context.getColor(R.color.theme_ice_1), - context.getColor(R.color.theme_ice_2), - context.getColor(R.color.theme_ice_3), - context.getColor(R.color.theme_ice_4), - context.getColor(R.color.theme_ice_5), - context.getColor(R.color.theme_ice_6), - context.getColor(R.color.theme_ice_7), - context.getColor(R.color.theme_ice_8), - ) - - GRASS.code -> listOf( - context.getColor(R.color.theme_grass_0), - context.getColor(R.color.theme_grass_1), - context.getColor(R.color.theme_grass_2), - context.getColor(R.color.theme_grass_3), - context.getColor(R.color.theme_grass_4), - context.getColor(R.color.theme_grass_5), - context.getColor(R.color.theme_grass_6), - context.getColor(R.color.theme_grass_7), - context.getColor(R.color.theme_grass_8), - ) - - else -> { - listOf( - context.getColor(R.color.theme_snutt_0), - context.getColor(R.color.theme_snutt_1), - context.getColor(R.color.theme_snutt_2), - context.getColor(R.color.theme_snutt_3), - context.getColor(R.color.theme_snutt_4), - context.getColor(R.color.theme_snutt_5), - context.getColor(R.color.theme_snutt_6), - context.getColor(R.color.theme_snutt_7), - context.getColor(R.color.theme_snutt_8), - ) - } - }[colorIndex.toInt() - 1] - } - - @Composable - fun getColorByIndexComposable(colorIndex: Long): androidx.compose.ui.graphics.Color { - return if (isDarkMode()) { - when (code) { - SNUTT.code -> listOf( - colorResource(R.color.theme_snutt_dark_0), - colorResource(R.color.theme_snutt_dark_1), - colorResource(R.color.theme_snutt_dark_2), - colorResource(R.color.theme_snutt_dark_3), - colorResource(R.color.theme_snutt_dark_4), - colorResource(R.color.theme_snutt_dark_5), - colorResource(R.color.theme_snutt_dark_6), - colorResource(R.color.theme_snutt_dark_7), - colorResource(R.color.theme_snutt_dark_8), - ) - - MODERN.code -> listOf( - colorResource(R.color.theme_modern_dark_0), - colorResource(R.color.theme_modern_dark_1), - colorResource(R.color.theme_modern_dark_2), - colorResource(R.color.theme_modern_dark_3), - colorResource(R.color.theme_modern_dark_4), - colorResource(R.color.theme_modern_dark_5), - colorResource(R.color.theme_modern_dark_6), - colorResource(R.color.theme_modern_dark_7), - colorResource(R.color.theme_modern_dark_8), - ) - - AUTUMN.code -> listOf( - colorResource(R.color.theme_autumn_dark_0), - colorResource(R.color.theme_autumn_dark_1), - colorResource(R.color.theme_autumn_dark_2), - colorResource(R.color.theme_autumn_dark_3), - colorResource(R.color.theme_autumn_dark_4), - colorResource(R.color.theme_autumn_dark_5), - colorResource(R.color.theme_autumn_dark_6), - colorResource(R.color.theme_autumn_dark_7), - colorResource(R.color.theme_autumn_dark_8), - ) - - CHERRY.code -> listOf( - colorResource(R.color.theme_cherry_dark_0), - colorResource(R.color.theme_cherry_dark_1), - colorResource(R.color.theme_cherry_dark_2), - colorResource(R.color.theme_cherry_dark_3), - colorResource(R.color.theme_cherry_dark_4), - colorResource(R.color.theme_cherry_dark_5), - colorResource(R.color.theme_cherry_dark_6), - colorResource(R.color.theme_cherry_dark_7), - colorResource(R.color.theme_cherry_dark_8), - ) - - ICE.code -> listOf( - colorResource(R.color.theme_ice_dark_0), - colorResource(R.color.theme_ice_dark_1), - colorResource(R.color.theme_ice_dark_2), - colorResource(R.color.theme_ice_dark_3), - colorResource(R.color.theme_ice_dark_4), - colorResource(R.color.theme_ice_dark_5), - colorResource(R.color.theme_ice_dark_6), - colorResource(R.color.theme_ice_dark_7), - colorResource(R.color.theme_ice_dark_8), - ) - - GRASS.code -> listOf( - colorResource(R.color.theme_grass_dark_0), - colorResource(R.color.theme_grass_dark_1), - colorResource(R.color.theme_grass_dark_2), - colorResource(R.color.theme_grass_dark_3), - colorResource(R.color.theme_grass_dark_4), - colorResource(R.color.theme_grass_dark_5), - colorResource(R.color.theme_grass_dark_6), - colorResource(R.color.theme_grass_dark_7), - colorResource(R.color.theme_grass_dark_8), - ) - - else -> listOf( - colorResource(R.color.theme_snutt_dark_0), - colorResource(R.color.theme_snutt_dark_1), - colorResource(R.color.theme_snutt_dark_2), - colorResource(R.color.theme_snutt_dark_3), - colorResource(R.color.theme_snutt_dark_4), - colorResource(R.color.theme_snutt_dark_5), - colorResource(R.color.theme_snutt_dark_6), - colorResource(R.color.theme_snutt_dark_7), - colorResource(R.color.theme_snutt_dark_8), - ) - }[colorIndex.toInt() - 1] - } else { - when (code) { - SNUTT.code -> listOf( - colorResource(R.color.theme_snutt_0), - colorResource(R.color.theme_snutt_1), - colorResource(R.color.theme_snutt_2), - colorResource(R.color.theme_snutt_3), - colorResource(R.color.theme_snutt_4), - colorResource(R.color.theme_snutt_5), - colorResource(R.color.theme_snutt_6), - colorResource(R.color.theme_snutt_7), - colorResource(R.color.theme_snutt_8), - ) - - MODERN.code -> listOf( - colorResource(R.color.theme_modern_0), - colorResource(R.color.theme_modern_1), - colorResource(R.color.theme_modern_2), - colorResource(R.color.theme_modern_3), - colorResource(R.color.theme_modern_4), - colorResource(R.color.theme_modern_5), - colorResource(R.color.theme_modern_6), - colorResource(R.color.theme_modern_7), - colorResource(R.color.theme_modern_8), - ) - - AUTUMN.code -> listOf( - colorResource(R.color.theme_autumn_0), - colorResource(R.color.theme_autumn_1), - colorResource(R.color.theme_autumn_2), - colorResource(R.color.theme_autumn_3), - colorResource(R.color.theme_autumn_4), - colorResource(R.color.theme_autumn_5), - colorResource(R.color.theme_autumn_6), - colorResource(R.color.theme_autumn_7), - colorResource(R.color.theme_autumn_8), - ) - - CHERRY.code -> listOf( - colorResource(R.color.theme_cherry_0), - colorResource(R.color.theme_cherry_1), - colorResource(R.color.theme_cherry_2), - colorResource(R.color.theme_cherry_3), - colorResource(R.color.theme_cherry_4), - colorResource(R.color.theme_cherry_5), - colorResource(R.color.theme_cherry_6), - colorResource(R.color.theme_cherry_7), - colorResource(R.color.theme_cherry_8), - ) - - ICE.code -> listOf( - colorResource(R.color.theme_ice_0), - colorResource(R.color.theme_ice_1), - colorResource(R.color.theme_ice_2), - colorResource(R.color.theme_ice_3), - colorResource(R.color.theme_ice_4), - colorResource(R.color.theme_ice_5), - colorResource(R.color.theme_ice_6), - colorResource(R.color.theme_ice_7), - colorResource(R.color.theme_ice_8), - ) - - GRASS.code -> listOf( - colorResource(R.color.theme_grass_0), - colorResource(R.color.theme_grass_1), - colorResource(R.color.theme_grass_2), - colorResource(R.color.theme_grass_3), - colorResource(R.color.theme_grass_4), - colorResource(R.color.theme_grass_5), - colorResource(R.color.theme_grass_6), - colorResource(R.color.theme_grass_7), - colorResource(R.color.theme_grass_8), - ) - - else -> listOf( - colorResource(R.color.theme_snutt_0), - colorResource(R.color.theme_snutt_1), - colorResource(R.color.theme_snutt_2), - colorResource(R.color.theme_snutt_3), - colorResource(R.color.theme_snutt_4), - colorResource(R.color.theme_snutt_5), - colorResource(R.color.theme_snutt_6), - colorResource(R.color.theme_snutt_7), - colorResource(R.color.theme_snutt_8), - ) - }[colorIndex.toInt() - 1] - } - } -} diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailViewModel.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailViewModel.kt index 5f203d8d4..20a92332a 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailViewModel.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailViewModel.kt @@ -10,8 +10,8 @@ import com.wafflestudio.snutt2.data.user.UserRepository import com.wafflestudio.snutt2.lib.network.ApiOnError import com.wafflestudio.snutt2.lib.network.dto.core.ColorDto import com.wafflestudio.snutt2.lib.toDataWithState -import com.wafflestudio.snutt2.model.BuiltInTheme1 -import com.wafflestudio.snutt2.model.CustomTheme1 +import com.wafflestudio.snutt2.model.BuiltInTheme +import com.wafflestudio.snutt2.model.CustomTheme import com.wafflestudio.snutt2.model.EditingTheme import com.wafflestudio.snutt2.views.logged_in.home.timetable.TableState import dagger.hilt.android.lifecycle.HiltViewModel @@ -75,7 +75,7 @@ class ThemeDetailViewModel @Inject constructor( private fun initBuiltInTheme(theme: Int, isDarkMode: Boolean) { try { - val originalTheme = BuiltInTheme1.fromCode(theme) + val originalTheme = BuiltInTheme.fromCode(theme) editingTheme.value = EditingTheme.fromTableTheme(originalTheme, isDarkMode) } catch (e: Exception) { apiOnError(e) @@ -84,13 +84,13 @@ class ThemeDetailViewModel @Inject constructor( private fun initCustomTheme(themeId: String, isDarkMode: Boolean) { val originalTheme = if (themeId.isEmpty()) { // 새로 생성한 커스텀 테마 - CustomTheme1.Default + CustomTheme.Default } else { // 이미 존재하는 커스텀 테마 try { themeRepository.getTheme(themeId) } catch (e: Exception) { apiOnError(e) - CustomTheme1.Default + CustomTheme.Default } } editingTheme.value = EditingTheme.fromTableTheme(originalTheme, isDarkMode) @@ -157,7 +157,7 @@ class ThemeDetailViewModel @Inject constructor( } suspend fun saveTheme() { - val theme = editingTheme.value?.toTableTheme() as? CustomTheme1 ?: return + val theme = editingTheme.value?.toTableTheme() as? CustomTheme ?: return if (theme.isEditable.not()) return val newTheme = if (theme.isNew) { @@ -171,7 +171,7 @@ class ThemeDetailViewModel @Inject constructor( suspend fun applyThemeToCurrentTable() { val currentTable = currentTable.value ?: return - val theme = editingTheme.value?.toTableTheme() as? CustomTheme1 ?: return + val theme = editingTheme.value?.toTableTheme() as? CustomTheme ?: return tableRepository.updateTableTheme( currentTable.id, @@ -181,7 +181,7 @@ class ThemeDetailViewModel @Inject constructor( suspend fun refreshCurrentTableIfNeeded() { // 현재 선택된 시간표의 테마라면 새로고침 val currentTable = currentTable.value ?: return - val theme = editingTheme.value?.toTableTheme() as? CustomTheme1 ?: return + val theme = editingTheme.value?.toTableTheme() as? CustomTheme ?: return if (theme.isAppliedToTable(currentTable)) { tableRepository.fetchTableById(currentTable.id) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/timetable/TableState.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/timetable/TableState.kt index d7e5b87fe..8f84efa3f 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/timetable/TableState.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/timetable/TableState.kt @@ -2,12 +2,12 @@ package com.wafflestudio.snutt2.views.logged_in.home.timetable import androidx.compose.runtime.Stable import com.wafflestudio.snutt2.lib.network.dto.core.TableDto -import com.wafflestudio.snutt2.model.TableTheme1 +import com.wafflestudio.snutt2.model.TableTheme import com.wafflestudio.snutt2.model.TableTrimParam @Stable data class TableState( val table: TableDto, val trimParam: TableTrimParam, - val previewTheme: TableTheme1?, + val previewTheme: TableTheme?, ) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/timetable/TimeTable.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/timetable/TimeTable.kt index 7396df610..4df24f0e1 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/timetable/TimeTable.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/timetable/TimeTable.kt @@ -66,10 +66,11 @@ fun TimeTable( LocalTableState.current.table.lectureList.let { // 테마 미리보기용 색 배치 로직. 서버와 통일되어 있다(2024-01-12) previewTheme?.let { theme -> if (previewTheme is CustomTheme) { + val colors = theme.getColors(isDarkMode()) it.mapIndexed { idx, lecture -> lecture.copy( colorIndex = 0, - color = (theme as CustomTheme).colors[idx % previewTheme.colors.size], + color = colors[idx % colors.size], ) } } else { diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/lecture_detail/LectureColorSelectorPage.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/lecture_detail/LectureColorSelectorPage.kt index 6f8388d7c..df79ad46b 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/lecture_detail/LectureColorSelectorPage.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/lecture_detail/LectureColorSelectorPage.kt @@ -35,6 +35,7 @@ import com.wafflestudio.snutt2.R import com.wafflestudio.snutt2.components.compose.ColorCircle import com.wafflestudio.snutt2.components.compose.showColorPickerDialog import com.wafflestudio.snutt2.ui.SNUTTColors +import com.wafflestudio.snutt2.ui.isDarkMode import com.wafflestudio.snutt2.ui.onSurfaceVariant import com.wafflestudio.snutt2.views.LocalModalState @@ -51,10 +52,11 @@ fun LectureColorSelectorPage( val theme by lectureDetailViewModel.currentTableTheme.collectAsState() var customFgColor by remember { mutableStateOf(Color(lectureState.color.fgColor?.toLong() ?: 0xffffffff)) } var customBgColor by remember { mutableStateOf(Color(lectureState.color.bgColor?.toLong() ?: 0xffffffff)) } + val isDarkMode = isDarkMode() var selectedIndex by remember { // -1: 커스텀 색상. 0,1,2...: 선택된 색상의 0-based 인덱스 if (theme is CustomTheme) { - mutableIntStateOf((theme as CustomTheme).colors.indexOf(lectureState.color)) + mutableIntStateOf(theme.getColors(isDarkMode).indexOf(lectureState.color)) } else { mutableIntStateOf(lectureState.colorIndex.toInt() - 1) } @@ -68,7 +70,7 @@ fun LectureColorSelectorPage( color = if (selectedIndex == -1) { ColorDto(customFgColor.toArgb(), customBgColor.toArgb()) } else { - (theme as CustomTheme).colors[selectedIndex] + theme.getColors(isDarkMode)[selectedIndex] }, ) } else { @@ -101,7 +103,7 @@ fun LectureColorSelectorPage( } Spacer(modifier = Modifier.height(10.dp)) if (theme is CustomTheme) { - (theme as CustomTheme).colors.forEachIndexed { idx, color -> + theme.getColors(isDarkMode).forEachIndexed { idx, color -> ColorItem( color = color, title = stringResource(R.string.lecture_color_selector_page_color_item, idx + 1), @@ -115,7 +117,7 @@ fun LectureColorSelectorPage( for (colorIndex in 1L..9L) ColorItem( color = ColorDto( fgColor = 0xffffff, - bgColor = (theme as BuiltInTheme).getColorByIndex(context, colorIndex), + bgColor = (theme as BuiltInTheme).getColorByIndex(colorIndex), ), title = stringResource(R.string.lecture_color_selector_page_color_item, colorIndex), isSelected = colorIndex.toInt() - 1 == selectedIndex, diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/lecture_detail/LectureDetailPage.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/lecture_detail/LectureDetailPage.kt index 1e0786e49..55b8e8789 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/lecture_detail/LectureDetailPage.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/lecture_detail/LectureDetailPage.kt @@ -322,7 +322,6 @@ fun LectureDetailPage( ColorDto( fgColor = 0xffffff, bgColor = (tableColorTheme as BuiltInTheme).getColorByIndex( - context, editingLectureDetail.colorIndex, ), ) From 36331b70d8ebdb3046a2d8676712826ce71e041b Mon Sep 17 00:00:00 2001 From: eastshine2741 Date: Sat, 16 Nov 2024 17:52:59 +0900 Subject: [PATCH 10/35] =?UTF-8?q?ThemeDetailPage=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wafflestudio/snutt2/views/RootActivity.kt | 11 +- .../home/settings/theme/ThemeDetailPage.kt | 478 ---------------- .../home/settings/theme/ThemeDetailScreen.kt | 531 ++++++++++++++++++ 3 files changed, 535 insertions(+), 485 deletions(-) delete mode 100644 app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailPage.kt create mode 100644 app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailScreen.kt diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/RootActivity.kt b/app/src/main/java/com/wafflestudio/snutt2/views/RootActivity.kt index 3d3964f92..b57beece0 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/RootActivity.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/RootActivity.kt @@ -65,8 +65,7 @@ import com.wafflestudio.snutt2.views.logged_in.home.popups.PopupState import com.wafflestudio.snutt2.views.logged_in.home.search.SearchViewModel import com.wafflestudio.snutt2.views.logged_in.home.settings.* import com.wafflestudio.snutt2.views.logged_in.home.settings.theme.ThemeConfigPage -import com.wafflestudio.snutt2.views.logged_in.home.settings.theme.ThemeDetailPage -import com.wafflestudio.snutt2.views.logged_in.home.settings.theme.ThemeDetailViewModel +import com.wafflestudio.snutt2.views.logged_in.home.settings.theme.ThemeDetailRoute import com.wafflestudio.snutt2.views.logged_in.home.settings.theme.ThemeListViewModel import com.wafflestudio.snutt2.views.logged_in.lecture_detail.LectureColorSelectorPage import com.wafflestudio.snutt2.views.logged_in.lecture_detail.LectureDetailPage @@ -314,11 +313,9 @@ class RootActivity : AppCompatActivity() { defaultValue = -1 }, ), - ) { backStackEntry -> - val themeDetailViewModel = - hiltViewModel(backStackEntry) - ThemeDetailPage( - themeDetailViewModel = themeDetailViewModel, + ) { + ThemeDetailRoute( + onNavigateBack = { navController.popBackStack() }, ) } diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailPage.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailPage.kt deleted file mode 100644 index 4a77427df..000000000 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailPage.kt +++ /dev/null @@ -1,478 +0,0 @@ -package com.wafflestudio.snutt2.views.logged_in.home.settings.theme - -import androidx.activity.compose.BackHandler -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.core.MutableTransitionState -import androidx.compose.animation.expandVertically -import androidx.compose.animation.shrinkVertically -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.Divider -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.MaterialTheme -import androidx.compose.material.ModalBottomSheetValue -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.graphics.toArgb -import androidx.compose.ui.platform.LocalConfiguration -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel -import com.wafflestudio.snutt2.R -import com.wafflestudio.snutt2.components.compose.CenteredTopBar -import com.wafflestudio.snutt2.components.compose.CloseIcon -import com.wafflestudio.snutt2.components.compose.ColorBox -import com.wafflestudio.snutt2.components.compose.ColorCircle -import com.wafflestudio.snutt2.components.compose.ComposableStatesWithScope -import com.wafflestudio.snutt2.components.compose.DuplicateIcon -import com.wafflestudio.snutt2.components.compose.EditText -import com.wafflestudio.snutt2.components.compose.clicks -import com.wafflestudio.snutt2.components.compose.showColorPickerDialog -import com.wafflestudio.snutt2.lib.network.dto.core.ColorDto -import com.wafflestudio.snutt2.lib.network.dto.core.TableDto -import com.wafflestudio.snutt2.model.BuiltInTheme -import com.wafflestudio.snutt2.model.CustomTheme -import com.wafflestudio.snutt2.ui.SNUTTColors -import com.wafflestudio.snutt2.ui.SNUTTTypography -import com.wafflestudio.snutt2.ui.isDarkMode -import com.wafflestudio.snutt2.ui.onSurfaceVariant -import com.wafflestudio.snutt2.views.LocalApiOnError -import com.wafflestudio.snutt2.views.LocalApiOnProgress -import com.wafflestudio.snutt2.views.LocalModalState -import com.wafflestudio.snutt2.views.LocalNavBottomSheetState -import com.wafflestudio.snutt2.views.LocalNavController -import com.wafflestudio.snutt2.views.LocalTableState -import com.wafflestudio.snutt2.views.launchSuspendApi -import com.wafflestudio.snutt2.views.logged_in.home.settings.SettingColumn -import com.wafflestudio.snutt2.views.logged_in.home.settings.UserViewModel -import com.wafflestudio.snutt2.views.logged_in.home.timetable.TableState -import com.wafflestudio.snutt2.views.logged_in.home.timetable.TimeTable -import com.wafflestudio.snutt2.views.logged_in.home.timetable.TimetableViewModel -import kotlinx.coroutines.launch - -@OptIn(ExperimentalMaterialApi::class) -@Composable -fun ThemeDetailPage( - themeDetailViewModel: ThemeDetailViewModel = hiltViewModel(), - timetableViewModel: TimetableViewModel = hiltViewModel(), - userViewModel: UserViewModel = hiltViewModel(), -) { - val context = LocalContext.current - val scope = rememberCoroutineScope() - val navController = LocalNavController.current - val modalState = LocalModalState.current - val navBottomSheetState = LocalNavBottomSheetState.current - val apiOnError = LocalApiOnError.current - val apiOnProgress = LocalApiOnProgress.current - val composableStates = ComposableStatesWithScope(scope) - - val table by timetableViewModel.currentTable.collectAsState() - val trimParam by userViewModel.trimParam.collectAsState() - val previewTheme by timetableViewModel.previewTheme.collectAsState() - val tableState = - TableState(table ?: TableDto.Default, trimParam, previewTheme) - - val editingTheme by themeDetailViewModel.editingTheme.collectAsState() - val editingColors by themeDetailViewModel.editingColors.collectAsState() - var themeName by remember { mutableStateOf(editingTheme.name) } - - val onBackPressed: () -> Unit = { - if (themeDetailViewModel.hasChange(themeName)) { - showCancelEditDialog( - composableStates = composableStates, - cancelEdit = { - modalState.hide() - navController.popBackStack() - }, - ) - } else { - navController.popBackStack() - } - } - - BackHandler { - onBackPressed() - } - - LaunchedEffect(Unit) { - timetableViewModel.setPreviewTheme(editingTheme) - } - - Column( - modifier = Modifier - .fillMaxHeight(0.95f) - .fillMaxWidth(), - ) { - CenteredTopBar( - title = { - Text( - text = if (editingTheme is CustomTheme) { - stringResource(R.string.theme_detail_app_bar_title_custom) - } else { - stringResource(R.string.theme_detail_app_bar_title_builtin) - }, - style = SNUTTTypography.h3, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - ) - }, - navigationIcon = { - Text( - text = stringResource(R.string.common_cancel), - style = SNUTTTypography.body1, - modifier = Modifier - .clicks { - onBackPressed() - }, - ) - }, - actions = { - Text( - text = stringResource(R.string.common_save), - style = SNUTTTypography.body1, - modifier = Modifier - .clicks { - scope.launch { - launchSuspendApi(apiOnProgress, apiOnError) { - themeDetailViewModel.saveTheme(themeName) - if (themeDetailViewModel.isNewTheme) { - showApplyToCurrentTableDialog( - composableStates = composableStates, - apply = { - themeDetailViewModel.applyThemeToCurrentTable() - modalState.hide() - navController.popBackStack() - }, - avoid = { - modalState.hide() - navController.popBackStack() - }, - ) - } else { - themeDetailViewModel.refreshCurrentTableIfNeeded() - navController.popBackStack() - } - } - } - }, - ) - }, - ) - Column( - modifier = Modifier - .background(MaterialTheme.colors.background) - .verticalScroll(rememberScrollState()), - ) { - Spacer(modifier = Modifier.height(20.dp)) - ThemeDetailItem( - title = stringResource(R.string.theme_detail_theme_name), - titleColor = MaterialTheme.colors.onSurfaceVariant.copy(alpha = if (editingTheme is CustomTheme) 1f else 0.5f), - ) { - EditText( - value = themeName, - onValueChange = { themeName = it }, - enabled = editingTheme is CustomTheme, - modifier = Modifier.fillMaxWidth(), - singleLine = true, - underlineEnabled = false, - textStyle = SNUTTTypography.body1.copy( - color = if (editingTheme is CustomTheme) { - MaterialTheme.colors.onSurface - } else { - MaterialTheme.colors.onSurfaceVariant.copy( - alpha = 0.5f, - ) - }, - ), - ) - } - SettingColumn( - title = stringResource(R.string.theme_detail_theme_colors), - ) { - if (editingTheme is CustomTheme) { - editingColors.forEachIndexed { idx, colorWithExpanded -> - val state = remember { - MutableTransitionState( - navBottomSheetState.currentValue == ModalBottomSheetValue.Hidden, // 바텀시트 올라올 때에는 애니메이션 적용 안하기 위함 - ).apply { targetState = true } - } - AnimatedVisibility(state) { - Column { - ThemeDetailItem( - title = stringResource(R.string.theme_detail_color_item, idx + 1), - modifier = Modifier.clicks { - themeDetailViewModel.toggleColorExpanded(idx) - }, - actions = { - DuplicateIcon( - modifier = Modifier - .size(30.dp) - .clicks { - if (editingColors.size < 9) { - themeDetailViewModel.duplicateColor(idx) - timetableViewModel.setPreviewTheme( - (editingTheme as CustomTheme).copy( - colors = themeDetailViewModel.editingColors.value.map { it.item }, - ), - ) - } - }, - colorFilter = ColorFilter.tint( - (if (isDarkMode()) SNUTTColors.DarkGray else SNUTTColors.Gray40).copy( - alpha = if (editingColors.size < 9) 1f else 0.3f, - ), - ), - ) - Spacer(modifier = Modifier.width(8.dp)) - CloseIcon( - modifier = Modifier - .size(30.dp) - .clicks { - if (editingColors.size > 1) { - themeDetailViewModel.removeColor(idx) - timetableViewModel.setPreviewTheme( - (editingTheme as CustomTheme).copy( - colors = themeDetailViewModel.editingColors.value.map { it.item }, - ), - ) - } - }, - colorFilter = ColorFilter.tint( - (if (isDarkMode()) SNUTTColors.DarkGray else SNUTTColors.Gray40).copy( - alpha = if (editingColors.size > 1) 1f else 0.3f, - ), - ), - ) - }, - ) { - ColorBox(colorWithExpanded.item) - } - AnimatedVisibility( - visible = colorWithExpanded.state, - enter = expandVertically(), - exit = shrinkVertically(), - ) { - ColorEditItem( - fgColor = Color(colorWithExpanded.item.fgColor ?: 0xffffff), - bgColor = Color(colorWithExpanded.item.bgColor ?: 0xffffff), - onFgColorPicked = { color -> - themeDetailViewModel.updateColor( - idx, - color.toArgb(), - colorWithExpanded.item.bgColor - ?: 0xffffff, - ) - timetableViewModel.setPreviewTheme( - (editingTheme as CustomTheme).copy( - colors = themeDetailViewModel.editingColors.value.map { it.item }, - ), - ) - }, - onBgColorPicked = { color -> - themeDetailViewModel.updateColor( - idx, - colorWithExpanded.item.fgColor - ?: 0xffffff, - color.toArgb(), - ) - timetableViewModel.setPreviewTheme( - (editingTheme as CustomTheme).copy( - colors = themeDetailViewModel.editingColors.value.map { it.item }, - ), - ) - }, - ) - } - Divider(thickness = 0.5.dp, color = MaterialTheme.colors.background) - } - } - } - AnimatedVisibility(editingColors.size < 9) { - Box( - modifier = Modifier - .fillMaxWidth() - .height(44.dp) - .clicks { - themeDetailViewModel.addColor() - timetableViewModel.setPreviewTheme( - (editingTheme as CustomTheme).copy( - colors = themeDetailViewModel.editingColors.value.map { it.item }, - ), - ) - }, - ) { - Text( - text = stringResource(R.string.theme_detail_add_color), - modifier = Modifier.align(Alignment.Center), - color = MaterialTheme.colors.onBackground, - ) - } - } - } else { - (1..9).forEach { idx -> - ThemeDetailItem( - title = stringResource(R.string.theme_detail_color_item, idx), - titleColor = MaterialTheme.colors.onSurfaceVariant.copy(alpha = 0.5f), - ) { - ColorBox( - ColorDto( - fgColor = 0xffffff, - bgColor = (editingTheme as BuiltInTheme).getColorByIndex(context, idx.toLong()), - ), - ) - } - } - } - } - SettingColumn( - title = stringResource(R.string.theme_detail_preview), - ) { - Box( - modifier = Modifier - .padding(horizontal = 16.dp) - .clip(RoundedCornerShape(10.dp)) - .background(MaterialTheme.colors.surface) - .padding(15.dp) - .size( - (LocalConfiguration.current.screenWidthDp * 0.8).dp, - (LocalConfiguration.current.screenHeightDp * 0.6).dp, - ) - .align(Alignment.CenterHorizontally), - ) { - CompositionLocalProvider(LocalTableState provides tableState) { - TimeTable(selectedLecture = null, touchEnabled = false) - } - } - } - Spacer(modifier = Modifier.height(24.dp)) - } - } -} - -@Composable -fun ThemeDetailItem( - title: String, - modifier: Modifier = Modifier, - titleColor: Color = MaterialTheme.colors.onSurfaceVariant, - actions: @Composable () -> Unit = {}, - content: @Composable () -> Unit = {}, -) { - Row( - modifier = modifier - .fillMaxWidth() - .height(48.dp) - .background(MaterialTheme.colors.surface) - .padding(horizontal = 20.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - Text( - text = title, - modifier = Modifier.width(72.dp), - style = SNUTTTypography.body2.copy( - color = titleColor, - ), - ) - content() - Spacer(modifier = Modifier.weight(1f)) - actions() - } -} - -@Composable -fun ColorEditItem( - fgColor: Color, - bgColor: Color, - onFgColorPicked: (Color) -> Unit, - onBgColorPicked: (Color) -> Unit, - modifier: Modifier = Modifier, -) { - val context = LocalContext.current - val modalState = LocalModalState.current - Row( - modifier = modifier - .fillMaxWidth() - .background(color = MaterialTheme.colors.surface), - ) { - Spacer(modifier = Modifier.width(92.dp)) - Column( - modifier = Modifier.padding(top = 5.dp, bottom = 12.dp), - ) { - Row( - verticalAlignment = Alignment.CenterVertically, - ) { - Text( - text = stringResource(R.string.theme_detail_color_fg), - color = MaterialTheme.colors.onSurfaceVariant, - style = SNUTTTypography.body2, - ) - Spacer(modifier = Modifier.width(11.dp)) - ColorCircle( - color = fgColor, - modifier = Modifier - .size(25.dp) - .clicks { - showColorPickerDialog( - context = context, - modalState = modalState, - initialColor = fgColor, - onColorPicked = { color -> - onFgColorPicked(color) - }, - ) - }, - ) - } - Spacer(modifier = Modifier.height(8.dp)) - Row { - Text( - text = stringResource(R.string.theme_detail_color_bg), - color = MaterialTheme.colors.onSurfaceVariant, - style = SNUTTTypography.body2, - ) - Spacer(modifier = Modifier.width(11.dp)) - ColorCircle( - color = bgColor, - modifier = Modifier - .size(25.dp) - .clicks { - showColorPickerDialog( - context = context, - modalState = modalState, - initialColor = bgColor, - onColorPicked = { color -> - onBgColorPicked(color) - }, - ) - }, - ) - } - } - } -} diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailScreen.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailScreen.kt new file mode 100644 index 000000000..3893e085f --- /dev/null +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailScreen.kt @@ -0,0 +1,531 @@ +package com.wafflestudio.snutt2.views.logged_in.home.settings.theme + +import androidx.activity.compose.BackHandler +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.MutableTransitionState +import androidx.compose.animation.expandVertically +import androidx.compose.animation.shrinkVertically +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.Divider +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.MaterialTheme +import androidx.compose.material.ModalBottomSheetValue +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import com.wafflestudio.snutt2.R +import com.wafflestudio.snutt2.components.compose.CenteredTopBar +import com.wafflestudio.snutt2.components.compose.CloseIcon +import com.wafflestudio.snutt2.components.compose.ColorBox +import com.wafflestudio.snutt2.components.compose.ColorCircle +import com.wafflestudio.snutt2.components.compose.ComposableStatesWithScope +import com.wafflestudio.snutt2.components.compose.DuplicateIcon +import com.wafflestudio.snutt2.components.compose.EditText +import com.wafflestudio.snutt2.components.compose.clicks +import com.wafflestudio.snutt2.components.compose.showColorPickerDialog +import com.wafflestudio.snutt2.lib.network.dto.core.ColorDto +import com.wafflestudio.snutt2.ui.SNUTTColors +import com.wafflestudio.snutt2.ui.SNUTTTypography +import com.wafflestudio.snutt2.ui.isDarkMode +import com.wafflestudio.snutt2.ui.onSurfaceVariant +import com.wafflestudio.snutt2.views.LocalApiOnError +import com.wafflestudio.snutt2.views.LocalApiOnProgress +import com.wafflestudio.snutt2.views.LocalModalState +import com.wafflestudio.snutt2.views.LocalNavBottomSheetState +import com.wafflestudio.snutt2.views.LocalTableState +import com.wafflestudio.snutt2.views.launchSuspendApi +import com.wafflestudio.snutt2.views.logged_in.home.settings.SettingColumn +import com.wafflestudio.snutt2.views.logged_in.home.timetable.TimeTable +import kotlinx.coroutines.launch + +@Composable +fun ThemeDetailRoute( + onNavigateBack: () -> Unit, + themeDetailViewModel: ThemeDetailViewModel = hiltViewModel(), +) { + val scope = rememberCoroutineScope() + val apiOnError = LocalApiOnError.current + val apiOnProgress = LocalApiOnProgress.current + val modalState = LocalModalState.current + val composableStates = ComposableStatesWithScope(scope) + val isDarkMode = isDarkMode() + + val themeDetailUiState by themeDetailViewModel.themeDetailUiState.collectAsState() + + LaunchedEffect(themeDetailViewModel) { + themeDetailViewModel.initEditingTheme(isDarkMode) + } + + ThemeDetailScreen( + themeDetailUiState = themeDetailUiState, + onNavigateBack = onNavigateBack, + onUpdateName = { + themeDetailViewModel.updateName(it) + }, + onToggleColorExpanded = { + themeDetailViewModel.toggleColorExpanded(it) + }, + onDuplicateColor = { + themeDetailViewModel.duplicateColor(it) + }, + onRemoveColor = { + themeDetailViewModel.removeColor(it) + }, + onUpdateColor = { index, fgColor, bgColor -> + themeDetailViewModel.updateColor(index, fgColor, bgColor) + }, + onAddColor = { + themeDetailViewModel.addColor() + }, + onSaveTheme = { isNew -> + scope.launch { + launchSuspendApi(apiOnProgress, apiOnError) { + themeDetailViewModel.saveTheme() + when (isNew) { + true -> { + showApplyToCurrentTableDialog( + composableStates = composableStates, + apply = { + themeDetailViewModel.applyThemeToCurrentTable() + modalState.hide() + onNavigateBack() + }, + avoid = { + modalState.hide() + onNavigateBack() + } + ) + } + + false -> { + themeDetailViewModel.refreshCurrentTableIfNeeded() + onNavigateBack() + } + } + } + } + }, + ) +} + +@Composable +fun ThemeDetailScreen( + themeDetailUiState: ThemeDetailUiState, + onNavigateBack: () -> Unit, + onSaveTheme: (Boolean) -> Unit, + onUpdateName: (String) -> Unit, + onToggleColorExpanded: (Int) -> Unit, + onDuplicateColor: (Int) -> Unit, + onRemoveColor: (Int) -> Unit, + onUpdateColor: (Int, Int, Int) -> Unit, + onAddColor: () -> Unit, +) { + when (themeDetailUiState) { + is ThemeDetailUiState.Success -> { + val editingTheme = themeDetailUiState.editingTheme + val composableStates = ComposableStatesWithScope(rememberCoroutineScope()) + val modalState = LocalModalState.current + + val onBackPressed: () -> Unit = { + if (editingTheme.hasChange()) { + showCancelEditDialog( + composableStates = composableStates, + cancelEdit = { + modalState.hide() + onNavigateBack() + } + ) + } else { + onNavigateBack() + } + } + + BackHandler { + onBackPressed() + } + + Column( + modifier = Modifier + .fillMaxHeight(0.95f) + .fillMaxWidth(), + ) { + ThemeDetailTopBar( + isCustomTheme = themeDetailUiState.editingTheme.isCustomTheme, + onClickBack = onBackPressed, + onClickSave = { + onSaveTheme(editingTheme.isNew) + } + ) + Column( + modifier = Modifier + .background(MaterialTheme.colors.background) + .verticalScroll(rememberScrollState()), + ) { + Spacer(modifier = Modifier.height(20.dp)) + ThemeDetailItem( + title = stringResource(R.string.theme_detail_theme_name), + titleColor = MaterialTheme.colors.onSurfaceVariant.copy(alpha = if (editingTheme.isEditable) 1f else 0.5f), + ) { + EditText( + value = editingTheme.name, + onValueChange = onUpdateName, + enabled = editingTheme.isEditable, + modifier = Modifier.fillMaxWidth(), + singleLine = true, + underlineEnabled = false, + textStyle = SNUTTTypography.body1.copy( + color = if (editingTheme.isEditable) { + MaterialTheme.colors.onSurface + } else { + MaterialTheme.colors.onSurfaceVariant.copy( + alpha = 0.5f, + ) + }, + ), + ) + } + SettingColumn( + title = stringResource(R.string.theme_detail_theme_colors), + ) { + editingTheme.colors.forEachIndexed { idx, colorWithExpanded -> + ThemeColorRow( + index = idx, + isEditable = editingTheme.isEditable, + color = colorWithExpanded.item, + isExpanded = colorWithExpanded.state, + isDuplicateEnabled = editingTheme.colors.size < 9, + isRemoveEnabled = editingTheme.colors.size > 1, + onToggleColorExpanded = onToggleColorExpanded, + onDuplicateColor = onDuplicateColor, + onRemoveColor = onRemoveColor, + onUpdateColor = onUpdateColor + ) + } + AnimatedVisibility(editingTheme.isEditable && editingTheme.colors.size < 9) { + ThemeColorAddRow( + onClick = onAddColor + ) + } + } + SettingColumn( + title = stringResource(R.string.theme_detail_preview), + ) { + Box( + modifier = Modifier + .padding(horizontal = 16.dp) + .clip(RoundedCornerShape(10.dp)) + .background(MaterialTheme.colors.surface) + .padding(15.dp) + .size( + (LocalConfiguration.current.screenWidthDp * 0.8).dp, + (LocalConfiguration.current.screenHeightDp * 0.6).dp, + ) + .align(Alignment.CenterHorizontally), + ) { + CompositionLocalProvider(LocalTableState provides themeDetailUiState.tableState) { + TimeTable(selectedLecture = null, touchEnabled = false) + } + } + } + Spacer(modifier = Modifier.height(24.dp)) + } + } + } + + is ThemeDetailUiState.Error -> { + + } + + is ThemeDetailUiState.Loading -> { + + } + } +} + +@Composable +fun ThemeDetailItem( + title: String, + modifier: Modifier = Modifier, + titleColor: Color = MaterialTheme.colors.onSurfaceVariant, + actions: @Composable () -> Unit = {}, + content: @Composable () -> Unit = {}, +) { + Row( + modifier = modifier + .fillMaxWidth() + .height(48.dp) + .background(MaterialTheme.colors.surface) + .padding(horizontal = 20.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = title, + modifier = Modifier.width(72.dp), + style = SNUTTTypography.body2.copy( + color = titleColor, + ), + ) + content() + Spacer(modifier = Modifier.weight(1f)) + actions() + } +} + +@OptIn(ExperimentalMaterialApi::class) +@Composable +private fun ThemeColorRow( + index: Int, + isEditable: Boolean, + color: ColorDto, + isExpanded: Boolean, + isDuplicateEnabled: Boolean, + isRemoveEnabled: Boolean, + onToggleColorExpanded: (Int) -> Unit, + onDuplicateColor: (Int) -> Unit, + onRemoveColor: (Int) -> Unit, + onUpdateColor: (Int, Int, Int) -> Unit, +) { + val navBottomSheetState = LocalNavBottomSheetState.current + val state = remember { + MutableTransitionState( + navBottomSheetState.currentValue == ModalBottomSheetValue.Hidden, // 바텀시트 올라올 때에는 애니메이션 적용 안하기 위함 + ).apply { targetState = true } + } + AnimatedVisibility(state) { + Column { + ThemeDetailItem( + title = stringResource(R.string.theme_detail_color_item, index + 1), + titleColor = if (isEditable) MaterialTheme.colors.onSurfaceVariant else MaterialTheme.colors.onSurfaceVariant.copy(alpha = 0.5f), + modifier = Modifier.clicks { + if (isEditable) { + onToggleColorExpanded(index) + } + }, + actions = { + if (isEditable) { + DuplicateIcon( + modifier = Modifier + .size(30.dp) + .clicks { + if (isDuplicateEnabled) { + onDuplicateColor(index) + } + }, + colorFilter = ColorFilter.tint( + (if (isDarkMode()) SNUTTColors.DarkGray else SNUTTColors.Gray40).copy( + alpha = if (isDuplicateEnabled) 1f else 0.3f, + ), + ), + ) + Spacer(modifier = Modifier.width(8.dp)) + CloseIcon( + modifier = Modifier + .size(30.dp) + .clicks { + if (isRemoveEnabled) { + onRemoveColor(index) + } + }, + colorFilter = ColorFilter.tint( + (if (isDarkMode()) SNUTTColors.DarkGray else SNUTTColors.Gray40).copy( + alpha = if (isRemoveEnabled) 1f else 0.3f, + ), + ), + ) + } + }, + ) { + ColorBox(color) + } + AnimatedVisibility( + visible = isExpanded && isEditable, + enter = expandVertically(), + exit = shrinkVertically(), + ) { + ColorEditItem( + fgColor = Color(color.fgColor ?: 0xffffff), + bgColor = Color(color.bgColor ?: 0xffffff), + onFgColorPicked = { pickedColor -> + onUpdateColor( + index, + pickedColor.toArgb(), + color.bgColor ?: 0xffffff, + ) + }, + onBgColorPicked = { pickedColor -> + onUpdateColor( + index, + color.fgColor ?: 0xffffff, + pickedColor.toArgb(), + ) + }, + ) + } + Divider(thickness = 0.5.dp, color = MaterialTheme.colors.background) + } + } +} + +@Composable +fun ColorEditItem( + fgColor: Color, + bgColor: Color, + onFgColorPicked: (Color) -> Unit, + onBgColorPicked: (Color) -> Unit, + modifier: Modifier = Modifier, +) { + val context = LocalContext.current + val modalState = LocalModalState.current + Row( + modifier = modifier + .fillMaxWidth() + .background(color = MaterialTheme.colors.surface), + ) { + Spacer(modifier = Modifier.width(92.dp)) + Column( + modifier = Modifier.padding(top = 5.dp, bottom = 12.dp), + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = stringResource(R.string.theme_detail_color_fg), + color = MaterialTheme.colors.onSurfaceVariant, + style = SNUTTTypography.body2, + ) + Spacer(modifier = Modifier.width(11.dp)) + ColorCircle( + color = fgColor, + modifier = Modifier + .size(25.dp) + .clicks { + showColorPickerDialog( + context = context, + modalState = modalState, + initialColor = fgColor, + onColorPicked = { color -> + onFgColorPicked(color) + }, + ) + }, + ) + } + Spacer(modifier = Modifier.height(8.dp)) + Row { + Text( + text = stringResource(R.string.theme_detail_color_bg), + color = MaterialTheme.colors.onSurfaceVariant, + style = SNUTTTypography.body2, + ) + Spacer(modifier = Modifier.width(11.dp)) + ColorCircle( + color = bgColor, + modifier = Modifier + .size(25.dp) + .clicks { + showColorPickerDialog( + context = context, + modalState = modalState, + initialColor = bgColor, + onColorPicked = { color -> + onBgColorPicked(color) + }, + ) + }, + ) + } + } + } +} + +@Composable +private fun ThemeColorAddRow( + onClick: () -> Unit, +) { + Box( + modifier = Modifier + .fillMaxWidth() + .height(44.dp) + .clicks { + onClick() + }, + ) { + Text( + text = stringResource(R.string.theme_detail_add_color), + modifier = Modifier.align(Alignment.Center), + color = MaterialTheme.colors.onBackground, + ) + } +} + +@Composable +private fun ThemeDetailTopBar( + isCustomTheme: Boolean, + onClickBack: () -> Unit, + onClickSave: () -> Unit, +) { + CenteredTopBar( + title = { + Text( + text = if (isCustomTheme) { + stringResource(R.string.theme_detail_app_bar_title_custom) + } else { + stringResource(R.string.theme_detail_app_bar_title_builtin) + }, + style = SNUTTTypography.h3, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + }, + navigationIcon = { + Text( + text = stringResource(R.string.common_cancel), + style = SNUTTTypography.body1, + modifier = Modifier + .clicks { + onClickBack() + }, + ) + }, + actions = { + Text( + text = stringResource(R.string.common_save), + style = SNUTTTypography.body1, + modifier = Modifier + .clicks { + onClickSave() + }, + ) + }, + ) +} From 48a75d01feb513946c8f71024d878f6336de90c0 Mon Sep 17 00:00:00 2001 From: eastshine2741 Date: Sat, 16 Nov 2024 18:02:30 +0900 Subject: [PATCH 11/35] =?UTF-8?q?UiState=20Error,=20Loading=20=EB=8C=80?= =?UTF-8?q?=EC=9D=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../home/settings/theme/ThemeDetailScreen.kt | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailScreen.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailScreen.kt index 3893e085f..203e23717 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailScreen.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailScreen.kt @@ -19,6 +19,7 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll +import androidx.compose.material.CircularProgressIndicator import androidx.compose.material.Divider import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.MaterialTheme @@ -51,6 +52,7 @@ import com.wafflestudio.snutt2.components.compose.ColorCircle import com.wafflestudio.snutt2.components.compose.ComposableStatesWithScope import com.wafflestudio.snutt2.components.compose.DuplicateIcon import com.wafflestudio.snutt2.components.compose.EditText +import com.wafflestudio.snutt2.components.compose.LoadingIndicator import com.wafflestudio.snutt2.components.compose.clicks import com.wafflestudio.snutt2.components.compose.showColorPickerDialog import com.wafflestudio.snutt2.lib.network.dto.core.ColorDto @@ -263,11 +265,30 @@ fun ThemeDetailScreen( } is ThemeDetailUiState.Error -> { - + Box( + modifier = Modifier + .fillMaxHeight(0.95f) + .fillMaxWidth() + .background(MaterialTheme.colors.background), + contentAlignment = Alignment.Center + ) { + Text( + text = stringResource(R.string.error_unknown), + color = MaterialTheme.colors.onBackground + ) + } } is ThemeDetailUiState.Loading -> { - + Box( + modifier = Modifier + .fillMaxHeight(0.95f) + .fillMaxWidth() + .background(MaterialTheme.colors.background), + contentAlignment = Alignment.Center + ) { + CircularProgressIndicator() + } } } } From c7ad540c8560e91f01d048b458f9bc6d2d2b9db1 Mon Sep 17 00:00:00 2001 From: eastshine2741 Date: Sat, 16 Nov 2024 18:04:08 +0900 Subject: [PATCH 12/35] =?UTF-8?q?isEditable=20=EC=A1=B0=EA=B1=B4=20?= =?UTF-8?q?=EC=98=A4=ED=83=80=20=EC=88=98=EC=A0=95=20&=20SNUTT=20=EC=99=B8?= =?UTF-8?q?=20=ED=85=8C=EB=A7=88=20=EC=83=89=EC=83=81=20=EC=98=AE=EA=B2=A8?= =?UTF-8?q?=EC=98=A4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wafflestudio/snutt2/model/TableTheme.kt | 182 +++++++++--------- 1 file changed, 91 insertions(+), 91 deletions(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/model/TableTheme.kt b/app/src/main/java/com/wafflestudio/snutt2/model/TableTheme.kt index bead2249a..77786e666 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/model/TableTheme.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/model/TableTheme.kt @@ -21,7 +21,7 @@ sealed class TableTheme( val isEditable: Boolean get() { return when (this) { - is CustomTheme -> isFromMarket + is CustomTheme -> isFromMarket.not() is BuiltInTheme -> true } } @@ -76,7 +76,7 @@ class BuiltInTheme( return androidx.compose.ui.graphics.Color(getColors(isDarkMode())[colorIndex.toInt() - 1].bgColor ?: 0xffffff) } - companion object { // FIXME: SNUTT 외 테마들에 색깔 옮겨오기 + companion object { val SNUTT = BuiltInTheme( code = 0, name = "SNUTT", @@ -107,51 +107,51 @@ class BuiltInTheme( code = 1, name = "모던", lightColors = listOf( - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#E54459"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#F58D3D"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#FAC42D"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#A6D930"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#2BC267"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#1BD0C8"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#1D99E8"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#4F48C4"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#AF56B3"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#F0652A"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#F5AD3E"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#998F36"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#89C291"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#266F55"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#13808F"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#366689"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#432920"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#D82F3D"), ), darkColors = listOf( - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#D95F71"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#DF6E3C"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#E68937"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#95B03E"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#419343"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#5BA0D7"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#58C1B7"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#3E35A7"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#783891"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#BB592F"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#E08B45"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#B4B194"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#5B967C"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#266F55"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#13808F"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#426586"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#5C4335"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#AD2F31"), ), ) val AUTUMN = BuiltInTheme( code = 2, name = "가을", lightColors = listOf( - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#E54459"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#F58D3D"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#FAC42D"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#A6D930"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#2BC267"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#1BD0C8"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#1D99E8"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#4F48C4"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#AF56B3"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#B82E31"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#DB701C"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#EAA32A"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#C6C013"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#3A856E"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#19B2AC"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#3994CE"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#3F3A9C"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#924396"), ), darkColors = listOf( - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#D95F71"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#DF6E3C"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#E68937"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#95B03E"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#419343"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#5BA0D7"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#58C1B7"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#3E35A7"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#A93A36"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#D56738"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#CC973F"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#A0942F"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#4E8370"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#29625A"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#4171A2"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#4F48C4"), ColorDto(fgRaw = "#FFFFFF", bgRaw = "#783891"), ), ) @@ -159,78 +159,78 @@ class BuiltInTheme( code = 3, name = "벚꽃", lightColors = listOf( - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#E54459"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#F58D3D"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#FAC42D"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#A6D930"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#2BC267"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#1BD0C8"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#1D99E8"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#4F48C4"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#AF56B3"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#FD79A8"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#FEC9DD"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#FEB0CC"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#FE93BF"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#E9B1D0"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#C67D97"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#BB8EA7"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#BDB4BF"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#E16597"), ), darkColors = listOf( - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#D95F71"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#DF6E3C"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#E68937"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#95B03E"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#419343"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#5BA0D7"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#58C1B7"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#3E35A7"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#783891"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#A43C58"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#7C164F"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#99446E"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#A77085"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#B290B8"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#BDB4BF"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#BB8EA7"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#736C75"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#C76F92"), ), ) val ICE = BuiltInTheme( code = 4, name = "얼음", lightColors = listOf( - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#E54459"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#F58D3D"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#FAC42D"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#A6D930"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#2BC267"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#1BD0C8"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#1D99E8"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#4F48C4"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#AF56B3"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#AABDCF"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#C0E9E8"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#66B6CA"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#015F95"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#A8D0DB"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#458ED0"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#62A9D1"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#20363D"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#6D8A96"), ), darkColors = listOf( - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#D95F71"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#DF6E3C"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#E68937"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#95B03E"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#419343"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#5BA0D7"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#58C1B7"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#3E35A7"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#783891"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#014D79"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#788DA4"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#AEC1C9"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#48595B"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#1C6C8E"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#64909C"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#88B1C6"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#44576B"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#757C80"), ), ) val GRASS = BuiltInTheme( code = 5, name = "잔디", lightColors = listOf( - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#E54459"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#F58D3D"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#FAC42D"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#A6D930"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#2BC267"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#1BD0C8"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#1D99E8"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#4F48C4"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#AF56B3"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#4FBEAA"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#9FC1A4"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#5A8173"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#84AEB1"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#266F55"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#D0E0C4"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#59886D"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#476060"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#3D7068"), ), darkColors = listOf( - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#D95F71"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#DF6E3C"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#E68937"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#95B03E"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#419343"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#5BA0D7"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#58C1B7"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#3E35A7"), - ColorDto(fgRaw = "#FFFFFF", bgRaw = "#783891"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#2D5A45"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#429587"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#86A99A"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#597B6A"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#42635B"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#586C5D"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#324845"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#AAB6B1"), + ColorDto(fgRaw = "#FFFFFF", bgRaw = "#747877"), ), ) From b23ad107d0cdcf6d3111ab8504f88190274fea6b Mon Sep 17 00:00:00 2001 From: eastshine2741 Date: Sat, 16 Nov 2024 18:31:12 +0900 Subject: [PATCH 13/35] =?UTF-8?q?ThemeColorRow=20=EC=95=A0=EB=8B=88?= =?UTF-8?q?=EB=A9=94=EC=9D=B4=EC=85=98=20=EB=B2=84=EA=B7=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../logged_in/home/settings/theme/ThemeDetailScreen.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailScreen.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailScreen.kt index 203e23717..6aa70f39c 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailScreen.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailScreen.kt @@ -342,7 +342,11 @@ private fun ThemeColorRow( navBottomSheetState.currentValue == ModalBottomSheetValue.Hidden, // 바텀시트 올라올 때에는 애니메이션 적용 안하기 위함 ).apply { targetState = true } } - AnimatedVisibility(state) { + AnimatedVisibility( + visibleState = state, + enter = expandVertically(), + exit = shrinkVertically() + ) { Column { ThemeDetailItem( title = stringResource(R.string.theme_detail_color_item, index + 1), From 71dd5b61d59da8203daaac270aedb76c0c4231f8 Mon Sep 17 00:00:00 2001 From: eastshine2741 Date: Sat, 16 Nov 2024 21:36:38 +0900 Subject: [PATCH 14/35] =?UTF-8?q?isEditable=20=EC=A1=B0=EA=B1=B4=20?= =?UTF-8?q?=EC=98=A4=ED=83=80=20=EC=88=98=EC=A0=95=202?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/com/wafflestudio/snutt2/model/TableTheme.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/model/TableTheme.kt b/app/src/main/java/com/wafflestudio/snutt2/model/TableTheme.kt index 77786e666..402634634 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/model/TableTheme.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/model/TableTheme.kt @@ -22,7 +22,7 @@ sealed class TableTheme( get() { return when (this) { is CustomTheme -> isFromMarket.not() - is BuiltInTheme -> true + is BuiltInTheme -> false } } From 09f76cc7880f51458382a01e5fcbb55cdd46032b Mon Sep 17 00:00:00 2001 From: eastshine2741 Date: Sat, 16 Nov 2024 21:57:44 +0900 Subject: [PATCH 15/35] =?UTF-8?q?=EB=88=84=EB=9D=BD=EB=90=9C=20private=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../views/logged_in/home/settings/theme/ThemeDetailScreen.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailScreen.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailScreen.kt index 6aa70f39c..89c648f72 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailScreen.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailScreen.kt @@ -294,7 +294,7 @@ fun ThemeDetailScreen( } @Composable -fun ThemeDetailItem( +private fun ThemeDetailItem( title: String, modifier: Modifier = Modifier, titleColor: Color = MaterialTheme.colors.onSurfaceVariant, @@ -422,7 +422,7 @@ private fun ThemeColorRow( } @Composable -fun ColorEditItem( +private fun ColorEditItem( fgColor: Color, bgColor: Color, onFgColorPicked: (Color) -> Unit, From fd2930019a869f6ac8409847e60d3a63d682efbf Mon Sep 17 00:00:00 2001 From: eastshine2741 Date: Sat, 16 Nov 2024 21:58:06 +0900 Subject: [PATCH 16/35] =?UTF-8?q?ThemeDetail=20Preview=20=EB=AA=87=20?= =?UTF-8?q?=EA=B0=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../home/settings/theme/ThemeDetailScreen.kt | 57 ++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailScreen.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailScreen.kt index 89c648f72..c6b38784b 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailScreen.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailScreen.kt @@ -25,6 +25,7 @@ import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.MaterialTheme import androidx.compose.material.ModalBottomSheetValue import androidx.compose.material.Text +import androidx.compose.material.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect @@ -42,6 +43,7 @@ import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import com.wafflestudio.snutt2.R @@ -52,11 +54,12 @@ import com.wafflestudio.snutt2.components.compose.ColorCircle import com.wafflestudio.snutt2.components.compose.ComposableStatesWithScope import com.wafflestudio.snutt2.components.compose.DuplicateIcon import com.wafflestudio.snutt2.components.compose.EditText -import com.wafflestudio.snutt2.components.compose.LoadingIndicator import com.wafflestudio.snutt2.components.compose.clicks +import com.wafflestudio.snutt2.components.compose.rememberModalState import com.wafflestudio.snutt2.components.compose.showColorPickerDialog import com.wafflestudio.snutt2.lib.network.dto.core.ColorDto import com.wafflestudio.snutt2.ui.SNUTTColors +import com.wafflestudio.snutt2.ui.SNUTTTheme import com.wafflestudio.snutt2.ui.SNUTTTypography import com.wafflestudio.snutt2.ui.isDarkMode import com.wafflestudio.snutt2.ui.onSurfaceVariant @@ -554,3 +557,55 @@ private fun ThemeDetailTopBar( }, ) } + +@OptIn(ExperimentalMaterialApi::class) +@Preview +@Composable +private fun ThemeColorRowPreview() { + SNUTTTheme { + val modalBottomSheetState = rememberModalBottomSheetState( + initialValue = ModalBottomSheetValue.Hidden, + skipHalfExpanded = true, + ) + val modalState = rememberModalState() + CompositionLocalProvider( + LocalNavBottomSheetState provides modalBottomSheetState, + LocalModalState provides modalState, + ) { + ThemeColorRow( + index = 0, + isEditable = true, + color = ColorDto("#ffffff", "#1BD0C8"), + isExpanded = true, + isDuplicateEnabled = true, + isRemoveEnabled = true, + onToggleColorExpanded = {}, + onDuplicateColor = {}, + onRemoveColor = {}, + onUpdateColor = { _, _, _ -> } + ) + } + } +} + +@Preview +@Composable +private fun ThemeColorAddRowPreview() { + SNUTTTheme { + ThemeColorAddRow( + onClick = {} + ) + } +} + +@Preview +@Composable +private fun ThemeDetailTopBarPreview() { + SNUTTTheme { + ThemeDetailTopBar( + isCustomTheme = true, + onClickBack = {}, + onClickSave = {} + ) + } +} From 4540741dca6148b626f28289bdbcf75f0b289d71 Mon Sep 17 00:00:00 2001 From: eastshine2741 Date: Sat, 16 Nov 2024 23:17:15 +0900 Subject: [PATCH 17/35] =?UTF-8?q?ThemeListViewModel=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wafflestudio/snutt2/views/RootActivity.kt | 16 ++++++++++++++-- .../home/settings/theme/ThemeListViewModel.kt | 13 ++++++++----- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/RootActivity.kt b/app/src/main/java/com/wafflestudio/snutt2/views/RootActivity.kt index b57beece0..647f46655 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/RootActivity.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/RootActivity.kt @@ -52,6 +52,8 @@ import com.wafflestudio.snutt2.layouts.bottomsheetnavigation.ModalBottomSheetLay import com.wafflestudio.snutt2.layouts.bottomsheetnavigation.bottomSheet import com.wafflestudio.snutt2.lib.network.ApiOnError import com.wafflestudio.snutt2.lib.network.ApiOnProgress +import com.wafflestudio.snutt2.model.BuiltInTheme +import com.wafflestudio.snutt2.model.CustomTheme import com.wafflestudio.snutt2.react_native.ReactNativeBundleManager import com.wafflestudio.snutt2.ui.SNUTTColors import com.wafflestudio.snutt2.ui.SNUTTTheme @@ -64,7 +66,7 @@ import com.wafflestudio.snutt2.views.logged_in.home.TableListViewModel import com.wafflestudio.snutt2.views.logged_in.home.popups.PopupState import com.wafflestudio.snutt2.views.logged_in.home.search.SearchViewModel import com.wafflestudio.snutt2.views.logged_in.home.settings.* -import com.wafflestudio.snutt2.views.logged_in.home.settings.theme.ThemeConfigPage +import com.wafflestudio.snutt2.views.logged_in.home.settings.theme.ThemeConfigRoute import com.wafflestudio.snutt2.views.logged_in.home.settings.theme.ThemeDetailRoute import com.wafflestudio.snutt2.views.logged_in.home.settings.theme.ThemeListViewModel import com.wafflestudio.snutt2.views.logged_in.lecture_detail.LectureColorSelectorPage @@ -434,8 +436,18 @@ class RootActivity : AppCompatActivity() { navController.getBackStackEntry(NavigationDestination.Home) } val themeListViewModel = hiltViewModel(parentEntry) - ThemeConfigPage( + ThemeConfigRoute( themeListViewModel = themeListViewModel, + onNavigateBack = { navController.popBackStack() }, + onNavigateToThemeDetail = { theme -> + when (theme) { + is CustomTheme -> navController.navigate("${NavigationDestination.ThemeDetail}?themeId=${theme.id}") + is BuiltInTheme -> navController.navigate("${NavigationDestination.ThemeDetail}?theme=${theme.code}") + } + }, + onClickAddTheme = { + navController.navigate(NavigationDestination.ThemeDetail) + } ) } if (BuildConfig.DEBUG) composable2(NavigationDestination.NetworkLog) { NetworkLogPage() } diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeListViewModel.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeListViewModel.kt index fc09ab5f1..cb1982c15 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeListViewModel.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeListViewModel.kt @@ -6,6 +6,7 @@ import com.wafflestudio.snutt2.data.tables.TableRepository import com.wafflestudio.snutt2.data.themes.ThemeRepository import com.wafflestudio.snutt2.model.BuiltInTheme import com.wafflestudio.snutt2.model.CustomTheme +import com.wafflestudio.snutt2.model.TableTheme import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.StateFlow import javax.inject.Inject @@ -26,16 +27,18 @@ class ThemeListViewModel @Inject constructor( themeRepository.fetchThemes() } - suspend fun deleteThemeAndRefreshTableIfNeeded(themeId: String) { // 현재 선택된 시간표의 테마라면 서버에서 변경된 색 배치를 불러옴 - themeRepository.deleteTheme(themeId) + suspend fun deleteThemeAndRefreshTableIfNeeded(theme: TableTheme) { // 현재 선택된 시간표의 테마라면 서버에서 변경된 색 배치를 불러옴 + if (theme !is CustomTheme) return + themeRepository.deleteTheme(theme.id) val currentTable = currentTable.value ?: return - if (currentTable.themeId == themeId) { + if (theme.isAppliedToTable(currentTable)) { tableRepository.fetchTableById(currentTable.id) } } - suspend fun copyTheme(themeId: String) { - themeRepository.copyTheme(themeId) + suspend fun copyTheme(theme: TableTheme) { + if (theme !is CustomTheme) return + themeRepository.copyTheme(theme.id) } } From 405cf211d8da60d632e0e03be524a36458f574c9 Mon Sep 17 00:00:00 2001 From: eastshine2741 Date: Sat, 16 Nov 2024 23:18:11 +0900 Subject: [PATCH 18/35] =?UTF-8?q?ThemeConfigScreen=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...hemeConfigPage.kt => ThemeConfigScreen.kt} | 259 ++++++++++-------- 1 file changed, 141 insertions(+), 118 deletions(-) rename app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/{ThemeConfigPage.kt => ThemeConfigScreen.kt} (62%) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigPage.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigScreen.kt similarity index 62% rename from app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigPage.kt rename to app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigScreen.kt index f163f4462..153b2afb7 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigPage.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigScreen.kt @@ -2,8 +2,6 @@ package com.wafflestudio.snutt2.views.logged_in.home.settings.theme import androidx.activity.compose.BackHandler import androidx.compose.foundation.background -import androidx.compose.foundation.horizontalScroll -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -55,6 +53,8 @@ import com.wafflestudio.snutt2.components.compose.QuestionCircleIcon import com.wafflestudio.snutt2.components.compose.SimpleTopBar import com.wafflestudio.snutt2.components.compose.ThemeIcon import com.wafflestudio.snutt2.components.compose.clicks +import com.wafflestudio.snutt2.model.BuiltInTheme +import com.wafflestudio.snutt2.model.CustomTheme import com.wafflestudio.snutt2.model.TableTheme import com.wafflestudio.snutt2.ui.SNUTTColors import com.wafflestudio.snutt2.ui.SNUTTTypography @@ -63,33 +63,69 @@ import com.wafflestudio.snutt2.ui.onSurfaceVariant import com.wafflestudio.snutt2.views.LocalApiOnError import com.wafflestudio.snutt2.views.LocalApiOnProgress import com.wafflestudio.snutt2.views.LocalModalState -import com.wafflestudio.snutt2.views.LocalNavController -import com.wafflestudio.snutt2.views.NavigationDestination import com.wafflestudio.snutt2.views.launchSuspendApi import com.wafflestudio.snutt2.views.logged_in.home.settings.SettingColumn import kotlinx.coroutines.launch -@OptIn(ExperimentalMaterialApi::class) @Composable -fun ThemeConfigPage( +fun ThemeConfigRoute( + onNavigateBack: () -> Unit, + onNavigateToThemeDetail: (TableTheme) -> Unit, + onClickAddTheme: () -> Unit, themeListViewModel: ThemeListViewModel = hiltViewModel(), ) { - val navController = LocalNavController.current - val modalState = LocalModalState.current val apiOnError = LocalApiOnError.current val apiOnProgress = LocalApiOnProgress.current - val bottomSheet = BottomSheet() - val scope = rememberCoroutineScope() - val composableStates = ComposableStatesWithScope(scope) val customThemes by themeListViewModel.customThemes.collectAsState() val builtInThemes by themeListViewModel.builtInThemes.collectAsState() + ThemeConfigScreen( + customThemes = customThemes, + builtInThemes = builtInThemes, + onNavigateBack = onNavigateBack, + onFetchThemes = { + launchSuspendApi(apiOnProgress, apiOnError) { + themeListViewModel.fetchThemes() + } + }, + onNavigateToDetail = onNavigateToThemeDetail, + onClickAddTheme = onClickAddTheme, + onDuplicateTheme = { + launchSuspendApi(apiOnProgress, apiOnError) { + themeListViewModel.copyTheme(it) + } + }, + onDeleteTheme = { + launchSuspendApi(apiOnProgress, apiOnError) { + themeListViewModel.deleteThemeAndRefreshTableIfNeeded(it) + } + }, + ) +} + +@OptIn(ExperimentalMaterialApi::class) +@Composable +fun ThemeConfigScreen( + customThemes: List, + builtInThemes: List, + onNavigateBack: () -> Unit, + onFetchThemes: suspend () -> Unit, + onNavigateToDetail: (TableTheme) -> Unit, + onClickAddTheme: () -> Unit, + onDuplicateTheme: suspend (TableTheme) -> Unit, + onDeleteTheme: suspend (TableTheme) -> Unit, +) { + val modalState = LocalModalState.current + val bottomSheet = BottomSheet() + val scope = rememberCoroutineScope() + val composableStates = ComposableStatesWithScope(scope) + val onBackPressed: () -> Unit = { if (bottomSheet.isVisible) { scope.launch { bottomSheet.hide() } } else { - navController.popBackStack() + onNavigateBack() } } @@ -98,9 +134,7 @@ fun ThemeConfigPage( } LaunchedEffect(Unit) { - runCatching { - themeListViewModel.fetchThemes() - }.onFailure(apiOnError) + onFetchThemes() } ModalBottomSheetLayout( @@ -124,113 +158,53 @@ fun ThemeConfigPage( .background(MaterialTheme.colors.background) .verticalScroll(rememberScrollState()), ) { - SettingColumn( + ThemesRow( title = stringResource(R.string.theme_config_custom_theme), - titleStyle = SNUTTTypography.body2.copy( - color = MaterialTheme.colors.onSurfaceVariant, - fontSize = 13.sp, - ), - ) { - LazyRow( - modifier = Modifier - .fillMaxWidth() - .background(MaterialTheme.colors.surface) - .padding(top = 20.dp, bottom = 12.dp), - horizontalArrangement = Arrangement.Start, - ) { - item { - Spacer(modifier = Modifier.width(20.dp)) - AddThemeItem( - onClick = { - navController.navigate(NavigationDestination.ThemeDetail) - }, - ) - Spacer(modifier = Modifier.width(20.dp)) - } - items( - items = customThemes, - ) { theme -> - ThemeItem( - theme = theme, - onClick = { - scope.launch { - navController.navigate("${NavigationDestination.ThemeDetail}?themeId=${theme.id}") - bottomSheet.hide() - } - }, - onClickMore = { - scope.launch { - bottomSheet.setSheetContent { - CustomThemeMoreActionBottomSheet( - onClickDetail = { - scope.launch { - navController.navigate("${NavigationDestination.ThemeDetail}?themeId=${theme.id}") - bottomSheet.hide() - } - }, - onClickDuplicate = { - scope.launch { - launchSuspendApi( - apiOnProgress, - apiOnError, - ) { - themeListViewModel.copyTheme(theme.id) - bottomSheet.hide() - } - } - }, - onClickDelete = { - showDeleteThemeDialog( - composableStates = composableStates, - onConfirm = { - themeListViewModel.deleteThemeAndRefreshTableIfNeeded( - theme.id, - ) - modalState.hide() - bottomSheet.hide() - }, - ) - }, - ) + themes = customThemes, + onClickItem = onNavigateToDetail, + onClickMore = { theme -> + scope.launch { + bottomSheet.setSheetContent { + CustomThemeMoreActionBottomSheet( + onClickDetail = { + scope.launch { + onNavigateToDetail(theme) + bottomSheet.hide() + } + }, + onClickDuplicate = { + scope.launch { + onDuplicateTheme(theme) + bottomSheet.hide() } - bottomSheet.show() - } - }, - ) - Spacer(modifier = Modifier.width(20.dp)) + }, + onClickDelete = { + showDeleteThemeDialog( + composableStates = composableStates, + onConfirm = { + onDeleteTheme(theme) + modalState.hide() + bottomSheet.hide() + }, + ) + }, + ) + } + bottomSheet.show() } - } - } + }, + leadingItem = { + AddThemeItem( + onClick = onClickAddTheme, + ) + }, + ) Spacer(modifier = Modifier.height(4.dp)) - SettingColumn( + ThemesRow( title = stringResource(R.string.theme_config_builtin_theme), - titleStyle = SNUTTTypography.body2.copy( - color = MaterialTheme.colors.onSurfaceVariant, - fontSize = 13.sp, - ), - ) { - Row( - modifier = Modifier - .fillMaxWidth() - .background(MaterialTheme.colors.surface) - .padding(top = 20.dp, bottom = 12.dp) - .horizontalScroll(rememberScrollState()), - ) { - Spacer(modifier = Modifier.width(20.dp)) - builtInThemes.forEach { theme -> - ThemeItem( - theme = theme, - onClick = { - scope.launch { - navController.navigate("${NavigationDestination.ThemeDetail}?theme=${theme.code}") - bottomSheet.hide() - } - }, - ) - Spacer(modifier = Modifier.width(20.dp)) - } - } - } + themes = builtInThemes, + onClickItem = onNavigateToDetail, + ) Spacer(modifier = Modifier.height(25.dp)) ThemeGuideTexts( modifier = Modifier @@ -243,6 +217,55 @@ fun ThemeConfigPage( } } +@Composable +private fun ThemesRow( + title: String, + themes: List, + onClickItem: (TableTheme) -> Unit, + modifier: Modifier = Modifier, + onClickMore: ((TableTheme) -> Unit)? = null, + leadingItem: (@Composable () -> Unit)? = null, +) { + SettingColumn( + title = title, + titleStyle = SNUTTTypography.body2.copy( + color = MaterialTheme.colors.onSurfaceVariant, + fontSize = 13.sp, + ), + modifier = modifier, + ) { + LazyRow( + modifier = Modifier + .fillMaxWidth() + .background(MaterialTheme.colors.surface) + .padding(top = 20.dp, bottom = 12.dp), + ) { + item { + Spacer(modifier = Modifier.width(20.dp)) + leadingItem?.let { + it() + Spacer(modifier = Modifier.width(20.dp)) + } + } + + items( + items = themes, + ) { theme -> + ThemeItem( + theme = theme, + onClick = { + onClickItem(theme) + }, + onClickMore = onClickMore?.let { + { it(theme) } + }, + ) + Spacer(modifier = Modifier.width(20.dp)) + } + } + } +} + @Composable fun AddThemeItem( onClick: () -> Unit, @@ -274,7 +297,7 @@ fun AddThemeItem( } @Composable -private fun ThemeItem( +fun ThemeItem( theme: TableTheme, onClick: () -> Unit, modifier: Modifier = Modifier, From 2f91a1a304793774afebffa3779924e8009a7135 Mon Sep 17 00:00:00 2001 From: eastshine2741 Date: Sat, 16 Nov 2024 23:18:40 +0900 Subject: [PATCH 19/35] =?UTF-8?q?ThemeListViewModel=20->=20ThemeConfigView?= =?UTF-8?q?Model=20=EC=9D=B4=EB=A6=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/wafflestudio/snutt2/views/RootActivity.kt | 6 +++--- .../logged_in/home/drawer/ChangeThemeBottomSheet.kt | 8 ++++---- .../home/settings/theme/ThemeConfigScreen.kt | 12 ++++++------ ...ThemeListViewModel.kt => ThemeConfigViewModel.kt} | 2 +- 4 files changed, 14 insertions(+), 14 deletions(-) rename app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/{ThemeListViewModel.kt => ThemeConfigViewModel.kt} (97%) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/RootActivity.kt b/app/src/main/java/com/wafflestudio/snutt2/views/RootActivity.kt index 647f46655..e5381a61f 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/RootActivity.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/RootActivity.kt @@ -68,7 +68,7 @@ import com.wafflestudio.snutt2.views.logged_in.home.search.SearchViewModel import com.wafflestudio.snutt2.views.logged_in.home.settings.* import com.wafflestudio.snutt2.views.logged_in.home.settings.theme.ThemeConfigRoute import com.wafflestudio.snutt2.views.logged_in.home.settings.theme.ThemeDetailRoute -import com.wafflestudio.snutt2.views.logged_in.home.settings.theme.ThemeListViewModel +import com.wafflestudio.snutt2.views.logged_in.home.settings.theme.ThemeConfigViewModel import com.wafflestudio.snutt2.views.logged_in.lecture_detail.LectureColorSelectorPage import com.wafflestudio.snutt2.views.logged_in.lecture_detail.LectureDetailPage import com.wafflestudio.snutt2.views.logged_in.lecture_detail.LectureDetailViewModel @@ -435,9 +435,9 @@ class RootActivity : AppCompatActivity() { val parentEntry = remember(it) { navController.getBackStackEntry(NavigationDestination.Home) } - val themeListViewModel = hiltViewModel(parentEntry) + val themeConfigViewModel = hiltViewModel(parentEntry) ThemeConfigRoute( - themeListViewModel = themeListViewModel, + themeConfigViewModel = themeConfigViewModel, onNavigateBack = { navController.popBackStack() }, onNavigateToThemeDetail = { theme -> when (theme) { diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/drawer/ChangeThemeBottomSheet.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/drawer/ChangeThemeBottomSheet.kt index aa0e034ce..7d7dfc711 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/drawer/ChangeThemeBottomSheet.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/drawer/ChangeThemeBottomSheet.kt @@ -47,7 +47,7 @@ import com.wafflestudio.snutt2.views.LocalBottomSheetState import com.wafflestudio.snutt2.views.LocalNavController import com.wafflestudio.snutt2.views.NavigationDestination import com.wafflestudio.snutt2.views.logged_in.home.settings.theme.AddThemeItem -import com.wafflestudio.snutt2.views.logged_in.home.settings.theme.ThemeListViewModel +import com.wafflestudio.snutt2.views.logged_in.home.settings.theme.ThemeConfigViewModel import com.wafflestudio.snutt2.views.logged_in.home.timetable.TimetableViewModel @Composable @@ -55,13 +55,13 @@ fun ChangeThemeBottomSheet( onPreview: (TableTheme) -> Unit, onApply: () -> Unit, onDispose: () -> Unit, - themeListViewModel: ThemeListViewModel = hiltViewModel(), + themeConfigViewModel: ThemeConfigViewModel = hiltViewModel(), timetableViewModel: TimetableViewModel = hiltViewModel(), ) { val navController = LocalNavController.current val bottomSheet = LocalBottomSheetState.current - val customThemes by themeListViewModel.customThemes.collectAsState() - val builtInThemes by themeListViewModel.builtInThemes.collectAsState() + val customThemes by themeConfigViewModel.customThemes.collectAsState() + val builtInThemes by themeConfigViewModel.builtInThemes.collectAsState() val previewTheme by timetableViewModel.previewTheme.collectAsState() var previousCustomThemes = remember { customThemes } val selectedTheme by remember { diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigScreen.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigScreen.kt index 153b2afb7..60a22eab3 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigScreen.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigScreen.kt @@ -72,13 +72,13 @@ fun ThemeConfigRoute( onNavigateBack: () -> Unit, onNavigateToThemeDetail: (TableTheme) -> Unit, onClickAddTheme: () -> Unit, - themeListViewModel: ThemeListViewModel = hiltViewModel(), + themeConfigViewModel: ThemeConfigViewModel = hiltViewModel(), ) { val apiOnError = LocalApiOnError.current val apiOnProgress = LocalApiOnProgress.current - val customThemes by themeListViewModel.customThemes.collectAsState() - val builtInThemes by themeListViewModel.builtInThemes.collectAsState() + val customThemes by themeConfigViewModel.customThemes.collectAsState() + val builtInThemes by themeConfigViewModel.builtInThemes.collectAsState() ThemeConfigScreen( customThemes = customThemes, @@ -86,19 +86,19 @@ fun ThemeConfigRoute( onNavigateBack = onNavigateBack, onFetchThemes = { launchSuspendApi(apiOnProgress, apiOnError) { - themeListViewModel.fetchThemes() + themeConfigViewModel.fetchThemes() } }, onNavigateToDetail = onNavigateToThemeDetail, onClickAddTheme = onClickAddTheme, onDuplicateTheme = { launchSuspendApi(apiOnProgress, apiOnError) { - themeListViewModel.copyTheme(it) + themeConfigViewModel.copyTheme(it) } }, onDeleteTheme = { launchSuspendApi(apiOnProgress, apiOnError) { - themeListViewModel.deleteThemeAndRefreshTableIfNeeded(it) + themeConfigViewModel.deleteThemeAndRefreshTableIfNeeded(it) } }, ) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeListViewModel.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigViewModel.kt similarity index 97% rename from app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeListViewModel.kt rename to app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigViewModel.kt index cb1982c15..fba7b8c59 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeListViewModel.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigViewModel.kt @@ -12,7 +12,7 @@ import kotlinx.coroutines.flow.StateFlow import javax.inject.Inject @HiltViewModel -class ThemeListViewModel @Inject constructor( +class ThemeConfigViewModel @Inject constructor( private val themeRepository: ThemeRepository, private val tableRepository: TableRepository, currentTableRepository: CurrentTableRepository, From 1129f1d40ded5a379330c1a8ae177bfa501e0cfb Mon Sep 17 00:00:00 2001 From: eastshine2741 Date: Sat, 16 Nov 2024 23:19:26 +0900 Subject: [PATCH 20/35] =?UTF-8?q?=EB=88=84=EB=9D=BD=EB=90=9C=20private=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../views/logged_in/home/settings/theme/ThemeConfigScreen.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigScreen.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigScreen.kt index 60a22eab3..c79a438d8 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigScreen.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigScreen.kt @@ -297,7 +297,7 @@ fun AddThemeItem( } @Composable -fun ThemeItem( +private fun ThemeItem( theme: TableTheme, onClick: () -> Unit, modifier: Modifier = Modifier, @@ -351,7 +351,7 @@ fun ThemeItem( } @Composable -fun ThemeGuideTexts( +private fun ThemeGuideTexts( modifier: Modifier = Modifier, ) { val texts = listOf( From 05a8a5e3b44cd116d968b669a9d329524ad9d74b Mon Sep 17 00:00:00 2001 From: eastshine2741 Date: Sat, 16 Nov 2024 23:24:09 +0900 Subject: [PATCH 21/35] =?UTF-8?q?ThemeConfig=20Preview=20=EB=AA=87=20?= =?UTF-8?q?=EA=B0=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../home/settings/theme/ThemeConfigScreen.kt | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigScreen.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigScreen.kt index c79a438d8..f8b53f316 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigScreen.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigScreen.kt @@ -40,6 +40,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.withStyle +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel @@ -57,6 +58,7 @@ import com.wafflestudio.snutt2.model.BuiltInTheme import com.wafflestudio.snutt2.model.CustomTheme import com.wafflestudio.snutt2.model.TableTheme import com.wafflestudio.snutt2.ui.SNUTTColors +import com.wafflestudio.snutt2.ui.SNUTTTheme import com.wafflestudio.snutt2.ui.SNUTTTypography import com.wafflestudio.snutt2.ui.isDarkMode import com.wafflestudio.snutt2.ui.onSurfaceVariant @@ -402,3 +404,29 @@ private fun ThemeGuideTexts( ) } } + +@Preview +@Composable +private fun ThemesRowPreview() { + SNUTTTheme { + ThemesRow( + title = "title", + themes = List(5) { BuiltInTheme.fromCode(it) }, + onClickItem = {}, + onClickMore = {}, + leadingItem = { + AddThemeItem( + onClick = {} + ) + } + ) + } +} + +@Preview +@Composable +private fun ThemeGuideTextsPreview() { + SNUTTTheme { + ThemeGuideTexts() + } +} From ee00ec4984e1f635da8c52723d5d50b7910743dd Mon Sep 17 00:00:00 2001 From: eastshine2741 Date: Sat, 16 Nov 2024 23:25:59 +0900 Subject: [PATCH 22/35] lint --- .../snutt2/components/compose/ThemeIcon.kt | 2 +- .../snutt2/lib/network/dto/core/ThemeDto.kt | 2 +- .../wafflestudio/snutt2/model/TableTheme.kt | 1 - .../wafflestudio/snutt2/views/RootActivity.kt | 2 +- .../home/settings/theme/ThemeConfigScreen.kt | 4 ++-- .../home/settings/theme/ThemeDetailScreen.kt | 24 +++++++++---------- 6 files changed, 17 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/components/compose/ThemeIcon.kt b/app/src/main/java/com/wafflestudio/snutt2/components/compose/ThemeIcon.kt index d7f36e9c7..d9422130c 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/components/compose/ThemeIcon.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/components/compose/ThemeIcon.kt @@ -312,7 +312,7 @@ fun ThemeIconPreview() { bgColor = Random.nextInt(0x0, 0xffffff), ) }, - isFromMarket = false + isFromMarket = false, ), modifier = Modifier.size(80.dp), ) diff --git a/app/src/main/java/com/wafflestudio/snutt2/lib/network/dto/core/ThemeDto.kt b/app/src/main/java/com/wafflestudio/snutt2/lib/network/dto/core/ThemeDto.kt index a4fcdf674..896df16be 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/lib/network/dto/core/ThemeDto.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/lib/network/dto/core/ThemeDto.kt @@ -20,7 +20,7 @@ data class ThemeDto( id = id!!, name = name ?: "", colors = colors ?: emptyList(), - isFromMarket = false // FIXME: 서버 응답에 맞게 수정 + isFromMarket = false, // FIXME: 서버 응답에 맞게 수정 ) } else { BuiltInTheme.fromCode(theme ?: 0) diff --git a/app/src/main/java/com/wafflestudio/snutt2/model/TableTheme.kt b/app/src/main/java/com/wafflestudio/snutt2/model/TableTheme.kt index 402634634..6fd6c3bc8 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/model/TableTheme.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/model/TableTheme.kt @@ -1,7 +1,6 @@ package com.wafflestudio.snutt2.model import androidx.compose.runtime.Composable -import androidx.compose.ui.res.colorResource import com.wafflestudio.snutt2.lib.Selectable import com.wafflestudio.snutt2.lib.network.dto.core.ColorDto import com.wafflestudio.snutt2.lib.network.dto.core.TableDto diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/RootActivity.kt b/app/src/main/java/com/wafflestudio/snutt2/views/RootActivity.kt index e5381a61f..7d2c5e0c1 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/RootActivity.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/RootActivity.kt @@ -447,7 +447,7 @@ class RootActivity : AppCompatActivity() { }, onClickAddTheme = { navController.navigate(NavigationDestination.ThemeDetail) - } + }, ) } if (BuildConfig.DEBUG) composable2(NavigationDestination.NetworkLog) { NetworkLogPage() } diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigScreen.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigScreen.kt index f8b53f316..658855bec 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigScreen.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigScreen.kt @@ -416,9 +416,9 @@ private fun ThemesRowPreview() { onClickMore = {}, leadingItem = { AddThemeItem( - onClick = {} + onClick = {}, ) - } + }, ) } } diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailScreen.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailScreen.kt index c6b38784b..ca55b64e2 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailScreen.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeDetailScreen.kt @@ -128,7 +128,7 @@ fun ThemeDetailRoute( avoid = { modalState.hide() onNavigateBack() - } + }, ) } @@ -168,7 +168,7 @@ fun ThemeDetailScreen( cancelEdit = { modalState.hide() onNavigateBack() - } + }, ) } else { onNavigateBack() @@ -189,7 +189,7 @@ fun ThemeDetailScreen( onClickBack = onBackPressed, onClickSave = { onSaveTheme(editingTheme.isNew) - } + }, ) Column( modifier = Modifier @@ -233,12 +233,12 @@ fun ThemeDetailScreen( onToggleColorExpanded = onToggleColorExpanded, onDuplicateColor = onDuplicateColor, onRemoveColor = onRemoveColor, - onUpdateColor = onUpdateColor + onUpdateColor = onUpdateColor, ) } AnimatedVisibility(editingTheme.isEditable && editingTheme.colors.size < 9) { ThemeColorAddRow( - onClick = onAddColor + onClick = onAddColor, ) } } @@ -273,11 +273,11 @@ fun ThemeDetailScreen( .fillMaxHeight(0.95f) .fillMaxWidth() .background(MaterialTheme.colors.background), - contentAlignment = Alignment.Center + contentAlignment = Alignment.Center, ) { Text( text = stringResource(R.string.error_unknown), - color = MaterialTheme.colors.onBackground + color = MaterialTheme.colors.onBackground, ) } } @@ -288,7 +288,7 @@ fun ThemeDetailScreen( .fillMaxHeight(0.95f) .fillMaxWidth() .background(MaterialTheme.colors.background), - contentAlignment = Alignment.Center + contentAlignment = Alignment.Center, ) { CircularProgressIndicator() } @@ -348,7 +348,7 @@ private fun ThemeColorRow( AnimatedVisibility( visibleState = state, enter = expandVertically(), - exit = shrinkVertically() + exit = shrinkVertically(), ) { Column { ThemeDetailItem( @@ -582,7 +582,7 @@ private fun ThemeColorRowPreview() { onToggleColorExpanded = {}, onDuplicateColor = {}, onRemoveColor = {}, - onUpdateColor = { _, _, _ -> } + onUpdateColor = { _, _, _ -> }, ) } } @@ -593,7 +593,7 @@ private fun ThemeColorRowPreview() { private fun ThemeColorAddRowPreview() { SNUTTTheme { ThemeColorAddRow( - onClick = {} + onClick = {}, ) } } @@ -605,7 +605,7 @@ private fun ThemeDetailTopBarPreview() { ThemeDetailTopBar( isCustomTheme = true, onClickBack = {}, - onClickSave = {} + onClickSave = {}, ) } } From 4a8a70be7661b0c9e882daf726fbfb9174709354 Mon Sep 17 00:00:00 2001 From: eastshine2741 Date: Sat, 16 Nov 2024 23:35:49 +0900 Subject: [PATCH 23/35] =?UTF-8?q?ThemeConfigViewModel=EC=9D=98=20StateFlow?= =?UTF-8?q?=EA=B0=80=20backing=20field=20=EA=B0=80=EC=A7=80=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../logged_in/home/settings/theme/ThemeConfigViewModel.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigViewModel.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigViewModel.kt index fba7b8c59..1f05872be 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigViewModel.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigViewModel.kt @@ -18,8 +18,8 @@ class ThemeConfigViewModel @Inject constructor( currentTableRepository: CurrentTableRepository, ) : ViewModel() { - val customThemes: StateFlow> get() = themeRepository.customThemes - val builtInThemes: StateFlow> get() = themeRepository.builtInThemes + val customThemes: StateFlow> = themeRepository.customThemes + val builtInThemes: StateFlow> = themeRepository.builtInThemes val currentTable = currentTableRepository.currentTable From 186e59b1c28927636b4e7996d64037d26c940b81 Mon Sep 17 00:00:00 2001 From: eastshine2741 Date: Sun, 17 Nov 2024 00:01:00 +0900 Subject: [PATCH 24/35] =?UTF-8?q?=EB=84=A4=EC=9D=B4=EB=B0=8D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/wafflestudio/snutt2/views/RootActivity.kt | 2 +- .../views/logged_in/home/settings/theme/ThemeConfigScreen.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/RootActivity.kt b/app/src/main/java/com/wafflestudio/snutt2/views/RootActivity.kt index 7d2c5e0c1..94105617e 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/RootActivity.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/RootActivity.kt @@ -439,7 +439,7 @@ class RootActivity : AppCompatActivity() { ThemeConfigRoute( themeConfigViewModel = themeConfigViewModel, onNavigateBack = { navController.popBackStack() }, - onNavigateToThemeDetail = { theme -> + onNavigateToDetail = { theme -> when (theme) { is CustomTheme -> navController.navigate("${NavigationDestination.ThemeDetail}?themeId=${theme.id}") is BuiltInTheme -> navController.navigate("${NavigationDestination.ThemeDetail}?theme=${theme.code}") diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigScreen.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigScreen.kt index 658855bec..986cb08b0 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigScreen.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigScreen.kt @@ -72,7 +72,7 @@ import kotlinx.coroutines.launch @Composable fun ThemeConfigRoute( onNavigateBack: () -> Unit, - onNavigateToThemeDetail: (TableTheme) -> Unit, + onNavigateToDetail: (TableTheme) -> Unit, onClickAddTheme: () -> Unit, themeConfigViewModel: ThemeConfigViewModel = hiltViewModel(), ) { @@ -91,7 +91,7 @@ fun ThemeConfigRoute( themeConfigViewModel.fetchThemes() } }, - onNavigateToDetail = onNavigateToThemeDetail, + onNavigateToDetail = onNavigateToDetail, onClickAddTheme = onClickAddTheme, onDuplicateTheme = { launchSuspendApi(apiOnProgress, apiOnError) { From 0a2fb22b3762a3731f8f3bfc2612d79ab743c454 Mon Sep 17 00:00:00 2001 From: eastshine2741 Date: Sat, 16 Nov 2024 23:45:15 +0900 Subject: [PATCH 25/35] =?UTF-8?q?=EB=8B=B4=EC=9D=80=20=ED=85=8C=EB=A7=88?= =?UTF-8?q?=20=EB=94=B0=EB=A1=9C=20=EB=B3=B4=EC=97=AC=EC=A3=BC=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../snutt2/lib/network/dto/core/ThemeDto.kt | 3 ++- .../home/settings/theme/ThemeConfigScreen.kt | 17 +++++++++++++---- .../home/settings/theme/ThemeConfigViewModel.kt | 10 +++++++++- app/src/main/res/values/strings.xml | 1 + 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/lib/network/dto/core/ThemeDto.kt b/app/src/main/java/com/wafflestudio/snutt2/lib/network/dto/core/ThemeDto.kt index 896df16be..ceabcd93f 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/lib/network/dto/core/ThemeDto.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/lib/network/dto/core/ThemeDto.kt @@ -12,6 +12,7 @@ data class ThemeDto( val name: String?, val colors: List?, val isCustom: Boolean?, + val status: String?, ) { fun toTableTheme(): TableTheme { @@ -20,7 +21,7 @@ data class ThemeDto( id = id!!, name = name ?: "", colors = colors ?: emptyList(), - isFromMarket = false, // FIXME: 서버 응답에 맞게 수정 + isFromMarket = status == "DOWNLOADED", ) } else { BuiltInTheme.fromCode(theme ?: 0) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigScreen.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigScreen.kt index 986cb08b0..776ee6f3f 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigScreen.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigScreen.kt @@ -79,11 +79,13 @@ fun ThemeConfigRoute( val apiOnError = LocalApiOnError.current val apiOnProgress = LocalApiOnProgress.current - val customThemes by themeConfigViewModel.customThemes.collectAsState() + val myCustomThemes by themeConfigViewModel.myCustomThemes.collectAsState() + val marketCustomThemes by themeConfigViewModel.marketCustomThemes.collectAsState() val builtInThemes by themeConfigViewModel.builtInThemes.collectAsState() ThemeConfigScreen( - customThemes = customThemes, + myCustomThemes = myCustomThemes, + marketCustomThemes = marketCustomThemes, builtInThemes = builtInThemes, onNavigateBack = onNavigateBack, onFetchThemes = { @@ -109,7 +111,8 @@ fun ThemeConfigRoute( @OptIn(ExperimentalMaterialApi::class) @Composable fun ThemeConfigScreen( - customThemes: List, + myCustomThemes: List, + marketCustomThemes: List, builtInThemes: List, onNavigateBack: () -> Unit, onFetchThemes: suspend () -> Unit, @@ -162,7 +165,7 @@ fun ThemeConfigScreen( ) { ThemesRow( title = stringResource(R.string.theme_config_custom_theme), - themes = customThemes, + themes = myCustomThemes, onClickItem = onNavigateToDetail, onClickMore = { theme -> scope.launch { @@ -202,6 +205,12 @@ fun ThemeConfigScreen( }, ) Spacer(modifier = Modifier.height(4.dp)) + ThemesRow( + title = stringResource(R.string.theme_config_market_custom_theme), + themes = marketCustomThemes, + onClickItem = onNavigateToDetail, + ) + Spacer(modifier = Modifier.height(4.dp)) ThemesRow( title = stringResource(R.string.theme_config_builtin_theme), themes = builtInThemes, diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigViewModel.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigViewModel.kt index 1f05872be..ce9da82e6 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigViewModel.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigViewModel.kt @@ -1,9 +1,11 @@ package com.wafflestudio.snutt2.views.logged_in.home.settings.theme import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import com.wafflestudio.snutt2.data.current_table.CurrentTableRepository import com.wafflestudio.snutt2.data.tables.TableRepository import com.wafflestudio.snutt2.data.themes.ThemeRepository +import com.wafflestudio.snutt2.lib.map import com.wafflestudio.snutt2.model.BuiltInTheme import com.wafflestudio.snutt2.model.CustomTheme import com.wafflestudio.snutt2.model.TableTheme @@ -18,7 +20,13 @@ class ThemeConfigViewModel @Inject constructor( currentTableRepository: CurrentTableRepository, ) : ViewModel() { - val customThemes: StateFlow> = themeRepository.customThemes + val customThemes = themeRepository.customThemes + val myCustomThemes = themeRepository.customThemes.map(viewModelScope) { customThemes -> + customThemes.filter { it.isFromMarket.not() } + } + val marketCustomThemes = themeRepository.customThemes.map(viewModelScope) { customThemes -> + customThemes.filter { it.isFromMarket } + } val builtInThemes: StateFlow> = themeRepository.builtInThemes val currentTable = currentTableRepository.currentTable diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1e9f1b6ad..f59ae5996 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -527,6 +527,7 @@ 여기를 눌러 빈자리 알림 서비스를 이용해 보세요! 시간표 테마 커스텀 테마 + 담은 테마 제공 테마 테마는 어떻게 적용하나요? 시간표 목록 > 더보기 버튼 > 테마 설정 From 95f7019246b0be1ed810e0269df944e8f55072f9 Mon Sep 17 00:00:00 2001 From: eastshine2741 Date: Sun, 15 Sep 2024 16:02:34 +0900 Subject: [PATCH 26/35] =?UTF-8?q?=EB=8D=94=EB=B3=B4=EA=B8=B0=20=ED=83=AD?= =?UTF-8?q?=20=ED=85=8C=EB=A7=88=EB=A7=88=EC=BC=93=20=EC=A7=84=EC=9E=85?= =?UTF-8?q?=EC=A0=90=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../snutt2/views/logged_in/home/settings/SettingsPage.kt | 6 ++++++ app/src/main/res/values/strings.xml | 1 + 2 files changed, 7 insertions(+) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/SettingsPage.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/SettingsPage.kt index d07a75786..d021eff68 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/SettingsPage.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/SettingsPage.kt @@ -135,6 +135,12 @@ fun SettingsPage( ) }, ) + SettingItem( + title = stringResource(R.string.settings_item_theme_market), + hasNextPage = true, + onClick = { + }, + ) } Margin(height = 10.dp) SettingColumn { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f59ae5996..032036a8d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -409,6 +409,7 @@ 불편한 점이나 버그를 제보해주세요.\n더 나은 SNUTT를 위한 아이디어도 환영해요. 개인정보처리방침 빈자리 알림 + 테마 마켓 서비스 약관 버전 정보 개발자 정보 From 3b8730f65b14944d6307a7a5e4062554900b886a Mon Sep 17 00:00:00 2001 From: eastshine2741 Date: Tue, 17 Sep 2024 01:50:10 +0900 Subject: [PATCH 27/35] =?UTF-8?q?ThemeMarketScreen=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../snutt2/views/NavigationDestination.kt | 1 + .../wafflestudio/snutt2/views/RootActivity.kt | 6 ++ .../logged_in/home/settings/SettingsPage.kt | 3 + .../thememarket/ThemeMarketScreen.kt | 72 +++++++++++++++++++ app/src/main/res/values/strings.xml | 1 + 5 files changed, 83 insertions(+) create mode 100644 app/src/main/java/com/wafflestudio/snutt2/views/logged_in/thememarket/ThemeMarketScreen.kt diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/NavigationDestination.kt b/app/src/main/java/com/wafflestudio/snutt2/views/NavigationDestination.kt index 7aa9eeea1..043f28518 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/NavigationDestination.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/NavigationDestination.kt @@ -27,6 +27,7 @@ object NavigationDestination { const val Bookmark = "bookmarks" const val NetworkLog = "network_log" const val VacancyNotification = "vacancy" + const val ThemeMarket = "theme_market" const val Friends = "friends" const val ThemeConfig = "theme_config" const val ThemeDetail = "theme_detail" diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/RootActivity.kt b/app/src/main/java/com/wafflestudio/snutt2/views/RootActivity.kt index 94105617e..ba71c0626 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/RootActivity.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/RootActivity.kt @@ -75,6 +75,7 @@ import com.wafflestudio.snutt2.views.logged_in.lecture_detail.LectureDetailViewM import com.wafflestudio.snutt2.views.logged_in.lecture_detail.deeplink.TimetableLectureDetailPage import com.wafflestudio.snutt2.views.logged_in.notifications.NotificationPage import com.wafflestudio.snutt2.views.logged_in.table_lectures.LecturesOfTablePage +import com.wafflestudio.snutt2.views.logged_in.thememarket.ThemeMarketRoute import com.wafflestudio.snutt2.views.logged_in.vacancy_noti.VacancyPage import com.wafflestudio.snutt2.views.logged_in.vacancy_noti.VacancyViewModel import com.wafflestudio.snutt2.views.logged_out.* @@ -431,6 +432,11 @@ class RootActivity : AppCompatActivity() { val vacancyViewModel = hiltViewModel(parentEntry) VacancyPage(vacancyViewModel) } + composable2(NavigationDestination.ThemeMarket) { + ThemeMarketRoute( + onBackClick = { navController.popBackStack() }, + ) + } composable2(NavigationDestination.ThemeConfig) { val parentEntry = remember(it) { navController.getBackStackEntry(NavigationDestination.Home) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/SettingsPage.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/SettingsPage.kt index d021eff68..7a1b9403d 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/SettingsPage.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/SettingsPage.kt @@ -139,6 +139,9 @@ fun SettingsPage( title = stringResource(R.string.settings_item_theme_market), hasNextPage = true, onClick = { + navController.navigate( + NavigationDestination.ThemeMarket, + ) }, ) } diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/thememarket/ThemeMarketScreen.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/thememarket/ThemeMarketScreen.kt new file mode 100644 index 000000000..be975fc87 --- /dev/null +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/thememarket/ThemeMarketScreen.kt @@ -0,0 +1,72 @@ +package com.wafflestudio.snutt2.views.logged_in.thememarket + +import androidx.activity.compose.BackHandler +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.hilt.navigation.compose.hiltViewModel +import com.wafflestudio.snutt2.R +import com.wafflestudio.snutt2.components.compose.SimpleTopBar +import com.wafflestudio.snutt2.components.compose.SnuttWebView +import com.wafflestudio.snutt2.lib.android.webview.WebViewContainer +import com.wafflestudio.snutt2.ui.isDarkMode +import com.wafflestudio.snutt2.views.logged_in.home.settings.UserViewModel +import kotlinx.coroutines.flow.StateFlow + +@Composable +fun ThemeMarketRoute( + onBackClick: () -> Unit, + userViewModel: UserViewModel = hiltViewModel(), +) { + ThemeMarketScreen( + accessToken = userViewModel.accessToken, + onBackClick = onBackClick, + modifier = Modifier.fillMaxSize(), + ) +} + +@Composable +fun ThemeMarketScreen( + accessToken: StateFlow, + onBackClick: () -> Unit, + modifier: Modifier = Modifier, +) { + val context = LocalContext.current + val isDarkMode = isDarkMode() + val webViewContainer = remember { + WebViewContainer( + context = context, + accessToken = accessToken, + isDarkMode = isDarkMode, + ) + } + + BackHandler { + if (webViewContainer.webView.canGoBack()) { + webViewContainer.webView.goBack() + } else { + onBackClick() + } + } + + LaunchedEffect(Unit) { + webViewContainer.openPage("https://snutt-theme-market-dev.wafflestudio.com/download") + } + + Column( + modifier = modifier, + ) { + SimpleTopBar( + title = stringResource(R.string.theme_market_app_bar_title), + onClickNavigateBack = onBackClick, + ) + SnuttWebView( + webViewContainer = webViewContainer, + ) + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 032036a8d..ad1b256c1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -578,4 +578,5 @@ 카카오톡 초대 기능을 사용할 수 없습니다. 알 수 없는 오류가 발생했습니다. 카카오톡이 설치되어 있는지 확인해보세요. + 테마 마켓 From 328bf871cc9df5db60f924d847385d6440a42d15 Mon Sep 17 00:00:00 2001 From: eastshine2741 Date: Sat, 14 Dec 2024 18:01:13 +0900 Subject: [PATCH 28/35] =?UTF-8?q?=ED=85=8C=EB=A7=88=EB=A7=88=EC=BC=93=20?= =?UTF-8?q?=EC=9B=B9=EB=B7=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../webview/ThemeMarketWebViewContainer.kt | 119 ++++++++++++++++++ .../lib/android/webview/WebViewContainer.kt | 2 +- .../thememarket/ThemeMarketScreen.kt | 11 +- .../thememarket/ThemeMarketWebView.kt | 20 +++ 4 files changed, 145 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/com/wafflestudio/snutt2/lib/android/webview/ThemeMarketWebViewContainer.kt create mode 100644 app/src/main/java/com/wafflestudio/snutt2/views/logged_in/thememarket/ThemeMarketWebView.kt diff --git a/app/src/main/java/com/wafflestudio/snutt2/lib/android/webview/ThemeMarketWebViewContainer.kt b/app/src/main/java/com/wafflestudio/snutt2/lib/android/webview/ThemeMarketWebViewContainer.kt new file mode 100644 index 000000000..42f0d6ead --- /dev/null +++ b/app/src/main/java/com/wafflestudio/snutt2/lib/android/webview/ThemeMarketWebViewContainer.kt @@ -0,0 +1,119 @@ +package com.wafflestudio.snutt2.lib.android.webview + +import android.content.Context +import android.graphics.Bitmap +import android.os.Build +import android.webkit.CookieManager +import android.webkit.WebChromeClient +import android.webkit.WebResourceError +import android.webkit.WebResourceRequest +import android.webkit.WebView +import android.webkit.WebViewClient +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.mutableStateOf +import com.wafflestudio.snutt2.BuildConfig +import com.wafflestudio.snutt2.R +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.first +import java.net.URL + +class ThemeMarketWebViewContainer( + private val context: Context, + private val accessToken: StateFlow, + private val isDarkMode: Boolean, +) : WebViewContainer { + val loadState: MutableState = mutableStateOf(LoadState.InitialLoading(0)) + + override val webView: WebView = WebView(context).apply { + if (BuildConfig.DEBUG) { + WebView.setWebContentsDebuggingEnabled(true) + } + this.webViewClient = object : WebViewClient() { + override fun onPageFinished(view: WebView?, url: String?) { + if (loadState.value != LoadState.Error) { + loadState.value = LoadState.Success + } + } + + override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) { + loadState.value = when (loadState.value) { + is LoadState.InitialLoading -> LoadState.InitialLoading(0) + else -> LoadState.Loading(0) + } + } + + override fun onReceivedError( + view: WebView?, + request: WebResourceRequest?, + error: WebResourceError?, + ) { + loadState.value = LoadState.Error + } + } + this.webChromeClient = object : WebChromeClient() { + override fun onProgressChanged(view: WebView?, newProgress: Int) { + when (loadState.value) { + is LoadState.InitialLoading -> LoadState.InitialLoading(newProgress) + is LoadState.Loading -> LoadState.Loading(newProgress) + else -> null + }?.let { + loadState.value = it + } + } + } + this.settings.javaScriptEnabled = true + } + + override suspend fun openPage(url: String?) { + val accessToken = accessToken.filterNotNull().first() + val themeMarketUrl = url ?: THEME_MARKET_URL + val urlHost = URL(themeMarketUrl).host + + setCookies(urlHost, accessToken) + webView.loadUrl(themeMarketUrl) + } + + private fun setCookies(host: String, accessToken: String) { + CookieManager.getInstance().apply { + setCookie( + host, + "x-access-apikey=${context.getString(R.string.api_key)}", + ) + setCookie( + host, + "x-access-token=$accessToken", + ) + setCookie( + host, + "x-os-type=android", + ) + setCookie( + host, + "x-os-version=${Build.VERSION.SDK_INT}", + ) + setCookie( + host, + "x-app-version=${BuildConfig.VERSION_NAME}", + ) + setCookie( + host, + "x-app-type=${if (BuildConfig.DEBUG) "debug" else "release"}", + ) + setCookie( + host, + "theme=${ + if (isDarkMode) { + "dark" + } else { + "light" + } + }", + ) + }.flush() + } + + companion object { + private val THEME_MARKET_URL = "https://snutt-theme-market-dev.wafflestudio.com/download" + } +} diff --git a/app/src/main/java/com/wafflestudio/snutt2/lib/android/webview/WebViewContainer.kt b/app/src/main/java/com/wafflestudio/snutt2/lib/android/webview/WebViewContainer.kt index c77d6fa55..f454d340b 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/lib/android/webview/WebViewContainer.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/lib/android/webview/WebViewContainer.kt @@ -5,5 +5,5 @@ import android.webkit.WebView interface WebViewContainer { val webView: WebView - suspend fun openPage(url: String?) + suspend fun openPage(url: String? = null) } diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/thememarket/ThemeMarketScreen.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/thememarket/ThemeMarketScreen.kt index be975fc87..8fd816b73 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/thememarket/ThemeMarketScreen.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/thememarket/ThemeMarketScreen.kt @@ -12,8 +12,7 @@ import androidx.compose.ui.res.stringResource import androidx.hilt.navigation.compose.hiltViewModel import com.wafflestudio.snutt2.R import com.wafflestudio.snutt2.components.compose.SimpleTopBar -import com.wafflestudio.snutt2.components.compose.SnuttWebView -import com.wafflestudio.snutt2.lib.android.webview.WebViewContainer +import com.wafflestudio.snutt2.lib.android.webview.ThemeMarketWebViewContainer import com.wafflestudio.snutt2.ui.isDarkMode import com.wafflestudio.snutt2.views.logged_in.home.settings.UserViewModel import kotlinx.coroutines.flow.StateFlow @@ -39,7 +38,7 @@ fun ThemeMarketScreen( val context = LocalContext.current val isDarkMode = isDarkMode() val webViewContainer = remember { - WebViewContainer( + ThemeMarketWebViewContainer( context = context, accessToken = accessToken, isDarkMode = isDarkMode, @@ -55,7 +54,7 @@ fun ThemeMarketScreen( } LaunchedEffect(Unit) { - webViewContainer.openPage("https://snutt-theme-market-dev.wafflestudio.com/download") + webViewContainer.openPage() } Column( @@ -65,8 +64,8 @@ fun ThemeMarketScreen( title = stringResource(R.string.theme_market_app_bar_title), onClickNavigateBack = onBackClick, ) - SnuttWebView( - webViewContainer = webViewContainer, + ThemeMarketWebView( + themeMarketWebViewContainer = webViewContainer, ) } } diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/thememarket/ThemeMarketWebView.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/thememarket/ThemeMarketWebView.kt new file mode 100644 index 000000000..eef3d1136 --- /dev/null +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/thememarket/ThemeMarketWebView.kt @@ -0,0 +1,20 @@ +package com.wafflestudio.snutt2.views.logged_in.thememarket + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clipToBounds +import androidx.compose.ui.viewinterop.AndroidView +import com.wafflestudio.snutt2.lib.android.webview.WebViewContainer + +@Composable +fun ThemeMarketWebView( + themeMarketWebViewContainer: WebViewContainer, + modifier: Modifier = Modifier, +) { + AndroidView( + factory = { + themeMarketWebViewContainer.webView + }, + modifier = modifier.clipToBounds(), // Compose에서 WebView 사용 시, WebView가 잠깐 동안 다른 Composable을 가리는 WebView 버그 대응(https://issuetracker.google.com/issues/174233728?pli=1#comment5) + ) +} From eb42341aee3837ca004010bc04daa60960ce6163 Mon Sep 17 00:00:00 2001 From: eastshine2741 Date: Sun, 15 Dec 2024 14:47:14 +0900 Subject: [PATCH 29/35] =?UTF-8?q?=EB=8B=B4=EC=9D=80=20=ED=85=8C=EB=A7=88?= =?UTF-8?q?=EA=B0=80=20=EC=97=86=EC=9C=BC=EB=A9=B4=20=EB=8B=B4=EC=9D=80=20?= =?UTF-8?q?=ED=85=8C=EB=A7=88=20UI=20=EB=B3=B4=EC=97=AC=EC=A3=BC=EC=A7=80?= =?UTF-8?q?=20=EC=95=8A=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../home/settings/theme/ThemeConfigScreen.kt | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigScreen.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigScreen.kt index 776ee6f3f..bc8f7415a 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigScreen.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigScreen.kt @@ -204,12 +204,14 @@ fun ThemeConfigScreen( ) }, ) - Spacer(modifier = Modifier.height(4.dp)) - ThemesRow( - title = stringResource(R.string.theme_config_market_custom_theme), - themes = marketCustomThemes, - onClickItem = onNavigateToDetail, - ) + if (marketCustomThemes.isNotEmpty()) { + Spacer(modifier = Modifier.height(4.dp)) + ThemesRow( + title = stringResource(R.string.theme_config_market_custom_theme), + themes = marketCustomThemes, + onClickItem = onNavigateToDetail, + ) + } Spacer(modifier = Modifier.height(4.dp)) ThemesRow( title = stringResource(R.string.theme_config_builtin_theme), From b4a27dd5e50773ca2afe0124d9f765280146d5c2 Mon Sep 17 00:00:00 2001 From: eastshine2741 Date: Sun, 15 Dec 2024 15:17:22 +0900 Subject: [PATCH 30/35] lint --- .../lib/android/webview/ThemeMarketWebViewContainer.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/lib/android/webview/ThemeMarketWebViewContainer.kt b/app/src/main/java/com/wafflestudio/snutt2/lib/android/webview/ThemeMarketWebViewContainer.kt index 42f0d6ead..31737dc2e 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/lib/android/webview/ThemeMarketWebViewContainer.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/lib/android/webview/ThemeMarketWebViewContainer.kt @@ -103,11 +103,11 @@ class ThemeMarketWebViewContainer( setCookie( host, "theme=${ - if (isDarkMode) { - "dark" - } else { - "light" - } + if (isDarkMode) { + "dark" + } else { + "light" + } }", ) }.flush() From 3918fed80c6d52852f8debe8510c9aa2ea0eac17 Mon Sep 17 00:00:00 2001 From: eastshine2741 Date: Sun, 15 Dec 2024 15:29:09 +0900 Subject: [PATCH 31/35] =?UTF-8?q?=EC=9B=B9=EB=B7=B0=20Loading,=20Error=20?= =?UTF-8?q?=EB=8C=80=EC=9D=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thememarket/ThemeMarketScreen.kt | 1 + .../thememarket/ThemeMarketWebView.kt | 130 +++++++++++++++++- 2 files changed, 128 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/thememarket/ThemeMarketScreen.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/thememarket/ThemeMarketScreen.kt index 8fd816b73..306174c50 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/thememarket/ThemeMarketScreen.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/thememarket/ThemeMarketScreen.kt @@ -66,6 +66,7 @@ fun ThemeMarketScreen( ) ThemeMarketWebView( themeMarketWebViewContainer = webViewContainer, + modifier = Modifier.fillMaxSize(), ) } } diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/thememarket/ThemeMarketWebView.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/thememarket/ThemeMarketWebView.kt index eef3d1136..590384d7c 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/thememarket/ThemeMarketWebView.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/thememarket/ThemeMarketWebView.kt @@ -1,20 +1,144 @@ package com.wafflestudio.snutt2.views.logged_in.thememarket +import android.webkit.WebView +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.size +import androidx.compose.material.Button +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.LinearProgressIndicator +import androidx.compose.material.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clipToBounds +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView -import com.wafflestudio.snutt2.lib.android.webview.WebViewContainer +import com.wafflestudio.snutt2.R +import com.wafflestudio.snutt2.lib.android.webview.LoadState +import com.wafflestudio.snutt2.lib.android.webview.ThemeMarketWebViewContainer +import com.wafflestudio.snutt2.ui.SNUTTColors +import com.wafflestudio.snutt2.ui.SNUTTTheme +import com.wafflestudio.snutt2.ui.SNUTTTypography +import kotlinx.coroutines.launch @Composable fun ThemeMarketWebView( - themeMarketWebViewContainer: WebViewContainer, + themeMarketWebViewContainer: ThemeMarketWebViewContainer, + modifier: Modifier = Modifier, +) { + val scope = rememberCoroutineScope() + when (val loadState = themeMarketWebViewContainer.loadState.value) { + LoadState.Error -> ThemeMarketWebViewError( + onRetry = { + scope.launch { + themeMarketWebViewContainer.openPage() + } + }, + modifier = modifier, + ) + is LoadState.InitialLoading -> ThemeMarketWebViewLoading(loadState.progress / 100f) + is LoadState.Loading -> ThemeMarketWebViewLoading(loadState.progress / 100f) + LoadState.Success -> ThemeMarketWebViewSuccess(themeMarketWebViewContainer.webView) + } +} + +@Composable +private fun ThemeMarketWebViewError( + onRetry: () -> Unit, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .fillMaxWidth(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Image( + modifier = Modifier.size(width = 50.dp, height = 58.dp), + painter = painterResource(R.drawable.ic_cat_retry), + contentDescription = "네트워크 연결을 확인해주세요.", + ) + + Spacer(modifier = Modifier.height(20.dp)) + + Text( + text = stringResource(R.string.reviews_error_message), + style = SNUTTTypography.subtitle1, + color = SNUTTColors.Black900, + ) + + Spacer(modifier = Modifier.height(20.dp)) + + Button( + onClick = onRetry, + colors = ButtonDefaults.buttonColors(backgroundColor = SNUTTColors.Sky), + ) { + Text( + text = stringResource(id = R.string.reviews_error_retry), + style = SNUTTTypography.h3, + color = SNUTTColors.White900, + ) + } + } +} + +@Composable +private fun ThemeMarketWebViewLoading( + progress: Float, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier, + verticalArrangement = Arrangement.Top, + ) { + LinearProgressIndicator( + modifier = Modifier + .fillMaxWidth() + .height(2.dp), + progress = progress, + color = SNUTTColors.Gray200, + ) + } +} + +@Composable +private fun ThemeMarketWebViewSuccess( + webView: WebView, modifier: Modifier = Modifier, ) { AndroidView( factory = { - themeMarketWebViewContainer.webView + webView }, modifier = modifier.clipToBounds(), // Compose에서 WebView 사용 시, WebView가 잠깐 동안 다른 Composable을 가리는 WebView 버그 대응(https://issuetracker.google.com/issues/174233728?pli=1#comment5) ) } + +@Preview(showBackground = true, heightDp = 640) +@Composable +private fun ThemeMarketWebViewErrorPreview() { + SNUTTTheme { + ThemeMarketWebViewError( + onRetry = {}, + ) + } +} + +@Preview(showBackground = true, heightDp = 640) +@Composable +private fun ThemeMarketWebViewLoadingPreview() { + SNUTTTheme { + ThemeMarketWebViewLoading( + progress = 0.5f, + ) + } +} From 92587c5d013f682c6d7107fcc61bd9a3275da49d Mon Sep 17 00:00:00 2001 From: eastshine2741 Date: Sun, 15 Dec 2024 15:45:55 +0900 Subject: [PATCH 32/35] =?UTF-8?q?=ED=85=8C=EB=A7=88=EB=A7=88=EC=BC=93?= =?UTF-8?q?=EC=9A=A9=20string=20resource=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../snutt2/views/logged_in/thememarket/ThemeMarketWebView.kt | 4 ++-- app/src/main/res/values/strings.xml | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/thememarket/ThemeMarketWebView.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/thememarket/ThemeMarketWebView.kt index 590384d7c..7f2d3a240 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/thememarket/ThemeMarketWebView.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/thememarket/ThemeMarketWebView.kt @@ -71,7 +71,7 @@ private fun ThemeMarketWebViewError( Spacer(modifier = Modifier.height(20.dp)) Text( - text = stringResource(R.string.reviews_error_message), + text = stringResource(R.string.theme_market_webview_error), style = SNUTTTypography.subtitle1, color = SNUTTColors.Black900, ) @@ -83,7 +83,7 @@ private fun ThemeMarketWebViewError( colors = ButtonDefaults.buttonColors(backgroundColor = SNUTTColors.Sky), ) { Text( - text = stringResource(id = R.string.reviews_error_retry), + text = stringResource(id = R.string.theme_market_webview_error_retry), style = SNUTTTypography.h3, color = SNUTTColors.White900, ) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ad1b256c1..835cbd974 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -579,4 +579,6 @@ 알 수 없는 오류가 발생했습니다. 카카오톡이 설치되어 있는지 확인해보세요. 테마 마켓 + 네트워크 연결을 확인해주세요 + 다시 불러오기 From e33312beb35d49e49637e9ffe5e3294597f9145c Mon Sep 17 00:00:00 2001 From: eastshine2741 Date: Sun, 15 Dec 2024 15:52:43 +0900 Subject: [PATCH 33/35] =?UTF-8?q?LoadState.InitialLoading=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wafflestudio/snutt2/lib/android/webview/LoadState.kt | 5 ++--- .../snutt2/lib/android/webview/ReviewWebViewContainer.kt | 8 ++------ .../lib/android/webview/ThemeMarketWebViewContainer.kt | 8 ++------ .../snutt2/views/logged_in/home/reviews/ReviewPage.kt | 5 ----- .../views/logged_in/thememarket/ThemeMarketWebView.kt | 1 - 5 files changed, 6 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/lib/android/webview/LoadState.kt b/app/src/main/java/com/wafflestudio/snutt2/lib/android/webview/LoadState.kt index b725595d1..181f10341 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/lib/android/webview/LoadState.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/lib/android/webview/LoadState.kt @@ -1,8 +1,7 @@ package com.wafflestudio.snutt2.lib.android.webview sealed class LoadState { - object Success : LoadState() - object Error : LoadState() + data object Success : LoadState() + data object Error : LoadState() data class Loading(val progress: Int) : LoadState() - data class InitialLoading(val progress: Int) : LoadState() } diff --git a/app/src/main/java/com/wafflestudio/snutt2/lib/android/webview/ReviewWebViewContainer.kt b/app/src/main/java/com/wafflestudio/snutt2/lib/android/webview/ReviewWebViewContainer.kt index 2a721e143..d6ad64204 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/lib/android/webview/ReviewWebViewContainer.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/lib/android/webview/ReviewWebViewContainer.kt @@ -18,7 +18,7 @@ class ReviewWebViewContainer( private val accessToken: StateFlow, private val isDarkMode: Boolean, ) : WebViewContainer { - val loadState: MutableState = mutableStateOf(LoadState.InitialLoading(0)) + val loadState: MutableState = mutableStateOf(LoadState.Loading(0)) override val webView: WebView = WebView(context).apply { if (BuildConfig.DEBUG) { @@ -32,10 +32,7 @@ class ReviewWebViewContainer( } override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) { - loadState.value = when (loadState.value) { - is LoadState.InitialLoading -> LoadState.InitialLoading(0) - else -> LoadState.Loading(0) - } + loadState.value = LoadState.Loading(0) } override fun onReceivedError( @@ -49,7 +46,6 @@ class ReviewWebViewContainer( this.webChromeClient = object : WebChromeClient() { override fun onProgressChanged(view: WebView?, newProgress: Int) { when (loadState.value) { - is LoadState.InitialLoading -> LoadState.InitialLoading(newProgress) is LoadState.Loading -> LoadState.Loading(newProgress) else -> null }?.let { diff --git a/app/src/main/java/com/wafflestudio/snutt2/lib/android/webview/ThemeMarketWebViewContainer.kt b/app/src/main/java/com/wafflestudio/snutt2/lib/android/webview/ThemeMarketWebViewContainer.kt index 31737dc2e..20d99eabf 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/lib/android/webview/ThemeMarketWebViewContainer.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/lib/android/webview/ThemeMarketWebViewContainer.kt @@ -23,7 +23,7 @@ class ThemeMarketWebViewContainer( private val accessToken: StateFlow, private val isDarkMode: Boolean, ) : WebViewContainer { - val loadState: MutableState = mutableStateOf(LoadState.InitialLoading(0)) + val loadState: MutableState = mutableStateOf(LoadState.Loading(0)) override val webView: WebView = WebView(context).apply { if (BuildConfig.DEBUG) { @@ -37,10 +37,7 @@ class ThemeMarketWebViewContainer( } override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) { - loadState.value = when (loadState.value) { - is LoadState.InitialLoading -> LoadState.InitialLoading(0) - else -> LoadState.Loading(0) - } + loadState.value = LoadState.Loading(0) } override fun onReceivedError( @@ -54,7 +51,6 @@ class ThemeMarketWebViewContainer( this.webChromeClient = object : WebChromeClient() { override fun onProgressChanged(view: WebView?, newProgress: Int) { when (loadState.value) { - is LoadState.InitialLoading -> LoadState.InitialLoading(newProgress) is LoadState.Loading -> LoadState.Loading(newProgress) else -> null }?.let { diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/reviews/ReviewPage.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/reviews/ReviewPage.kt index 05be9b348..8f47b0233 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/reviews/ReviewPage.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/reviews/ReviewPage.kt @@ -76,11 +76,6 @@ fun ReviewWebView(height: Float = 1.0f) { onRetry = { scope.launch { webViewContainer.reload() } }, ) - is LoadState.InitialLoading -> WebViewLoading( - modifier = Modifier.fillMaxSize(), - progress = loadState.progress / 100.0f, - ) - is LoadState.Loading -> WebViewLoading( modifier = Modifier.fillMaxSize(), progress = loadState.progress / 100.0f, diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/thememarket/ThemeMarketWebView.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/thememarket/ThemeMarketWebView.kt index 7f2d3a240..1bf2b0302 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/thememarket/ThemeMarketWebView.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/thememarket/ThemeMarketWebView.kt @@ -45,7 +45,6 @@ fun ThemeMarketWebView( }, modifier = modifier, ) - is LoadState.InitialLoading -> ThemeMarketWebViewLoading(loadState.progress / 100f) is LoadState.Loading -> ThemeMarketWebViewLoading(loadState.progress / 100f) LoadState.Success -> ThemeMarketWebViewSuccess(themeMarketWebViewContainer.webView) } From 186a9942c5a8a3ca6b45df1420ef733c1a848661 Mon Sep 17 00:00:00 2001 From: eastshine2741 Date: Mon, 27 Jan 2025 22:16:20 +0900 Subject: [PATCH 34/35] =?UTF-8?q?=EC=9B=B9=EB=B7=B0=20=EC=98=81=EC=97=AD?= =?UTF-8?q?=EC=9D=B4=20=EC=A0=84=EC=B2=B4=EB=A5=BC=20=EC=B1=84=EC=9A=B0?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../views/logged_in/thememarket/ThemeMarketWebView.kt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/thememarket/ThemeMarketWebView.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/thememarket/ThemeMarketWebView.kt index 1bf2b0302..ba38f88b9 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/thememarket/ThemeMarketWebView.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/thememarket/ThemeMarketWebView.kt @@ -45,8 +45,14 @@ fun ThemeMarketWebView( }, modifier = modifier, ) - is LoadState.Loading -> ThemeMarketWebViewLoading(loadState.progress / 100f) - LoadState.Success -> ThemeMarketWebViewSuccess(themeMarketWebViewContainer.webView) + is LoadState.Loading -> ThemeMarketWebViewLoading( + progress = loadState.progress / 100f, + modifier = modifier, + ) + LoadState.Success -> ThemeMarketWebViewSuccess( + webView = themeMarketWebViewContainer.webView, + modifier = modifier, + ) } } From 65a4181e0d63f7cdc8c4de5d990b3ab41ea20fc4 Mon Sep 17 00:00:00 2001 From: eastshine2741 Date: Tue, 28 Jan 2025 20:15:43 +0900 Subject: [PATCH 35/35] =?UTF-8?q?=EB=8B=B4=EC=9D=80=20=ED=85=8C=EB=A7=88?= =?UTF-8?q?=EC=97=90=203dot=20=EC=95=84=EC=9D=B4=EC=BD=98,=20=EB=B0=94?= =?UTF-8?q?=ED=85=80=EC=8B=9C=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../theme/CustomThemeMoreActionBottomSheet.kt | 37 ++++++++++++++++++- .../home/settings/theme/ThemeConfigScreen.kt | 27 +++++++++++++- 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/CustomThemeMoreActionBottomSheet.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/CustomThemeMoreActionBottomSheet.kt index 7297b226d..4f286eea5 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/CustomThemeMoreActionBottomSheet.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/CustomThemeMoreActionBottomSheet.kt @@ -18,7 +18,7 @@ import com.wafflestudio.snutt2.components.compose.PaletteIcon import com.wafflestudio.snutt2.components.compose.TrashIcon @Composable -fun CustomThemeMoreActionBottomSheet( +fun MyCustomThemeMoreActionBottomSheet( onClickDetail: () -> Unit, onClickDuplicate: () -> Unit, onClickDelete: () -> Unit, @@ -62,3 +62,38 @@ fun CustomThemeMoreActionBottomSheet( ) } } + +@Composable +fun MarketCustomThemeMoreActionBottomSheet( + onClickDetail: () -> Unit, + onClickDelete: () -> Unit, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .background(MaterialTheme.colors.surface) + .padding(vertical = 12.dp) + .fillMaxWidth(), + ) { + MoreActionItem( + icon = { + PaletteIcon( + modifier = Modifier.size(30.dp), + colorFilter = ColorFilter.tint(MaterialTheme.colors.onSurface), + ) + }, + text = stringResource(R.string.custom_theme_action_detail_view), + onClick = { onClickDetail() }, + ) + MoreActionItem( + icon = { + TrashIcon( + modifier = Modifier.size(30.dp), + colorFilter = ColorFilter.tint(MaterialTheme.colors.onSurface), + ) + }, + text = stringResource(R.string.custom_theme_action_delete), + onClick = { onClickDelete() }, + ) + } +} diff --git a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigScreen.kt b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigScreen.kt index 7dc166fd5..03d575dd6 100644 --- a/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigScreen.kt +++ b/app/src/main/java/com/wafflestudio/snutt2/views/logged_in/home/settings/theme/ThemeConfigScreen.kt @@ -169,7 +169,7 @@ fun ThemeConfigScreen( onClickMore = { theme -> scope.launch { bottomSheet.setSheetContent { - CustomThemeMoreActionBottomSheet( + MyCustomThemeMoreActionBottomSheet( onClickDetail = { scope.launch { onNavigateToDetail(theme) @@ -209,6 +209,31 @@ fun ThemeConfigScreen( title = stringResource(R.string.theme_config_market_custom_theme), themes = marketCustomThemes, onClickItem = onNavigateToDetail, + onClickMore = { theme -> + scope.launch { + bottomSheet.setSheetContent { + MarketCustomThemeMoreActionBottomSheet( + onClickDetail = { + scope.launch { + onNavigateToDetail(theme) + bottomSheet.hide() + } + }, + onClickDelete = { + showDeleteThemeDialog( + composableStates = composableStates, + onConfirm = { + onDeleteTheme(theme) + modalState.hide() + bottomSheet.hide() + }, + ) + }, + ) + } + bottomSheet.show() + } + }, ) } Spacer(modifier = Modifier.height(4.dp))