Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

테마 마켓 추가 #383

Merged
merged 37 commits into from
Feb 22, 2025
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
c01338d
ThemeDto의 필드를 nullable로 수정
eastshine2741 Oct 20, 2024
5b26aaf
GtCurrentTableThemeUseCase 생성
eastshine2741 Oct 20, 2024
812b80a
ThemeDetailViewModel init {} 함수분리
eastshine2741 Oct 20, 2024
db22d6c
ThemeDetailViewModel.isNewTheme의 backing field 제거
eastshine2741 Oct 20, 2024
8e1b8e3
null check용 depth 제거
eastshine2741 Oct 20, 2024
e073af3
시간표 색상 새로고침 조건문 단순화
eastshine2741 Oct 20, 2024
c337ea9
도메인모델 v2 추가
eastshine2741 Oct 20, 2024
2247639
비즈니스 로직 리팩토링
eastshine2741 Nov 16, 2024
c77b904
v2 도메인 모델으로 대체
eastshine2741 Nov 16, 2024
36331b7
ThemeDetailPage 리팩토링
eastshine2741 Nov 16, 2024
48a75d0
UiState Error, Loading 대응
eastshine2741 Nov 16, 2024
c7ad540
isEditable 조건 오타 수정 & SNUTT 외 테마 색상 옮겨오기
eastshine2741 Nov 16, 2024
b23ad10
ThemeColorRow 애니메이션 버그 수정
eastshine2741 Nov 16, 2024
71dd5b6
isEditable 조건 오타 수정 2
eastshine2741 Nov 16, 2024
09f76cc
누락된 private 추가
eastshine2741 Nov 16, 2024
fd29300
ThemeDetail Preview 몇 개 추가
eastshine2741 Nov 16, 2024
4540741
ThemeListViewModel 로직 개선
eastshine2741 Nov 16, 2024
405cf21
ThemeConfigScreen 리팩토링
eastshine2741 Nov 16, 2024
2f91a1a
ThemeListViewModel -> ThemeConfigViewModel 이름 수정
eastshine2741 Nov 16, 2024
1129f1d
누락된 private 추가
eastshine2741 Nov 16, 2024
05a8a5e
ThemeConfig Preview 몇 개 추가
eastshine2741 Nov 16, 2024
ee00ec4
lint
eastshine2741 Nov 16, 2024
4a8a70b
ThemeConfigViewModel의 StateFlow가 backing field 가지도록 수정
eastshine2741 Nov 16, 2024
186e59b
네이밍 수정
eastshine2741 Nov 16, 2024
0a2fb22
담은 테마 따로 보여주기
eastshine2741 Nov 16, 2024
95f7019
더보기 탭 테마마켓 진입점 생성
eastshine2741 Sep 15, 2024
3b8730f
ThemeMarketScreen 추가
eastshine2741 Sep 16, 2024
328bf87
테마마켓 웹뷰 추가
eastshine2741 Dec 14, 2024
eb42341
담은 테마가 없으면 담은 테마 UI 보여주지 않도록 수정
eastshine2741 Dec 15, 2024
b4a27dd
lint
eastshine2741 Dec 15, 2024
3918fed
웹뷰 Loading, Error 대응
eastshine2741 Dec 15, 2024
92587c5
테마마켓용 string resource 생성
eastshine2741 Dec 15, 2024
e33312b
LoadState.InitialLoading 삭제
eastshine2741 Dec 15, 2024
078d637
Merge branch 'develop' into eastshine2741/add-custom-theme-market
eastshine2741 Jan 27, 2025
186a994
웹뷰 영역이 전체를 채우도록 수정
eastshine2741 Jan 27, 2025
65a4181
담은 테마에 3dot 아이콘, 바텀시트 추가
eastshine2741 Jan 28, 2025
d7dec71
Merge branch 'develop' into eastshine2741/add-custom-theme-market
eastshine2741 Feb 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -311,6 +312,7 @@ fun ThemeIconPreview() {
bgColor = Random.nextInt(0x0, 0xffffff),
)
},
isFromMarket = false,
),
modifier = Modifier.size(80.dp),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,6 @@ class TimetableView : View {
lecture.color.bgColor!!
} else {
BuiltInTheme.fromCode(theme).getColorByIndex(
context,
lecture.colorIndex,
)
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -12,8 +11,6 @@ interface ThemeRepository {

val builtInThemes: StateFlow<List<BuiltInTheme>>

val currentTableTheme: StateFlow<TableTheme> // FIXME: 이게 ThemeRepository에 있는 게 맞나... ThemeRepository와 CurrentTableRepository에 의존하는 UseCase를 만들어야 할까?

suspend fun fetchThemes()

fun getTheme(themeId: String): CustomTheme
Expand Down
Original file line number Diff line number Diff line change
@@ -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<List<CustomTheme>>(emptyList())
Expand All @@ -29,17 +22,9 @@ class ThemeRepositoryImpl @Inject constructor(
private val _builtInThemes = MutableStateFlow<List<BuiltInTheme>>(emptyList())
override val builtInThemes: StateFlow<List<BuiltInTheme>> = _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 }.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)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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<TableTheme> {
return combine(currentTableRepository.currentTable, themeRepository.customThemes) { table, _ ->
table?.themeId?.let { themeId ->
themeRepository.getTheme(themeId)
} ?: table?.theme?.let {
BuiltInTheme.fromCode(it)
} ?: BuiltInTheme.SNUTT
}
}
}
Original file line number Diff line number Diff line change
@@ -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()
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class ReviewWebViewContainer(
private val accessToken: StateFlow<String?>,
private val isDarkMode: Boolean,
) : WebViewContainer {
val loadState: MutableState<LoadState> = mutableStateOf(LoadState.InitialLoading(0))
val loadState: MutableState<LoadState> = mutableStateOf(LoadState.Loading(0))

override val webView: WebView = WebView(context).apply {
if (BuildConfig.DEBUG) {
Expand All @@ -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(
Expand All @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
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<String?>,
private val isDarkMode: Boolean,
) : WebViewContainer {
val loadState: MutableState<LoadState> = mutableStateOf(LoadState.Loading(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 = 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.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"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ import android.webkit.WebView
interface WebViewContainer {
val webView: WebView

suspend fun openPage(url: String?)
suspend fun openPage(url: String? = null)
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +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<ColorDto> = emptyList(),
val isCustom: Boolean = false,
val id: String?,
val theme: Int?,
val name: String?,
val colors: List<ColorDto>?,
val isCustom: Boolean?,
val status: String?,
) {

fun toTableTheme(): TableTheme {
return if (isCustom) {
return if (isCustom != false) {
CustomTheme(
id = id!!,
name = name,
colors = colors,
name = name ?: "",
colors = colors ?: emptyList(),
isFromMarket = status == "DOWNLOADED",
)
} else {
BuiltInTheme(
code = theme,
name = name,
)
BuiltInTheme.fromCode(theme ?: 0)
}
}
}
Loading