Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f8d0beb2f | ||
|
|
10886153b2 | ||
|
|
1f88d721d7 | ||
|
|
9939910c71 | ||
|
|
4f4ae62026 | ||
|
|
3560d7bf89 | ||
|
|
5cfc42774c | ||
|
|
4130ab296e | ||
|
|
87d9a1d377 | ||
|
|
70f4266ed8 | ||
|
|
7a9c2512cf | ||
|
|
a04704db40 | ||
|
|
ec808cfd0c | ||
|
|
817234a80b | ||
|
|
88cb581231 |
@@ -86,7 +86,7 @@ android {
|
|||||||
applicationId "kr.co.vividnext.sodalive"
|
applicationId "kr.co.vividnext.sodalive"
|
||||||
minSdk 23
|
minSdk 23
|
||||||
targetSdk 35
|
targetSdk 35
|
||||||
versionCode 246
|
versionCode 250
|
||||||
versionName "1.56.1"
|
versionName "1.56.1"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">VoiceOn-Test</string>
|
<string name="app_name">SodaLive-Test</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">ボイスオン-テスト</string>
|
<string name="app_name">ソーダライブ-テスト</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">보이스온-테스트</string>
|
<string name="app_name">소다라이브-테스트</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ class SeriesListAllActivity : BaseActivity<ActivitySeriesListAllBinding>(
|
|||||||
if (intent.getBooleanExtra(Constants.EXTRA_IS_COMPLETED, false)) {
|
if (intent.getBooleanExtra(Constants.EXTRA_IS_COMPLETED, false)) {
|
||||||
getString(R.string.screen_series_main_completed_title)
|
getString(R.string.screen_series_main_completed_title)
|
||||||
} else if (intent.getBooleanExtra(Constants.EXTRA_IS_ORIGINAL, false)) {
|
} else if (intent.getBooleanExtra(Constants.EXTRA_IS_ORIGINAL, false)) {
|
||||||
getString(R.string.voiceon_original_only)
|
getString(R.string.sodalive_original_only)
|
||||||
} else {
|
} else {
|
||||||
getString(R.string.screen_series_main_title)
|
getString(R.string.screen_series_main_title)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* 보이스온 - 채팅방 캐릭터 정보 모델
|
* 소다라이브 - 채팅방 캐릭터 정보 모델
|
||||||
*/
|
*/
|
||||||
package kr.co.vividnext.sodalive.chat.talk.room
|
package kr.co.vividnext.sodalive.chat.talk.room
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* 보이스온 - 채팅 메시지 모델 (서버 응답 기반)
|
* 소다라이브 - 채팅 메시지 모델 (서버 응답 기반)
|
||||||
*/
|
*/
|
||||||
package kr.co.vividnext.sodalive.chat.talk.room
|
package kr.co.vividnext.sodalive.chat.talk.room
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* 보이스온 - 채팅방 메시지 어댑터 (4.2 ViewHolder 구현)
|
* 소다라이브 - 채팅방 메시지 어댑터 (4.2 ViewHolder 구현)
|
||||||
* - 4가지 ViewType 지원
|
* - 4가지 ViewType 지원
|
||||||
* - ViewHolder 바인딩 로직 구현: 시간 포맷팅, 상태 표시, 이미지 로딩, 그룹화 처리, 타이핑 애니메이션
|
* - ViewHolder 바인딩 로직 구현: 시간 포맷팅, 상태 표시, 이미지 로딩, 그룹화 처리, 타이핑 애니메이션
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* 보이스온 - 채팅 메시지 Entity/도메인 매퍼
|
* 소다라이브 - 채팅 메시지 Entity/도메인 매퍼
|
||||||
*/
|
*/
|
||||||
package kr.co.vividnext.sodalive.chat.talk.room
|
package kr.co.vividnext.sodalive.chat.talk.room
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* 보이스온 - 채팅 메시지 매핑 유틸리티
|
* 소다라이브 - 채팅 메시지 매핑 유틸리티
|
||||||
*/
|
*/
|
||||||
package kr.co.vividnext.sodalive.chat.talk.room
|
package kr.co.vividnext.sodalive.chat.talk.room
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* 보이스온 - 점진적 메시지 로딩 응답 DTO
|
* 소다라이브 - 점진적 메시지 로딩 응답 DTO
|
||||||
*/
|
*/
|
||||||
package kr.co.vividnext.sodalive.chat.talk.room
|
package kr.co.vividnext.sodalive.chat.talk.room
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* 보이스온 - 채팅방 ChatRepository (Repository 패턴)
|
* 소다라이브 - 채팅방 ChatRepository (Repository 패턴)
|
||||||
* - 로컬 DB(Room) + 네트워크(TalkApi) 통합 관리
|
* - 로컬 DB(Room) + 네트워크(TalkApi) 통합 관리
|
||||||
* - 통합 채팅방 입장 API 호출 및 로컬 동기화
|
* - 통합 채팅방 입장 API 호출 및 로컬 동기화
|
||||||
* - 점진적 메시지 로딩
|
* - 점진적 메시지 로딩
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* 보이스온 - 통합 채팅방 입장 응답 DTO
|
* 소다라이브 - 통합 채팅방 입장 응답 DTO
|
||||||
*/
|
*/
|
||||||
package kr.co.vividnext.sodalive.chat.talk.room
|
package kr.co.vividnext.sodalive.chat.talk.room
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* 보이스온 - 채팅방 메시지 전송 상태 Enum
|
* 소다라이브 - 채팅방 메시지 전송 상태 Enum
|
||||||
*/
|
*/
|
||||||
package kr.co.vividnext.sodalive.chat.talk.room
|
package kr.co.vividnext.sodalive.chat.talk.room
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* 보이스온 - 채팅방 메시지 타입 Enum
|
* 소다라이브 - 채팅방 메시지 타입 Enum
|
||||||
*/
|
*/
|
||||||
package kr.co.vividnext.sodalive.chat.talk.room
|
package kr.co.vividnext.sodalive.chat.talk.room
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* 보이스온 - 채팅 메시지 모델 (서버 응답 전용 DTO)
|
* 소다라이브 - 채팅 메시지 모델 (서버 응답 전용 DTO)
|
||||||
*/
|
*/
|
||||||
package kr.co.vividnext.sodalive.chat.talk.room
|
package kr.co.vividnext.sodalive.chat.talk.room
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* 보이스온 - 채팅 메시지 Room TypeConverters
|
* 소다라이브 - 채팅 메시지 Room TypeConverters
|
||||||
*/
|
*/
|
||||||
package kr.co.vividnext.sodalive.chat.talk.room.db
|
package kr.co.vividnext.sodalive.chat.talk.room.db
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* 보이스온 - 채팅 메시지 DAO
|
* 소다라이브 - 채팅 메시지 DAO
|
||||||
*/
|
*/
|
||||||
package kr.co.vividnext.sodalive.chat.talk.room.db
|
package kr.co.vividnext.sodalive.chat.talk.room.db
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* 보이스온 - 채팅방 Room Database (ChatMessage 테이블 포함)
|
* 소다라이브 - 채팅방 Room Database (ChatMessage 테이블 포함)
|
||||||
*/
|
*/
|
||||||
package kr.co.vividnext.sodalive.chat.talk.room.db
|
package kr.co.vividnext.sodalive.chat.talk.room.db
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* 보이스온 - 채팅 메시지 Room Entity
|
* 소다라이브 - 채팅 메시지 Room Entity
|
||||||
*/
|
*/
|
||||||
package kr.co.vividnext.sodalive.chat.talk.room.db
|
package kr.co.vividnext.sodalive.chat.talk.room.db
|
||||||
|
|
||||||
|
|||||||
@@ -463,13 +463,13 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
|
|||||||
viewModel { CreatorChannelLiveViewModel(get()) }
|
viewModel { CreatorChannelLiveViewModel(get()) }
|
||||||
viewModel { CreatorChannelAudioViewModel(get()) }
|
viewModel { CreatorChannelAudioViewModel(get()) }
|
||||||
viewModel { CreatorChannelSeriesViewModel(get()) }
|
viewModel { CreatorChannelSeriesViewModel(get()) }
|
||||||
viewModel { CreatorChannelCommunityViewModel(get(), get()) }
|
viewModel { CreatorChannelCommunityViewModel(get()) }
|
||||||
viewModel { CreatorChannelCommunityDetailViewModel(get(), get(), get()) }
|
viewModel { CreatorChannelCommunityDetailViewModel(get(), get()) }
|
||||||
viewModel { CreatorChannelCommunityReplyViewModel(get(), get(), get()) }
|
viewModel { CreatorChannelCommunityReplyViewModel(get(), get()) }
|
||||||
viewModel { CreatorChannelFanTalkViewModel(get(), get()) }
|
viewModel { CreatorChannelFanTalkViewModel(get()) }
|
||||||
viewModel { CreatorChannelFanTalkDetailViewModel(get(), get()) }
|
viewModel { CreatorChannelFanTalkDetailViewModel(get()) }
|
||||||
viewModel { CreatorChannelFanTalkWriteViewModel(get()) }
|
viewModel { CreatorChannelFanTalkWriteViewModel(get()) }
|
||||||
viewModel { CreatorChannelDonationViewModel(get(), get()) }
|
viewModel { CreatorChannelDonationViewModel(get()) }
|
||||||
viewModel { PushNotificationListViewModel(get()) }
|
viewModel { PushNotificationListViewModel(get()) }
|
||||||
viewModel { CharacterTabViewModel(get()) }
|
viewModel { CharacterTabViewModel(get()) }
|
||||||
viewModel { CharacterDetailViewModel(get()) }
|
viewModel { CharacterDetailViewModel(get()) }
|
||||||
|
|||||||
@@ -359,7 +359,7 @@ class UserProfileViewModel(
|
|||||||
"deep_link_sub5" to "$userId"
|
"deep_link_sub5" to "$userId"
|
||||||
)
|
)
|
||||||
val shareUrl = Utils.createOneLinkUrl(params = params)
|
val shareUrl = Utils.createOneLinkUrl(params = params)
|
||||||
onSuccess("보이스온 ${nickname}님의 채널입니다.\n$shareUrl")
|
onSuccess("소다라이브 ${nickname}님의 채널입니다.\n$shareUrl")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getChannelDonationList(creatorId: Long) {
|
fun getChannelDonationList(creatorId: Long) {
|
||||||
|
|||||||
@@ -604,7 +604,7 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>(FragmentHomeBinding::infl
|
|||||||
recyclerView.adapter = originalSeriesAdapter
|
recyclerView.adapter = originalSeriesAdapter
|
||||||
|
|
||||||
binding.tvSeriesOriginalAll.setOnClickListener {
|
binding.tvSeriesOriginalAll.setOnClickListener {
|
||||||
// ‘오직 보이스온에서만’ 전체보기: isOriginal=true로 시리즈 전체보기 화면 진입
|
// ‘오직 소다라이브에서만’ 전체보기: isOriginal=true로 시리즈 전체보기 화면 진입
|
||||||
if (SharedPreferenceManager.token.isNotBlank()) {
|
if (SharedPreferenceManager.token.isNotBlank()) {
|
||||||
startActivity(
|
startActivity(
|
||||||
Intent(
|
Intent(
|
||||||
|
|||||||
@@ -436,12 +436,12 @@ class LiveRoomViewModel(
|
|||||||
|
|
||||||
val message = if (isPrivateRoom) {
|
val message = if (isPrivateRoom) {
|
||||||
"${SharedPreferenceManager.nickname}님이 귀하를 " +
|
"${SharedPreferenceManager.nickname}님이 귀하를 " +
|
||||||
"보이스온의 비공개라이브에 초대하였습니다.\n" +
|
"소다라이브의 비공개라이브에 초대하였습니다.\n" +
|
||||||
"※ 라이브 참여: $shareUrl\n" +
|
"※ 라이브 참여: $shareUrl\n" +
|
||||||
"(입장 비밀번호 : $password)"
|
"(입장 비밀번호 : $password)"
|
||||||
} else {
|
} else {
|
||||||
"${SharedPreferenceManager.nickname}님이 귀하를 " +
|
"${SharedPreferenceManager.nickname}님이 귀하를 " +
|
||||||
"보이스온의 공개라이브에 초대하였습니다.\n" +
|
"소다라이브의 공개라이브에 초대하였습니다.\n" +
|
||||||
"※ 라이브 참여: $shareUrl"
|
"※ 라이브 참여: $shareUrl"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ class MyPageFragment : BaseFragment<FragmentMyBinding>(FragmentMyBinding::inflat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.ivIntroduceVoiceon.setOnClickListener {
|
binding.ivIntroduceSodalive.setOnClickListener {
|
||||||
val url = "https://blog.naver.com/sodalive_official"
|
val url = "https://blog.naver.com/sodalive_official"
|
||||||
if (URLUtil.isValidUrl(url)) {
|
if (URLUtil.isValidUrl(url)) {
|
||||||
startActivity(Intent(Intent.ACTION_VIEW, url.toUri()))
|
startActivity(Intent(Intent.ACTION_VIEW, url.toUri()))
|
||||||
|
|||||||
@@ -7,15 +7,13 @@ import io.reactivex.rxjava3.schedulers.Schedulers
|
|||||||
import kr.co.vividnext.sodalive.base.BaseViewModel
|
import kr.co.vividnext.sodalive.base.BaseViewModel
|
||||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||||
import kr.co.vividnext.sodalive.common.UtcRelativeTimeTextFormatter
|
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.data.CreatorChannelCommunityTabResponse
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.data.CreatorChannelCommunityTabResponse
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.model.CreatorChannelCommunityPostUiModel
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.model.CreatorChannelCommunityPostUiModel
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.model.toCommunityPostUiModels
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.model.toCommunityPostUiModels
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelRepository
|
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelRepository
|
||||||
|
|
||||||
class CreatorChannelCommunityViewModel(
|
class CreatorChannelCommunityViewModel(
|
||||||
private val repository: CreatorChannelRepository,
|
private val repository: CreatorChannelRepository
|
||||||
private val relativeTimeTextFormatter: UtcRelativeTimeTextFormatter
|
|
||||||
) : BaseViewModel() {
|
) : BaseViewModel() {
|
||||||
|
|
||||||
private val _communityStateLiveData = MutableLiveData<CreatorChannelCommunityUiState>()
|
private val _communityStateLiveData = MutableLiveData<CreatorChannelCommunityUiState>()
|
||||||
@@ -158,7 +156,6 @@ class CreatorChannelCommunityViewModel(
|
|||||||
|
|
||||||
private fun CreatorChannelCommunityTabResponse.toCommunityPostUiModels(): List<CreatorChannelCommunityPostUiModel> =
|
private fun CreatorChannelCommunityTabResponse.toCommunityPostUiModels(): List<CreatorChannelCommunityPostUiModel> =
|
||||||
communityPosts.toCommunityPostUiModels(
|
communityPosts.toCommunityPostUiModels(
|
||||||
relativeTimeTextFormatter = relativeTimeTextFormatter,
|
|
||||||
isOwner = isOwner,
|
isOwner = isOwner,
|
||||||
currentUserId = SharedPreferenceManager.userId
|
currentUserId = SharedPreferenceManager.userId
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import com.bumptech.glide.request.target.Target
|
|||||||
import kr.co.vividnext.sodalive.R
|
import kr.co.vividnext.sodalive.R
|
||||||
import kr.co.vividnext.sodalive.base.BaseActivity
|
import kr.co.vividnext.sodalive.base.BaseActivity
|
||||||
import kr.co.vividnext.sodalive.common.Constants
|
import kr.co.vividnext.sodalive.common.Constants
|
||||||
|
import kr.co.vividnext.sodalive.common.formatUtcRelativeTimeText
|
||||||
import kr.co.vividnext.sodalive.databinding.ActivityCreatorChannelCommunityDetailBinding
|
import kr.co.vividnext.sodalive.databinding.ActivityCreatorChannelCommunityDetailBinding
|
||||||
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.CreatorCommunityPostMenuBottomSheetDialog
|
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.CreatorCommunityPostMenuBottomSheetDialog
|
||||||
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.CreatorCommunityReportDialog
|
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.CreatorCommunityReportDialog
|
||||||
@@ -171,7 +172,8 @@ class CreatorChannelCommunityDetailActivity : BaseActivity<ActivityCreatorChanne
|
|||||||
transformations(CircleCropTransformation())
|
transformations(CircleCropTransformation())
|
||||||
}
|
}
|
||||||
tvCreatorChannelCommunityDetailNickname.text = post.creatorNickname
|
tvCreatorChannelCommunityDetailNickname.text = post.creatorNickname
|
||||||
tvCreatorChannelCommunityDetailTime.text = post.createdAtText
|
tvCreatorChannelCommunityDetailTime.text =
|
||||||
|
formatUtcRelativeTimeText(tvCreatorChannelCommunityDetailTime.context, post.createdAtUtc)
|
||||||
btnCreatorChannelCommunityDetailMore.isVisible = post.showMore
|
btnCreatorChannelCommunityDetailMore.isVisible = post.showMore
|
||||||
tvCreatorChannelCommunityDetailBody.text = post.content
|
tvCreatorChannelCommunityDetailBody.text = post.content
|
||||||
layoutCreatorChannelCommunityDetailReaction.isVisible = post.showReaction || content.isCommentAvailable
|
layoutCreatorChannelCommunityDetailReaction.isVisible = post.showReaction || content.isCommentAvailable
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import kr.co.vividnext.sodalive.base.BaseViewModel
|
|||||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||||
import kr.co.vividnext.sodalive.common.ToastMessage
|
import kr.co.vividnext.sodalive.common.ToastMessage
|
||||||
import kr.co.vividnext.sodalive.common.UtcRelativeTimeTextFormatter
|
|
||||||
import kr.co.vividnext.sodalive.explorer.profile.creator_community.CreatorCommunityRepository
|
import kr.co.vividnext.sodalive.explorer.profile.creator_community.CreatorCommunityRepository
|
||||||
import kr.co.vividnext.sodalive.explorer.profile.creator_community.modify.ModifyCommunityPostRequest
|
import kr.co.vividnext.sodalive.explorer.profile.creator_community.modify.ModifyCommunityPostRequest
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelEvent
|
import kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelEvent
|
||||||
@@ -25,8 +24,7 @@ import okhttp3.RequestBody.Companion.toRequestBody
|
|||||||
|
|
||||||
class CreatorChannelCommunityDetailViewModel(
|
class CreatorChannelCommunityDetailViewModel(
|
||||||
private val repository: CreatorChannelRepository,
|
private val repository: CreatorChannelRepository,
|
||||||
private val legacyRepository: CreatorCommunityRepository,
|
private val legacyRepository: CreatorCommunityRepository
|
||||||
private val relativeTimeTextFormatter: UtcRelativeTimeTextFormatter
|
|
||||||
) : BaseViewModel() {
|
) : BaseViewModel() {
|
||||||
|
|
||||||
private val _detailStateLiveData = MutableLiveData<CreatorChannelCommunityDetailUiState>()
|
private val _detailStateLiveData = MutableLiveData<CreatorChannelCommunityDetailUiState>()
|
||||||
@@ -536,7 +534,7 @@ class CreatorChannelCommunityDetailViewModel(
|
|||||||
creatorId = creatorId,
|
creatorId = creatorId,
|
||||||
creatorNickname = creatorNickname,
|
creatorNickname = creatorNickname,
|
||||||
creatorProfileUrl = creatorProfileUrl,
|
creatorProfileUrl = creatorProfileUrl,
|
||||||
createdAtText = relativeTimeTextFormatter.format(createdAtUtc),
|
createdAtUtc = createdAtUtc,
|
||||||
content = content,
|
content = content,
|
||||||
imageUrl = imageUrl.takeUnless { isLocked() },
|
imageUrl = imageUrl.takeUnless { isLocked() },
|
||||||
audioUrl = audioUrl.takeUnless { isLocked() },
|
audioUrl = audioUrl.takeUnless { isLocked() },
|
||||||
@@ -566,7 +564,6 @@ class CreatorChannelCommunityDetailViewModel(
|
|||||||
memberNickname = writerNickname,
|
memberNickname = writerNickname,
|
||||||
memberProfileUrl = writerProfileImageUrl,
|
memberProfileUrl = writerProfileImageUrl,
|
||||||
createdAtUtc = createdAtUtc,
|
createdAtUtc = createdAtUtc,
|
||||||
createdAtText = relativeTimeTextFormatter.format(createdAtUtc),
|
|
||||||
comment = content,
|
comment = content,
|
||||||
likeCount = 0,
|
likeCount = 0,
|
||||||
replyCount = if (latestReply != null) 1 else 0,
|
replyCount = if (latestReply != null) 1 else 0,
|
||||||
@@ -586,7 +583,6 @@ class CreatorChannelCommunityDetailViewModel(
|
|||||||
memberNickname = writerNickname,
|
memberNickname = writerNickname,
|
||||||
memberProfileUrl = writerProfileImageUrl,
|
memberProfileUrl = writerProfileImageUrl,
|
||||||
createdAtUtc = createdAtUtc,
|
createdAtUtc = createdAtUtc,
|
||||||
createdAtText = relativeTimeTextFormatter.format(createdAtUtc),
|
|
||||||
comment = content,
|
comment = content,
|
||||||
likeCount = 0,
|
likeCount = 0,
|
||||||
isLiked = false,
|
isLiked = false,
|
||||||
@@ -625,7 +621,7 @@ data class CreatorChannelCommunityPostDetailUiModel(
|
|||||||
val creatorId: Long,
|
val creatorId: Long,
|
||||||
val creatorNickname: String,
|
val creatorNickname: String,
|
||||||
val creatorProfileUrl: String?,
|
val creatorProfileUrl: String?,
|
||||||
val createdAtText: String,
|
val createdAtUtc: String,
|
||||||
val content: String,
|
val content: String,
|
||||||
val imageUrl: String?,
|
val imageUrl: String?,
|
||||||
val audioUrl: String?,
|
val audioUrl: String?,
|
||||||
@@ -648,7 +644,6 @@ data class CreatorChannelCommunityCommentUiModel(
|
|||||||
val memberNickname: String,
|
val memberNickname: String,
|
||||||
val memberProfileUrl: String?,
|
val memberProfileUrl: String?,
|
||||||
val createdAtUtc: String,
|
val createdAtUtc: String,
|
||||||
val createdAtText: String,
|
|
||||||
val comment: String,
|
val comment: String,
|
||||||
val likeCount: Int,
|
val likeCount: Int,
|
||||||
val replyCount: Int,
|
val replyCount: Int,
|
||||||
@@ -665,7 +660,6 @@ data class CreatorChannelCommunityReplyUiModel(
|
|||||||
val memberNickname: String,
|
val memberNickname: String,
|
||||||
val memberProfileUrl: String?,
|
val memberProfileUrl: String?,
|
||||||
val createdAtUtc: String,
|
val createdAtUtc: String,
|
||||||
val createdAtText: String,
|
|
||||||
val comment: String,
|
val comment: String,
|
||||||
val likeCount: Int,
|
val likeCount: Int,
|
||||||
val isLiked: Boolean,
|
val isLiked: Boolean,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
|||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import kr.co.vividnext.sodalive.R
|
import kr.co.vividnext.sodalive.R
|
||||||
import kr.co.vividnext.sodalive.base.BaseActivity
|
import kr.co.vividnext.sodalive.base.BaseActivity
|
||||||
|
import kr.co.vividnext.sodalive.common.formatUtcRelativeTimeText
|
||||||
import kr.co.vividnext.sodalive.databinding.ActivityCreatorChannelCommunityReplyBinding
|
import kr.co.vividnext.sodalive.databinding.ActivityCreatorChannelCommunityReplyBinding
|
||||||
import kr.co.vividnext.sodalive.v2.components.modal.V2ModalDialog
|
import kr.co.vividnext.sodalive.v2.components.modal.V2ModalDialog
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.detail.CreatorChannelCommunityCommentUiModel
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.detail.CreatorChannelCommunityCommentUiModel
|
||||||
@@ -110,6 +111,11 @@ class CreatorChannelCommunityReplyActivity : BaseActivity<ActivityCreatorChannel
|
|||||||
onModifyClick = { comment -> viewModel.startParentCommentEdit(comment.comment) },
|
onModifyClick = { comment -> viewModel.startParentCommentEdit(comment.comment) },
|
||||||
onDeleteClick = { showDeleteParentCommentDialog() }
|
onDeleteClick = { showDeleteParentCommentDialog() }
|
||||||
)
|
)
|
||||||
|
layoutCreatorChannelCommunityReplyParent.tvCreatorChannelCommunityCommentTime.text =
|
||||||
|
formatUtcRelativeTimeText(
|
||||||
|
layoutCreatorChannelCommunityReplyParent.tvCreatorChannelCommunityCommentTime.context,
|
||||||
|
content.parentComment.createdAtUtc
|
||||||
|
)
|
||||||
layoutCreatorChannelCommunityReplyParent.layoutCreatorChannelCommunityCommentLatestReply.isVisible = false
|
layoutCreatorChannelCommunityReplyParent.layoutCreatorChannelCommunityCommentLatestReply.isVisible = false
|
||||||
replyAdapter.submitItems(content.replies)
|
replyAdapter.submitItems(content.replies)
|
||||||
if (etCreatorChannelCommunityReply.text.toString() != content.replyInput) {
|
if (etCreatorChannelCommunityReply.text.toString() != content.replyInput) {
|
||||||
@@ -183,7 +189,6 @@ class CreatorChannelCommunityReplyActivity : BaseActivity<ActivityCreatorChannel
|
|||||||
memberNickname = intent.getStringExtra(EXTRA_MEMBER_NICKNAME).orEmpty(),
|
memberNickname = intent.getStringExtra(EXTRA_MEMBER_NICKNAME).orEmpty(),
|
||||||
memberProfileUrl = intent.getStringExtra(EXTRA_MEMBER_PROFILE_URL),
|
memberProfileUrl = intent.getStringExtra(EXTRA_MEMBER_PROFILE_URL),
|
||||||
createdAtUtc = intent.getStringExtra(EXTRA_CREATED_AT_UTC).orEmpty(),
|
createdAtUtc = intent.getStringExtra(EXTRA_CREATED_AT_UTC).orEmpty(),
|
||||||
createdAtText = intent.getStringExtra(EXTRA_CREATED_AT_TEXT).orEmpty(),
|
|
||||||
comment = intent.getStringExtra(EXTRA_COMMENT).orEmpty(),
|
comment = intent.getStringExtra(EXTRA_COMMENT).orEmpty(),
|
||||||
likeCount = intent.getIntExtra(EXTRA_LIKE_COUNT, 0),
|
likeCount = intent.getIntExtra(EXTRA_LIKE_COUNT, 0),
|
||||||
replyCount = intent.getIntExtra(EXTRA_REPLY_COUNT, 0),
|
replyCount = intent.getIntExtra(EXTRA_REPLY_COUNT, 0),
|
||||||
@@ -201,7 +206,6 @@ class CreatorChannelCommunityReplyActivity : BaseActivity<ActivityCreatorChannel
|
|||||||
private const val EXTRA_MEMBER_NICKNAME = "extra_member_nickname"
|
private const val EXTRA_MEMBER_NICKNAME = "extra_member_nickname"
|
||||||
private const val EXTRA_MEMBER_PROFILE_URL = "extra_member_profile_url"
|
private const val EXTRA_MEMBER_PROFILE_URL = "extra_member_profile_url"
|
||||||
private const val EXTRA_CREATED_AT_UTC = "extra_created_at_utc"
|
private const val EXTRA_CREATED_AT_UTC = "extra_created_at_utc"
|
||||||
private const val EXTRA_CREATED_AT_TEXT = "extra_created_at_text"
|
|
||||||
private const val EXTRA_COMMENT = "extra_comment"
|
private const val EXTRA_COMMENT = "extra_comment"
|
||||||
private const val EXTRA_LIKE_COUNT = "extra_like_count"
|
private const val EXTRA_LIKE_COUNT = "extra_like_count"
|
||||||
private const val EXTRA_REPLY_COUNT = "extra_reply_count"
|
private const val EXTRA_REPLY_COUNT = "extra_reply_count"
|
||||||
@@ -217,7 +221,6 @@ class CreatorChannelCommunityReplyActivity : BaseActivity<ActivityCreatorChannel
|
|||||||
.putExtra(EXTRA_MEMBER_NICKNAME, parentComment.memberNickname)
|
.putExtra(EXTRA_MEMBER_NICKNAME, parentComment.memberNickname)
|
||||||
.putExtra(EXTRA_MEMBER_PROFILE_URL, parentComment.memberProfileUrl)
|
.putExtra(EXTRA_MEMBER_PROFILE_URL, parentComment.memberProfileUrl)
|
||||||
.putExtra(EXTRA_CREATED_AT_UTC, parentComment.createdAtUtc)
|
.putExtra(EXTRA_CREATED_AT_UTC, parentComment.createdAtUtc)
|
||||||
.putExtra(EXTRA_CREATED_AT_TEXT, parentComment.createdAtText)
|
|
||||||
.putExtra(EXTRA_COMMENT, parentComment.comment)
|
.putExtra(EXTRA_COMMENT, parentComment.comment)
|
||||||
.putExtra(EXTRA_LIKE_COUNT, parentComment.likeCount)
|
.putExtra(EXTRA_LIKE_COUNT, parentComment.likeCount)
|
||||||
.putExtra(EXTRA_REPLY_COUNT, parentComment.replyCount)
|
.putExtra(EXTRA_REPLY_COUNT, parentComment.replyCount)
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import kr.co.vividnext.sodalive.base.BaseViewModel
|
|||||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||||
import kr.co.vividnext.sodalive.common.ToastMessage
|
import kr.co.vividnext.sodalive.common.ToastMessage
|
||||||
import kr.co.vividnext.sodalive.common.UtcRelativeTimeTextFormatter
|
|
||||||
import kr.co.vividnext.sodalive.explorer.profile.creator_community.CreatorCommunityRepository
|
import kr.co.vividnext.sodalive.explorer.profile.creator_community.CreatorCommunityRepository
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelEvent
|
import kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelEvent
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.detail.CreatorChannelCommunityCommentUiModel
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.detail.CreatorChannelCommunityCommentUiModel
|
||||||
@@ -21,8 +20,7 @@ import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelRepository
|
|||||||
|
|
||||||
class CreatorChannelCommunityReplyViewModel(
|
class CreatorChannelCommunityReplyViewModel(
|
||||||
private val repository: CreatorChannelRepository,
|
private val repository: CreatorChannelRepository,
|
||||||
private val legacyRepository: CreatorCommunityRepository,
|
private val legacyRepository: CreatorCommunityRepository
|
||||||
private val relativeTimeTextFormatter: UtcRelativeTimeTextFormatter
|
|
||||||
) : BaseViewModel() {
|
) : BaseViewModel() {
|
||||||
|
|
||||||
private val _replyStateLiveData = MutableLiveData<CreatorChannelCommunityReplyUiState>()
|
private val _replyStateLiveData = MutableLiveData<CreatorChannelCommunityReplyUiState>()
|
||||||
@@ -57,20 +55,17 @@ class CreatorChannelCommunityReplyViewModel(
|
|||||||
private var isModifyingReply = false
|
private var isModifyingReply = false
|
||||||
|
|
||||||
fun loadReplies(postId: Long, parentComment: CreatorChannelCommunityCommentUiModel) {
|
fun loadReplies(postId: Long, parentComment: CreatorChannelCommunityCommentUiModel) {
|
||||||
val formattedParentComment = parentComment.copy(
|
|
||||||
createdAtText = relativeTimeTextFormatter.format(parentComment.createdAtUtc)
|
|
||||||
)
|
|
||||||
this.postId = postId
|
this.postId = postId
|
||||||
this.parentComment = formattedParentComment
|
this.parentComment = parentComment
|
||||||
replyRequestGeneration++
|
replyRequestGeneration++
|
||||||
isLoadingReplies = false
|
isLoadingReplies = false
|
||||||
_replyStateLiveData.value = CreatorChannelCommunityReplyUiState.Content(
|
_replyStateLiveData.value = CreatorChannelCommunityReplyUiState.Content(
|
||||||
parentComment = formattedParentComment,
|
parentComment = parentComment,
|
||||||
replies = emptyList(),
|
replies = emptyList(),
|
||||||
replyPage = FIRST_PAGE,
|
replyPage = FIRST_PAGE,
|
||||||
hasNextReply = false
|
hasNextReply = false
|
||||||
)
|
)
|
||||||
if (formattedParentComment.commentId > 0) {
|
if (parentComment.commentId > 0) {
|
||||||
loadReplyPage(page = FIRST_PAGE, append = false)
|
loadReplyPage(page = FIRST_PAGE, append = false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -486,7 +481,6 @@ class CreatorChannelCommunityReplyViewModel(
|
|||||||
memberNickname = writerNickname,
|
memberNickname = writerNickname,
|
||||||
memberProfileUrl = writerProfileImageUrl,
|
memberProfileUrl = writerProfileImageUrl,
|
||||||
createdAtUtc = createdAtUtc,
|
createdAtUtc = createdAtUtc,
|
||||||
createdAtText = relativeTimeTextFormatter.format(createdAtUtc),
|
|
||||||
comment = content,
|
comment = content,
|
||||||
likeCount = 0,
|
likeCount = 0,
|
||||||
isLiked = false,
|
isLiked = false,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import androidx.core.view.isVisible
|
|||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import coil.transform.CircleCropTransformation
|
import coil.transform.CircleCropTransformation
|
||||||
import kr.co.vividnext.sodalive.R
|
import kr.co.vividnext.sodalive.R
|
||||||
|
import kr.co.vividnext.sodalive.common.formatUtcRelativeTimeText
|
||||||
import kr.co.vividnext.sodalive.databinding.ItemCreatorChannelCommunityReplyBinding
|
import kr.co.vividnext.sodalive.databinding.ItemCreatorChannelCommunityReplyBinding
|
||||||
import kr.co.vividnext.sodalive.extensions.loadUrl
|
import kr.co.vividnext.sodalive.extensions.loadUrl
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.detail.CreatorChannelCommunityReplyUiModel
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.detail.CreatorChannelCommunityReplyUiModel
|
||||||
@@ -51,7 +52,8 @@ class CreatorChannelCommunityReplyAdapter(
|
|||||||
transformations(CircleCropTransformation())
|
transformations(CircleCropTransformation())
|
||||||
}
|
}
|
||||||
tvCreatorChannelCommunityReplyItemNickname.text = item.memberNickname
|
tvCreatorChannelCommunityReplyItemNickname.text = item.memberNickname
|
||||||
tvCreatorChannelCommunityReplyItemTime.text = item.createdAtText
|
tvCreatorChannelCommunityReplyItemTime.text =
|
||||||
|
formatUtcRelativeTimeText(tvCreatorChannelCommunityReplyItemTime.context, item.createdAtUtc)
|
||||||
tvCreatorChannelCommunityReplyItemBody.text = item.comment
|
tvCreatorChannelCommunityReplyItemBody.text = item.comment
|
||||||
ivCreatorChannelCommunityReplyItemMore.isVisible = item.canShowMore
|
ivCreatorChannelCommunityReplyItemMore.isVisible = item.canShowMore
|
||||||
ivCreatorChannelCommunityReplyItemMore.setOnClickListener { anchor ->
|
ivCreatorChannelCommunityReplyItemMore.setOnClickListener { anchor ->
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import androidx.core.view.isVisible
|
|||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import coil.transform.CircleCropTransformation
|
import coil.transform.CircleCropTransformation
|
||||||
import kr.co.vividnext.sodalive.R
|
import kr.co.vividnext.sodalive.R
|
||||||
|
import kr.co.vividnext.sodalive.common.formatUtcRelativeTimeText
|
||||||
import kr.co.vividnext.sodalive.databinding.ItemCreatorChannelCommunityCommentBinding
|
import kr.co.vividnext.sodalive.databinding.ItemCreatorChannelCommunityCommentBinding
|
||||||
import kr.co.vividnext.sodalive.extensions.loadUrl
|
import kr.co.vividnext.sodalive.extensions.loadUrl
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.detail.CreatorChannelCommunityCommentUiModel
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.detail.CreatorChannelCommunityCommentUiModel
|
||||||
@@ -70,7 +71,8 @@ class CreatorChannelCommunityCommentAdapter(
|
|||||||
transformations(CircleCropTransformation())
|
transformations(CircleCropTransformation())
|
||||||
}
|
}
|
||||||
tvCreatorChannelCommunityCommentNickname.text = item.memberNickname
|
tvCreatorChannelCommunityCommentNickname.text = item.memberNickname
|
||||||
tvCreatorChannelCommunityCommentTime.text = item.createdAtText
|
tvCreatorChannelCommunityCommentTime.text =
|
||||||
|
formatUtcRelativeTimeText(tvCreatorChannelCommunityCommentTime.context, item.createdAtUtc)
|
||||||
tvCreatorChannelCommunityCommentBody.text = item.comment
|
tvCreatorChannelCommunityCommentBody.text = item.comment
|
||||||
ivCreatorChannelCommunityCommentMore.isVisible = item.canShowMore && onDeleteClick != null
|
ivCreatorChannelCommunityCommentMore.isVisible = item.canShowMore && onDeleteClick != null
|
||||||
ivCreatorChannelCommunityCommentMore.setOnClickListener { anchor ->
|
ivCreatorChannelCommunityCommentMore.setOnClickListener { anchor ->
|
||||||
|
|||||||
@@ -1,20 +1,17 @@
|
|||||||
package kr.co.vividnext.sodalive.v2.creator.channel.community.model
|
package kr.co.vividnext.sodalive.v2.creator.channel.community.model
|
||||||
|
|
||||||
import kr.co.vividnext.sodalive.common.UtcRelativeTimeTextFormatter
|
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.data.CreatorChannelCommunityPostResponse
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.data.CreatorChannelCommunityPostResponse
|
||||||
|
|
||||||
private const val GRID_PREVIEW_MAX_LENGTH = 24
|
private const val GRID_PREVIEW_MAX_LENGTH = 24
|
||||||
|
|
||||||
fun List<CreatorChannelCommunityPostResponse>.toCommunityPostUiModels(
|
fun List<CreatorChannelCommunityPostResponse>.toCommunityPostUiModels(
|
||||||
relativeTimeTextFormatter: UtcRelativeTimeTextFormatter,
|
|
||||||
isOwner: Boolean,
|
isOwner: Boolean,
|
||||||
currentUserId: Long
|
currentUserId: Long
|
||||||
): List<CreatorChannelCommunityPostUiModel> = map {
|
): List<CreatorChannelCommunityPostUiModel> = map {
|
||||||
it.toCommunityPostUiModel(relativeTimeTextFormatter, isOwner, currentUserId)
|
it.toCommunityPostUiModel(isOwner, currentUserId)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CreatorChannelCommunityPostResponse.toCommunityPostUiModel(
|
private fun CreatorChannelCommunityPostResponse.toCommunityPostUiModel(
|
||||||
relativeTimeTextFormatter: UtcRelativeTimeTextFormatter,
|
|
||||||
isOwner: Boolean,
|
isOwner: Boolean,
|
||||||
currentUserId: Long
|
currentUserId: Long
|
||||||
): CreatorChannelCommunityPostUiModel {
|
): CreatorChannelCommunityPostUiModel {
|
||||||
@@ -26,7 +23,7 @@ private fun CreatorChannelCommunityPostResponse.toCommunityPostUiModel(
|
|||||||
creatorId = creatorId,
|
creatorId = creatorId,
|
||||||
creatorNickname = creatorNickname,
|
creatorNickname = creatorNickname,
|
||||||
creatorProfileUrl = creatorProfileUrl,
|
creatorProfileUrl = creatorProfileUrl,
|
||||||
createdAtText = relativeTimeTextFormatter.format(createdAtUtc),
|
createdAtUtc = createdAtUtc,
|
||||||
content = content,
|
content = content,
|
||||||
imageUrl = visibleImageUrl,
|
imageUrl = visibleImageUrl,
|
||||||
price = price,
|
price = price,
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ data class CreatorChannelCommunityPostUiModel(
|
|||||||
val creatorId: Long,
|
val creatorId: Long,
|
||||||
val creatorNickname: String,
|
val creatorNickname: String,
|
||||||
val creatorProfileUrl: String,
|
val creatorProfileUrl: String,
|
||||||
val createdAtText: String,
|
val createdAtUtc: String,
|
||||||
val content: String,
|
val content: String,
|
||||||
val imageUrl: String?,
|
val imageUrl: String?,
|
||||||
val price: Int,
|
val price: Int,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import com.bumptech.glide.load.engine.GlideException
|
|||||||
import com.bumptech.glide.request.RequestListener
|
import com.bumptech.glide.request.RequestListener
|
||||||
import com.bumptech.glide.request.target.Target
|
import com.bumptech.glide.request.target.Target
|
||||||
import kr.co.vividnext.sodalive.R
|
import kr.co.vividnext.sodalive.R
|
||||||
|
import kr.co.vividnext.sodalive.common.formatUtcRelativeTimeText
|
||||||
import kr.co.vividnext.sodalive.databinding.ItemCreatorChannelCommunityListBinding
|
import kr.co.vividnext.sodalive.databinding.ItemCreatorChannelCommunityListBinding
|
||||||
import kr.co.vividnext.sodalive.extensions.dpToPx
|
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||||
import kr.co.vividnext.sodalive.extensions.loadUrl
|
import kr.co.vividnext.sodalive.extensions.loadUrl
|
||||||
@@ -68,7 +69,8 @@ class CreatorChannelCommunityListAdapter(
|
|||||||
transformations(CircleCropTransformation())
|
transformations(CircleCropTransformation())
|
||||||
}
|
}
|
||||||
tvCreatorChannelCommunityListNickname.text = item.creatorNickname
|
tvCreatorChannelCommunityListNickname.text = item.creatorNickname
|
||||||
tvCreatorChannelCommunityListTime.text = item.createdAtText
|
tvCreatorChannelCommunityListTime.text =
|
||||||
|
formatUtcRelativeTimeText(tvCreatorChannelCommunityListTime.context, item.createdAtUtc)
|
||||||
layoutCreatorChannelCommunityListNotice.isVisible = item.showNotice
|
layoutCreatorChannelCommunityListNotice.isVisible = item.showNotice
|
||||||
tvCreatorChannelCommunityListBody.text = item.content
|
tvCreatorChannelCommunityListBody.text = item.content
|
||||||
layoutCreatorChannelCommunityListReaction.isVisible = item.showReaction || item.showComment
|
layoutCreatorChannelCommunityListReaction.isVisible = item.showReaction || item.showComment
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ private data class CreatorChannelDonationItemLayoutKey(
|
|||||||
val nickname: String,
|
val nickname: String,
|
||||||
val can: Int,
|
val can: Int,
|
||||||
val message: String,
|
val message: String,
|
||||||
val createdAtText: String
|
val createdAtUtc: String
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun CreatorChannelDonationUiState.Content.toContentLayoutKey(): CreatorChannelDonationContentLayoutKey {
|
private fun CreatorChannelDonationUiState.Content.toContentLayoutKey(): CreatorChannelDonationContentLayoutKey {
|
||||||
@@ -206,7 +206,7 @@ private fun CreatorChannelDonationUiState.Content.toContentLayoutKey(): CreatorC
|
|||||||
nickname = donation.nickname,
|
nickname = donation.nickname,
|
||||||
can = donation.can,
|
can = donation.can,
|
||||||
message = donation.message,
|
message = donation.message,
|
||||||
createdAtText = donation.createdAtText
|
createdAtUtc = donation.createdAtUtc
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ import io.reactivex.rxjava3.schedulers.Schedulers
|
|||||||
import kr.co.vividnext.sodalive.base.BaseViewModel
|
import kr.co.vividnext.sodalive.base.BaseViewModel
|
||||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||||
import kr.co.vividnext.sodalive.common.SodaLiveApplicationHolder
|
|
||||||
import kr.co.vividnext.sodalive.common.UtcRelativeTimeTextFormatter
|
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelRepository
|
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelRepository
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.donation.data.CreatorChannelDonationTabResponse
|
import kr.co.vividnext.sodalive.v2.creator.channel.donation.data.CreatorChannelDonationTabResponse
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.donation.model.CreatorChannelDonationRankingUiModel
|
import kr.co.vividnext.sodalive.v2.creator.channel.donation.model.CreatorChannelDonationRankingUiModel
|
||||||
@@ -17,15 +15,13 @@ import kr.co.vividnext.sodalive.v2.creator.channel.donation.model.toDonationRank
|
|||||||
import kr.co.vividnext.sodalive.v2.creator.channel.donation.model.toDonationUiModels
|
import kr.co.vividnext.sodalive.v2.creator.channel.donation.model.toDonationUiModels
|
||||||
|
|
||||||
class CreatorChannelDonationViewModel(
|
class CreatorChannelDonationViewModel(
|
||||||
private val repository: CreatorChannelRepository,
|
private val repository: CreatorChannelRepository
|
||||||
private val relativeTimeTextFormatter: UtcRelativeTimeTextFormatter
|
|
||||||
) : BaseViewModel() {
|
) : BaseViewModel() {
|
||||||
|
|
||||||
private val _donationStateLiveData = MutableLiveData<CreatorChannelDonationUiState>()
|
private val _donationStateLiveData = MutableLiveData<CreatorChannelDonationUiState>()
|
||||||
val donationStateLiveData: LiveData<CreatorChannelDonationUiState>
|
val donationStateLiveData: LiveData<CreatorChannelDonationUiState>
|
||||||
get() = _donationStateLiveData
|
get() = _donationStateLiveData
|
||||||
|
|
||||||
private val context = SodaLiveApplicationHolder.get()
|
|
||||||
private var creatorId: Long = 0L
|
private var creatorId: Long = 0L
|
||||||
private var isOwner: Boolean = false
|
private var isOwner: Boolean = false
|
||||||
private var requestGeneration: Int = 0
|
private var requestGeneration: Int = 0
|
||||||
@@ -222,7 +218,7 @@ class CreatorChannelDonationViewModel(
|
|||||||
rankings.toDonationRankingUiModels()
|
rankings.toDonationRankingUiModels()
|
||||||
|
|
||||||
private fun CreatorChannelDonationTabResponse.toDonationUiModels(): List<CreatorChannelDonationUiModel> =
|
private fun CreatorChannelDonationTabResponse.toDonationUiModels(): List<CreatorChannelDonationUiModel> =
|
||||||
donations.toDonationUiModels(context, relativeTimeTextFormatter)
|
donations.toDonationUiModels()
|
||||||
|
|
||||||
private fun authToken(): String = "Bearer ${SharedPreferenceManager.token}"
|
private fun authToken(): String = "Bearer ${SharedPreferenceManager.token}"
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,15 @@
|
|||||||
package kr.co.vividnext.sodalive.v2.creator.channel.donation.model
|
package kr.co.vividnext.sodalive.v2.creator.channel.donation.model
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.annotation.ColorRes
|
import androidx.annotation.ColorRes
|
||||||
import kr.co.vividnext.sodalive.R
|
import kr.co.vividnext.sodalive.R
|
||||||
import kr.co.vividnext.sodalive.common.UtcRelativeTimeTextFormatter
|
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.donation.data.CreatorChannelDonationResponse
|
import kr.co.vividnext.sodalive.v2.creator.channel.donation.data.CreatorChannelDonationResponse
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.donation.data.MemberDonationRankingResponse
|
import kr.co.vividnext.sodalive.v2.creator.channel.donation.data.MemberDonationRankingResponse
|
||||||
|
|
||||||
fun List<MemberDonationRankingResponse>.toDonationRankingUiModels(): List<CreatorChannelDonationRankingUiModel> =
|
fun List<MemberDonationRankingResponse>.toDonationRankingUiModels(): List<CreatorChannelDonationRankingUiModel> =
|
||||||
mapIndexed { index, response -> response.toDonationRankingUiModel(rank = index + 1) }
|
mapIndexed { index, response -> response.toDonationRankingUiModel(rank = index + 1) }
|
||||||
|
|
||||||
fun List<CreatorChannelDonationResponse>.toDonationUiModels(
|
fun List<CreatorChannelDonationResponse>.toDonationUiModels(): List<CreatorChannelDonationUiModel> =
|
||||||
context: Context,
|
map { it.toDonationUiModel() }
|
||||||
relativeTimeTextFormatter: UtcRelativeTimeTextFormatter
|
|
||||||
): List<CreatorChannelDonationUiModel> = map { it.toDonationUiModel(context, relativeTimeTextFormatter) }
|
|
||||||
|
|
||||||
private fun MemberDonationRankingResponse.toDonationRankingUiModel(rank: Int) = CreatorChannelDonationRankingUiModel(
|
private fun MemberDonationRankingResponse.toDonationRankingUiModel(rank: Int) = CreatorChannelDonationRankingUiModel(
|
||||||
rank = rank,
|
rank = rank,
|
||||||
@@ -23,15 +19,12 @@ private fun MemberDonationRankingResponse.toDonationRankingUiModel(rank: Int) =
|
|||||||
donationCan = donationCan
|
donationCan = donationCan
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun CreatorChannelDonationResponse.toDonationUiModel(
|
private fun CreatorChannelDonationResponse.toDonationUiModel() = CreatorChannelDonationUiModel(
|
||||||
context: Context,
|
|
||||||
relativeTimeTextFormatter: UtcRelativeTimeTextFormatter
|
|
||||||
) = CreatorChannelDonationUiModel(
|
|
||||||
nickname = nickname,
|
nickname = nickname,
|
||||||
profileImageUrl = profileImageUrl,
|
profileImageUrl = profileImageUrl,
|
||||||
can = can,
|
can = can,
|
||||||
message = message.takeUnless { it.isBlank() } ?: context.getString(R.string.creator_channel_donation_fallback_message, can),
|
message = message,
|
||||||
createdAtText = relativeTimeTextFormatter.format(createdAtUtc),
|
createdAtUtc = createdAtUtc,
|
||||||
headerColorResId = calculateDonationHeaderColorRes(can)
|
headerColorResId = calculateDonationHeaderColorRes(can)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,6 @@ data class CreatorChannelDonationUiModel(
|
|||||||
val profileImageUrl: String,
|
val profileImageUrl: String,
|
||||||
val can: Int,
|
val can: Int,
|
||||||
val message: String,
|
val message: String,
|
||||||
val createdAtText: String,
|
val createdAtUtc: String,
|
||||||
@param:ColorRes val headerColorResId: Int
|
@param:ColorRes val headerColorResId: Int
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import android.view.ViewGroup
|
|||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import kr.co.vividnext.sodalive.R
|
||||||
|
import kr.co.vividnext.sodalive.common.formatUtcRelativeTimeText
|
||||||
import kr.co.vividnext.sodalive.databinding.ItemCreatorChannelDonationBinding
|
import kr.co.vividnext.sodalive.databinding.ItemCreatorChannelDonationBinding
|
||||||
import kr.co.vividnext.sodalive.databinding.ItemCreatorChannelDonationEmptyBinding
|
import kr.co.vividnext.sodalive.databinding.ItemCreatorChannelDonationEmptyBinding
|
||||||
import kr.co.vividnext.sodalive.databinding.ItemCreatorChannelDonationRankingBinding
|
import kr.co.vividnext.sodalive.databinding.ItemCreatorChannelDonationRankingBinding
|
||||||
@@ -115,6 +117,7 @@ class CreatorChannelDonationAdapter(
|
|||||||
) : RecyclerView.ViewHolder(binding.root) {
|
) : RecyclerView.ViewHolder(binding.root) {
|
||||||
|
|
||||||
fun bind(item: CreatorChannelDonationUiModel) = with(binding) {
|
fun bind(item: CreatorChannelDonationUiModel) = with(binding) {
|
||||||
|
val context = root.context
|
||||||
bindCreatorChannelDonationCard(
|
bindCreatorChannelDonationCard(
|
||||||
headerView = layoutCreatorChannelDonationHeader,
|
headerView = layoutCreatorChannelDonationHeader,
|
||||||
profileView = ivCreatorChannelDonationProfile,
|
profileView = ivCreatorChannelDonationProfile,
|
||||||
@@ -125,9 +128,10 @@ class CreatorChannelDonationAdapter(
|
|||||||
headerColorResId = item.headerColorResId,
|
headerColorResId = item.headerColorResId,
|
||||||
profileImageUrl = item.profileImageUrl,
|
profileImageUrl = item.profileImageUrl,
|
||||||
nickname = item.nickname,
|
nickname = item.nickname,
|
||||||
createdAtText = item.createdAtText,
|
createdAtText = formatUtcRelativeTimeText(context, item.createdAtUtc),
|
||||||
can = item.can,
|
can = item.can,
|
||||||
message = item.message
|
message = item.message.takeUnless { it.isBlank() }
|
||||||
|
?: context.getString(R.string.creator_channel_donation_fallback_message, item.can)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,15 +9,13 @@ import kr.co.vividnext.sodalive.common.SodaLiveApplicationHolder
|
|||||||
import kr.co.vividnext.sodalive.base.BaseViewModel
|
import kr.co.vividnext.sodalive.base.BaseViewModel
|
||||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||||
import kr.co.vividnext.sodalive.common.UtcRelativeTimeTextFormatter
|
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelRepository
|
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelRepository
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.data.CreatorChannelFanTalkTabResponse
|
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.data.CreatorChannelFanTalkTabResponse
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.model.CreatorChannelFanTalkUiModel
|
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.model.CreatorChannelFanTalkUiModel
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.model.toFanTalkUiModels
|
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.model.toFanTalkUiModels
|
||||||
|
|
||||||
class CreatorChannelFanTalkViewModel(
|
class CreatorChannelFanTalkViewModel(
|
||||||
private val repository: CreatorChannelRepository,
|
private val repository: CreatorChannelRepository
|
||||||
private val relativeTimeTextFormatter: UtcRelativeTimeTextFormatter
|
|
||||||
) : BaseViewModel() {
|
) : BaseViewModel() {
|
||||||
|
|
||||||
private val _fanTalkStateLiveData = MutableLiveData<CreatorChannelFanTalkUiState>()
|
private val _fanTalkStateLiveData = MutableLiveData<CreatorChannelFanTalkUiState>()
|
||||||
@@ -198,7 +196,6 @@ class CreatorChannelFanTalkViewModel(
|
|||||||
|
|
||||||
private fun CreatorChannelFanTalkTabResponse.toFanTalkUiModels(): List<CreatorChannelFanTalkUiModel> =
|
private fun CreatorChannelFanTalkTabResponse.toFanTalkUiModels(): List<CreatorChannelFanTalkUiModel> =
|
||||||
fanTalks.toFanTalkUiModels(
|
fanTalks.toFanTalkUiModels(
|
||||||
relativeTimeTextFormatter = relativeTimeTextFormatter,
|
|
||||||
isOwner = isOwner,
|
isOwner = isOwner,
|
||||||
currentUserId = SharedPreferenceManager.userId
|
currentUserId = SharedPreferenceManager.userId
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import androidx.core.widget.doAfterTextChanged
|
|||||||
import coil.transform.CircleCropTransformation
|
import coil.transform.CircleCropTransformation
|
||||||
import kr.co.vividnext.sodalive.R
|
import kr.co.vividnext.sodalive.R
|
||||||
import kr.co.vividnext.sodalive.base.BaseActivity
|
import kr.co.vividnext.sodalive.base.BaseActivity
|
||||||
|
import kr.co.vividnext.sodalive.common.formatUtcRelativeTimeText
|
||||||
import kr.co.vividnext.sodalive.databinding.ActivityCreatorChannelFantalkDetailBinding
|
import kr.co.vividnext.sodalive.databinding.ActivityCreatorChannelFantalkDetailBinding
|
||||||
import kr.co.vividnext.sodalive.databinding.ViewCreatorChannelFantalkMorePopupBinding
|
import kr.co.vividnext.sodalive.databinding.ViewCreatorChannelFantalkMorePopupBinding
|
||||||
import kr.co.vividnext.sodalive.extensions.loadUrl
|
import kr.co.vividnext.sodalive.extensions.loadUrl
|
||||||
@@ -107,7 +108,8 @@ class CreatorChannelFanTalkDetailActivity : BaseActivity<ActivityCreatorChannelF
|
|||||||
private fun bindParentFanTalk(item: CreatorChannelFanTalkUiModel) = with(binding) {
|
private fun bindParentFanTalk(item: CreatorChannelFanTalkUiModel) = with(binding) {
|
||||||
ivCreatorChannelFantalkDetailProfile.loadProfile(item.writerProfileImageUrl)
|
ivCreatorChannelFantalkDetailProfile.loadProfile(item.writerProfileImageUrl)
|
||||||
tvCreatorChannelFantalkDetailNickname.text = item.writerNickname
|
tvCreatorChannelFantalkDetailNickname.text = item.writerNickname
|
||||||
tvCreatorChannelFantalkDetailTime.text = item.createdAtText
|
tvCreatorChannelFantalkDetailTime.text =
|
||||||
|
formatUtcRelativeTimeText(tvCreatorChannelFantalkDetailTime.context, item.createdAtUtc)
|
||||||
tvCreatorChannelFantalkDetailContent.text = item.content
|
tvCreatorChannelFantalkDetailContent.text = item.content
|
||||||
ivCreatorChannelFantalkDetailMore.isVisible = false
|
ivCreatorChannelFantalkDetailMore.isVisible = false
|
||||||
ivCreatorChannelFantalkDetailMore.setOnClickListener(null)
|
ivCreatorChannelFantalkDetailMore.setOnClickListener(null)
|
||||||
@@ -123,7 +125,8 @@ class CreatorChannelFanTalkDetailActivity : BaseActivity<ActivityCreatorChannelF
|
|||||||
|
|
||||||
ivCreatorChannelFantalkDetailReplyProfile.loadProfile(reply.writerProfileImageUrl)
|
ivCreatorChannelFantalkDetailReplyProfile.loadProfile(reply.writerProfileImageUrl)
|
||||||
tvCreatorChannelFantalkDetailReplyNickname.text = reply.writerNickname
|
tvCreatorChannelFantalkDetailReplyNickname.text = reply.writerNickname
|
||||||
tvCreatorChannelFantalkDetailReplyTime.text = reply.createdAtText
|
tvCreatorChannelFantalkDetailReplyTime.text =
|
||||||
|
formatUtcRelativeTimeText(tvCreatorChannelFantalkDetailReplyTime.context, reply.createdAtUtc)
|
||||||
tvCreatorChannelFantalkDetailReplyContent.text = reply.content
|
tvCreatorChannelFantalkDetailReplyContent.text = reply.content
|
||||||
ivCreatorChannelFantalkDetailReplyMore.isVisible = reply.fanTalkId > 0L
|
ivCreatorChannelFantalkDetailReplyMore.isVisible = reply.fanTalkId > 0L
|
||||||
ivCreatorChannelFantalkDetailReplyMore.setOnClickListener(
|
ivCreatorChannelFantalkDetailReplyMore.setOnClickListener(
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ data class CreatorChannelFanTalkDetailPayload(
|
|||||||
val writerNickname: String,
|
val writerNickname: String,
|
||||||
val writerProfileImageUrl: String,
|
val writerProfileImageUrl: String,
|
||||||
val content: String,
|
val content: String,
|
||||||
val createdAtText: String,
|
val createdAtUtc: String,
|
||||||
val reply: CreatorChannelFanTalkDetailReplyPayload?,
|
val reply: CreatorChannelFanTalkDetailReplyPayload?,
|
||||||
val showEdit: Boolean,
|
val showEdit: Boolean,
|
||||||
val showDelete: Boolean
|
val showDelete: Boolean
|
||||||
@@ -26,7 +26,7 @@ data class CreatorChannelFanTalkDetailPayload(
|
|||||||
writerNickname = writerNickname,
|
writerNickname = writerNickname,
|
||||||
writerProfileImageUrl = writerProfileImageUrl,
|
writerProfileImageUrl = writerProfileImageUrl,
|
||||||
content = content,
|
content = content,
|
||||||
createdAtText = createdAtText,
|
createdAtUtc = createdAtUtc,
|
||||||
reply = reply?.toUiModel(),
|
reply = reply?.toUiModel(),
|
||||||
rightAction = CreatorChannelFanTalkRightAction.OwnerMore(showEdit = showEdit, showDelete = showDelete)
|
rightAction = CreatorChannelFanTalkRightAction.OwnerMore(showEdit = showEdit, showDelete = showDelete)
|
||||||
)
|
)
|
||||||
@@ -39,7 +39,7 @@ data class CreatorChannelFanTalkDetailReplyPayload(
|
|||||||
val writerNickname: String,
|
val writerNickname: String,
|
||||||
val writerProfileImageUrl: String,
|
val writerProfileImageUrl: String,
|
||||||
val content: String,
|
val content: String,
|
||||||
val createdAtText: String
|
val createdAtUtc: String
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
fun toUiModel(): CreatorChannelFanTalkReplyUiModel = CreatorChannelFanTalkReplyUiModel(
|
fun toUiModel(): CreatorChannelFanTalkReplyUiModel = CreatorChannelFanTalkReplyUiModel(
|
||||||
fanTalkId = fanTalkId,
|
fanTalkId = fanTalkId,
|
||||||
@@ -47,7 +47,7 @@ data class CreatorChannelFanTalkDetailReplyPayload(
|
|||||||
writerNickname = writerNickname,
|
writerNickname = writerNickname,
|
||||||
writerProfileImageUrl = writerProfileImageUrl,
|
writerProfileImageUrl = writerProfileImageUrl,
|
||||||
content = content,
|
content = content,
|
||||||
createdAtText = createdAtText
|
createdAtUtc = createdAtUtc
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ fun CreatorChannelFanTalkUiModel.toFanTalkDetailPayload(): CreatorChannelFanTalk
|
|||||||
writerNickname = writerNickname,
|
writerNickname = writerNickname,
|
||||||
writerProfileImageUrl = writerProfileImageUrl,
|
writerProfileImageUrl = writerProfileImageUrl,
|
||||||
content = content,
|
content = content,
|
||||||
createdAtText = createdAtText,
|
createdAtUtc = createdAtUtc,
|
||||||
reply = reply?.toFanTalkDetailReplyPayload(),
|
reply = reply?.toFanTalkDetailReplyPayload(),
|
||||||
showEdit = ownerMore?.showEdit == true,
|
showEdit = ownerMore?.showEdit == true,
|
||||||
showDelete = ownerMore?.showDelete == true
|
showDelete = ownerMore?.showDelete == true
|
||||||
@@ -73,7 +73,7 @@ fun CreatorChannelFanTalkReplyUiModel.toFanTalkDetailReplyPayload(): CreatorChan
|
|||||||
writerNickname = writerNickname,
|
writerNickname = writerNickname,
|
||||||
writerProfileImageUrl = writerProfileImageUrl,
|
writerProfileImageUrl = writerProfileImageUrl,
|
||||||
content = content,
|
content = content,
|
||||||
createdAtText = createdAtText
|
createdAtUtc = createdAtUtc
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,15 +11,13 @@ import kr.co.vividnext.sodalive.base.BaseViewModel
|
|||||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||||
import kr.co.vividnext.sodalive.common.ToastMessage
|
import kr.co.vividnext.sodalive.common.ToastMessage
|
||||||
import kr.co.vividnext.sodalive.common.UtcRelativeTimeTextFormatter
|
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelEvent
|
import kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelEvent
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelRepository
|
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelRepository
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.data.CreatorChannelFanTalkReplyResponse
|
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.data.CreatorChannelFanTalkReplyResponse
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.model.toFanTalkReplyUiModel
|
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.model.toFanTalkReplyUiModel
|
||||||
|
|
||||||
class CreatorChannelFanTalkDetailViewModel(
|
class CreatorChannelFanTalkDetailViewModel(
|
||||||
private val repository: CreatorChannelRepository,
|
private val repository: CreatorChannelRepository
|
||||||
private val relativeTimeTextFormatter: UtcRelativeTimeTextFormatter
|
|
||||||
) : BaseViewModel() {
|
) : BaseViewModel() {
|
||||||
|
|
||||||
private val _detailStateLiveData = MutableLiveData<CreatorChannelFanTalkDetailUiState>()
|
private val _detailStateLiveData = MutableLiveData<CreatorChannelFanTalkDetailUiState>()
|
||||||
@@ -191,7 +189,7 @@ class CreatorChannelFanTalkDetailViewModel(
|
|||||||
val current = currentContent() ?: return
|
val current = currentContent() ?: return
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
_detailStateLiveData.value = current.copy(
|
_detailStateLiveData.value = current.copy(
|
||||||
reply = response.data?.toFanTalkReplyUiModel(relativeTimeTextFormatter) ?: current.reply,
|
reply = response.data?.toFanTalkReplyUiModel() ?: current.reply,
|
||||||
replyInput = "",
|
replyInput = "",
|
||||||
isSubmitting = false,
|
isSubmitting = false,
|
||||||
editingTarget = null
|
editingTarget = null
|
||||||
|
|||||||
@@ -1,19 +1,16 @@
|
|||||||
package kr.co.vividnext.sodalive.v2.creator.channel.fantalk.model
|
package kr.co.vividnext.sodalive.v2.creator.channel.fantalk.model
|
||||||
|
|
||||||
import kr.co.vividnext.sodalive.common.UtcRelativeTimeTextFormatter
|
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.data.CreatorChannelFanTalkReplyResponse
|
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.data.CreatorChannelFanTalkReplyResponse
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.data.CreatorChannelFanTalkResponse
|
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.data.CreatorChannelFanTalkResponse
|
||||||
|
|
||||||
fun List<CreatorChannelFanTalkResponse>.toFanTalkUiModels(
|
fun List<CreatorChannelFanTalkResponse>.toFanTalkUiModels(
|
||||||
relativeTimeTextFormatter: UtcRelativeTimeTextFormatter,
|
|
||||||
isOwner: Boolean,
|
isOwner: Boolean,
|
||||||
currentUserId: Long
|
currentUserId: Long
|
||||||
): List<CreatorChannelFanTalkUiModel> = map {
|
): List<CreatorChannelFanTalkUiModel> = map {
|
||||||
it.toFanTalkUiModel(relativeTimeTextFormatter, isOwner, currentUserId)
|
it.toFanTalkUiModel(isOwner, currentUserId)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CreatorChannelFanTalkResponse.toFanTalkUiModel(
|
private fun CreatorChannelFanTalkResponse.toFanTalkUiModel(
|
||||||
relativeTimeTextFormatter: UtcRelativeTimeTextFormatter,
|
|
||||||
isOwner: Boolean,
|
isOwner: Boolean,
|
||||||
currentUserId: Long
|
currentUserId: Long
|
||||||
) = CreatorChannelFanTalkUiModel(
|
) = CreatorChannelFanTalkUiModel(
|
||||||
@@ -22,31 +19,27 @@ private fun CreatorChannelFanTalkResponse.toFanTalkUiModel(
|
|||||||
writerNickname = writerNickname,
|
writerNickname = writerNickname,
|
||||||
writerProfileImageUrl = writerProfileImageUrl,
|
writerProfileImageUrl = writerProfileImageUrl,
|
||||||
content = content,
|
content = content,
|
||||||
createdAtText = relativeTimeTextFormatter.format(createdAtUtc),
|
createdAtUtc = createdAtUtc,
|
||||||
reply = creatorReplies.orEmpty().firstOrNull()?.toFanTalkReplyUiModel(relativeTimeTextFormatter),
|
reply = creatorReplies.orEmpty().firstOrNull()?.toFanTalkReplyUiModel(),
|
||||||
rightAction = toRightAction(isOwner = isOwner, currentUserId = currentUserId)
|
rightAction = toRightAction(isOwner = isOwner, currentUserId = currentUserId)
|
||||||
)
|
)
|
||||||
|
|
||||||
fun CreatorChannelFanTalkReplyResponse.toFanTalkReplyUiModel(
|
fun CreatorChannelFanTalkReplyResponse.toFanTalkReplyUiModel() = CreatorChannelFanTalkReplyUiModel(
|
||||||
relativeTimeTextFormatter: UtcRelativeTimeTextFormatter
|
|
||||||
) = CreatorChannelFanTalkReplyUiModel(
|
|
||||||
fanTalkId = fanTalkId,
|
fanTalkId = fanTalkId,
|
||||||
writerId = writerId,
|
writerId = writerId,
|
||||||
writerNickname = writerNickname,
|
writerNickname = writerNickname,
|
||||||
writerProfileImageUrl = writerProfileImageUrl,
|
writerProfileImageUrl = writerProfileImageUrl,
|
||||||
content = content,
|
content = content,
|
||||||
createdAtText = relativeTimeTextFormatter.format(createdAtUtc)
|
createdAtUtc = createdAtUtc
|
||||||
)
|
)
|
||||||
|
|
||||||
fun CreatorChannelFanTalkResponse.toFanTalkReplyUiModel(
|
fun CreatorChannelFanTalkResponse.toFanTalkReplyUiModel() = CreatorChannelFanTalkReplyUiModel(
|
||||||
relativeTimeTextFormatter: UtcRelativeTimeTextFormatter
|
|
||||||
) = CreatorChannelFanTalkReplyUiModel(
|
|
||||||
fanTalkId = fanTalkId,
|
fanTalkId = fanTalkId,
|
||||||
writerId = writerId,
|
writerId = writerId,
|
||||||
writerNickname = writerNickname,
|
writerNickname = writerNickname,
|
||||||
writerProfileImageUrl = writerProfileImageUrl,
|
writerProfileImageUrl = writerProfileImageUrl,
|
||||||
content = content,
|
content = content,
|
||||||
createdAtText = relativeTimeTextFormatter.format(createdAtUtc)
|
createdAtUtc = createdAtUtc
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun CreatorChannelFanTalkResponse.toRightAction(
|
private fun CreatorChannelFanTalkResponse.toRightAction(
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ data class CreatorChannelFanTalkUiModel(
|
|||||||
val writerNickname: String,
|
val writerNickname: String,
|
||||||
val writerProfileImageUrl: String,
|
val writerProfileImageUrl: String,
|
||||||
val content: String,
|
val content: String,
|
||||||
val createdAtText: String,
|
val createdAtUtc: String,
|
||||||
val reply: CreatorChannelFanTalkReplyUiModel?,
|
val reply: CreatorChannelFanTalkReplyUiModel?,
|
||||||
val rightAction: CreatorChannelFanTalkRightAction
|
val rightAction: CreatorChannelFanTalkRightAction
|
||||||
)
|
)
|
||||||
@@ -17,7 +17,7 @@ data class CreatorChannelFanTalkReplyUiModel(
|
|||||||
val writerNickname: String,
|
val writerNickname: String,
|
||||||
val writerProfileImageUrl: String,
|
val writerProfileImageUrl: String,
|
||||||
val content: String,
|
val content: String,
|
||||||
val createdAtText: String
|
val createdAtUtc: String
|
||||||
)
|
)
|
||||||
|
|
||||||
sealed interface CreatorChannelFanTalkRightAction {
|
sealed interface CreatorChannelFanTalkRightAction {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import androidx.core.view.isVisible
|
|||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import coil.transform.CircleCropTransformation
|
import coil.transform.CircleCropTransformation
|
||||||
import kr.co.vividnext.sodalive.R
|
import kr.co.vividnext.sodalive.R
|
||||||
|
import kr.co.vividnext.sodalive.common.formatUtcRelativeTimeText
|
||||||
import kr.co.vividnext.sodalive.databinding.ItemCreatorChannelFantalkBinding
|
import kr.co.vividnext.sodalive.databinding.ItemCreatorChannelFantalkBinding
|
||||||
import kr.co.vividnext.sodalive.extensions.loadUrl
|
import kr.co.vividnext.sodalive.extensions.loadUrl
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.model.CreatorChannelFanTalkReplyUiModel
|
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.model.CreatorChannelFanTalkReplyUiModel
|
||||||
@@ -51,7 +52,8 @@ class CreatorChannelFanTalkAdapter(
|
|||||||
fun bind(item: CreatorChannelFanTalkUiModel) = with(binding) {
|
fun bind(item: CreatorChannelFanTalkUiModel) = with(binding) {
|
||||||
ivCreatorChannelFantalkProfile.loadProfile(item.writerProfileImageUrl)
|
ivCreatorChannelFantalkProfile.loadProfile(item.writerProfileImageUrl)
|
||||||
tvCreatorChannelFantalkNickname.text = item.writerNickname
|
tvCreatorChannelFantalkNickname.text = item.writerNickname
|
||||||
tvCreatorChannelFantalkTime.text = item.createdAtText
|
tvCreatorChannelFantalkTime.text =
|
||||||
|
formatUtcRelativeTimeText(tvCreatorChannelFantalkTime.context, item.createdAtUtc)
|
||||||
tvCreatorChannelFantalkContent.text = item.content
|
tvCreatorChannelFantalkContent.text = item.content
|
||||||
root.setOnClickListener { onItemClick(item) }
|
root.setOnClickListener { onItemClick(item) }
|
||||||
tvCreatorChannelFantalkContent.setOnClickListener { onItemClick(item) }
|
tvCreatorChannelFantalkContent.setOnClickListener { onItemClick(item) }
|
||||||
@@ -63,6 +65,7 @@ class CreatorChannelFanTalkAdapter(
|
|||||||
when (item.rightAction) {
|
when (item.rightAction) {
|
||||||
CreatorChannelFanTalkRightAction.Report -> {
|
CreatorChannelFanTalkRightAction.Report -> {
|
||||||
tvCreatorChannelFantalkReport.isVisible = true
|
tvCreatorChannelFantalkReport.isVisible = true
|
||||||
|
tvCreatorChannelFantalkReport.text = root.context.getString(R.string.creator_channel_fantalk_report)
|
||||||
tvCreatorChannelFantalkReport.setOnClickListener { onReportClick(item) }
|
tvCreatorChannelFantalkReport.setOnClickListener { onReportClick(item) }
|
||||||
ivCreatorChannelFantalkMore.isVisible = false
|
ivCreatorChannelFantalkMore.isVisible = false
|
||||||
ivCreatorChannelFantalkMore.setOnClickListener(null)
|
ivCreatorChannelFantalkMore.setOnClickListener(null)
|
||||||
@@ -84,7 +87,8 @@ class CreatorChannelFanTalkAdapter(
|
|||||||
|
|
||||||
ivCreatorChannelFantalkReplyProfile.loadProfile(reply.writerProfileImageUrl)
|
ivCreatorChannelFantalkReplyProfile.loadProfile(reply.writerProfileImageUrl)
|
||||||
tvCreatorChannelFantalkReplyNickname.text = reply.writerNickname
|
tvCreatorChannelFantalkReplyNickname.text = reply.writerNickname
|
||||||
tvCreatorChannelFantalkReplyTime.text = reply.createdAtText
|
tvCreatorChannelFantalkReplyTime.text =
|
||||||
|
formatUtcRelativeTimeText(tvCreatorChannelFantalkReplyTime.context, reply.createdAtUtc)
|
||||||
tvCreatorChannelFantalkReplyContent.text = reply.content
|
tvCreatorChannelFantalkReplyContent.text = reply.content
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ internal fun bindCreatorChannelDonationCard(
|
|||||||
}
|
}
|
||||||
nicknameView.text = nickname
|
nicknameView.text = nickname
|
||||||
createdAtView.text = createdAtText
|
createdAtView.text = createdAtText
|
||||||
canView.text = context.getString(R.string.creator_channel_donation_can_format, can.moneyFormat())
|
canView.text = can.moneyFormat()
|
||||||
messageView.text = message
|
messageView.text = message
|
||||||
if (headerColorResId == R.color.red_400) {
|
if (headerColorResId == R.color.red_400) {
|
||||||
nicknameView.setTextColor(context.getColor(R.color.white))
|
nicknameView.setTextColor(context.getColor(R.color.white))
|
||||||
|
|||||||
|
After Width: | Height: | Size: 288 KiB |
|
After Width: | Height: | Size: 188 KiB |
|
Before Width: | Height: | Size: 549 B |
|
Before Width: | Height: | Size: 603 B |
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 482 B |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 847 B |
|
After Width: | Height: | Size: 286 KiB |
|
After Width: | Height: | Size: 185 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
@@ -27,7 +27,7 @@
|
|||||||
android:textSize="18.3sp"
|
android:textSize="18.3sp"
|
||||||
app:drawableStartCompat="@drawable/ic_back"
|
app:drawableStartCompat="@drawable/ic_back"
|
||||||
tools:ignore="RelativeOverlap"
|
tools:ignore="RelativeOverlap"
|
||||||
tools:text="보이스온" />
|
tools:text="소다라이브" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|||||||
@@ -88,7 +88,7 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:includeFontPadding="false"
|
android:includeFontPadding="false"
|
||||||
android:text="/500자"
|
android:text="/500"
|
||||||
android:textColor="@color/gray_500" />
|
android:textColor="@color/gray_500" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
android:fontFamily="@font/medium"
|
android:fontFamily="@font/medium"
|
||||||
android:textColor="@color/color_eeeeee"
|
android:textColor="@color/color_eeeeee"
|
||||||
android:textSize="14.7sp"
|
android:textSize="14.7sp"
|
||||||
tools:text="보이스온 App 오픈 관련 공지사항입니다." />
|
tools:text="소다라이브 App 오픈 관련 공지사항입니다." />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tv_date"
|
android:id="@+id/tv_date"
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
android:textSize="18.3sp"
|
android:textSize="18.3sp"
|
||||||
app:drawableStartCompat="@drawable/ic_back"
|
app:drawableStartCompat="@drawable/ic_back"
|
||||||
tools:ignore="RelativeOverlap"
|
tools:ignore="RelativeOverlap"
|
||||||
tools:text="보이스온" />
|
tools:text="소다라이브" />
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
<ScrollView
|
<ScrollView
|
||||||
|
|||||||
@@ -15,17 +15,19 @@
|
|||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
<TextView
|
|
||||||
style="@style/Typography.Heading3"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/screen_splash_tagline"
|
|
||||||
android:textColor="@color/white" />
|
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:contentDescription="@null"
|
||||||
android:src="@drawable/splash_text_logo" />
|
android:src="@drawable/splash_text_logo" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/Typography.Heading4"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/spacing_8"
|
||||||
|
android:text="@string/screen_splash_tagline"
|
||||||
|
android:textColor="@color/white" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|||||||
@@ -21,6 +21,6 @@
|
|||||||
android:textSize="18.3sp"
|
android:textSize="18.3sp"
|
||||||
app:drawableStartCompat="@drawable/ic_back"
|
app:drawableStartCompat="@drawable/ic_back"
|
||||||
tools:ignore="RelativeOverlap"
|
tools:ignore="RelativeOverlap"
|
||||||
tools:text="보이스온" />
|
tools:text="소다라이브" />
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|||||||
@@ -222,7 +222,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:fontFamily="@font/bold"
|
android:fontFamily="@font/bold"
|
||||||
android:text="@string/voiceon_original_only"
|
android:text="@string/sodalive_original_only"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/white"
|
||||||
android:textSize="24sp" />
|
android:textSize="24sp" />
|
||||||
|
|
||||||
|
|||||||
@@ -291,18 +291,20 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:layout_marginHorizontal="24dp"
|
android:layout_marginHorizontal="24dp"
|
||||||
android:layout_marginBottom="32dp"
|
android:adjustViewBounds="true"
|
||||||
|
android:layout_marginBottom="@dimen/spacing_32"
|
||||||
android:contentDescription="@null"
|
android:contentDescription="@null"
|
||||||
android:src="@drawable/img_apply_creator" />
|
android:src="@drawable/img_apply_creator" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/iv_introduce_voiceon"
|
android:id="@+id/iv_introduce_sodalive"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
|
android:adjustViewBounds="true"
|
||||||
android:layout_marginHorizontal="24dp"
|
android:layout_marginHorizontal="24dp"
|
||||||
android:contentDescription="@null"
|
android:contentDescription="@null"
|
||||||
android:src="@drawable/img_introduce_voiceon" />
|
android:src="@drawable/img_introduce_sodalive" />
|
||||||
|
|
||||||
<!-- Recent Content Section -->
|
<!-- Recent Content Section -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
|||||||
@@ -77,7 +77,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="2dp"
|
android:layout_marginStart="2dp"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/white"
|
||||||
tools:text="20캔" />
|
tools:text="20" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
android:fontFamily="@font/medium"
|
android:fontFamily="@font/medium"
|
||||||
android:textColor="@color/color_eeeeee"
|
android:textColor="@color/color_eeeeee"
|
||||||
android:textSize="14.7sp"
|
android:textSize="14.7sp"
|
||||||
tools:text="보이스온 App 오픈 관련 공지사항입니다." />
|
tools:text="소다라이브 App 오픈 관련 공지사항입니다." />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tv_date"
|
android:id="@+id/tv_date"
|
||||||
|
|||||||
@@ -5,9 +5,21 @@
|
|||||||
android:layout_height="52dp"
|
android:layout_height="52dp"
|
||||||
android:background="@color/black"
|
android:background="@color/black"
|
||||||
android:gravity="start|center_vertical"
|
android:gravity="start|center_vertical"
|
||||||
android:paddingHorizontal="@dimen/spacing_20"
|
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<HorizontalScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fillViewport="true"
|
||||||
|
android:scrollbars="none">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="start|center_vertical"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingHorizontal="@dimen/spacing_20">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tv_text_tab_first"
|
android:id="@+id/tv_text_tab_first"
|
||||||
style="@style/Typography.Heading3"
|
style="@style/Typography.Heading3"
|
||||||
@@ -40,4 +52,6 @@
|
|||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:text="팔로잉"
|
tools:text="팔로잉"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
</LinearLayout>
|
||||||
|
</HorizontalScrollView>
|
||||||
</kr.co.vividnext.sodalive.v2.widget.TextTabBarView>
|
</kr.co.vividnext.sodalive.v2.widget.TextTabBarView>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:contentDescription="@null"
|
android:contentDescription="@null"
|
||||||
android:src="@drawable/img_text_logo_v2" />
|
android:src="@drawable/img_text_logo" />
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:id="@+id/view_title_bar_spacer"
|
android:id="@+id/view_title_bar_spacer"
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 4.4 KiB |
@@ -1,5 +1,5 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">VoiceOn</string>
|
<string name="app_name">SodaLive</string>
|
||||||
<string name="picker_gallery">Gallery</string>
|
<string name="picker_gallery">Gallery</string>
|
||||||
<string name="picker_camera">Camera</string>
|
<string name="picker_camera">Camera</string>
|
||||||
<string name="picker_select">Select</string>
|
<string name="picker_select">Select</string>
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
|
|
||||||
<!-- Chat Room UI strings (English) -->
|
<!-- Chat Room UI strings (English) -->
|
||||||
<string name="chat_notice_clone">AI Clone converses based on the creator\'s information, but it may not perfectly reflect all details or match real conversations.</string>
|
<string name="chat_notice_clone">AI Clone converses based on the creator\'s information, but it may not perfectly reflect all details or match real conversations.</string>
|
||||||
<string name="chat_notice_character">VoiceOn AI Character Talk allows a high degree of freedom. You can be anyone in the conversation. Talk as a character in a universe or create your own story with a new persona together with the character.\nNote: AI Character Talk is in open beta; conversations may be awkward or incomplete.</string>
|
<string name="chat_notice_character">SodaLive AI Character Talk allows a high degree of freedom. You can be anyone in the conversation. Talk as a character in a universe or create your own story with a new persona together with the character.\nNote: AI Character Talk is in open beta; conversations may be awkward or incomplete.</string>
|
||||||
<string name="chat_character_type_clone">Clone</string>
|
<string name="chat_character_type_clone">Clone</string>
|
||||||
<string name="chat_character_type_character">Character</string>
|
<string name="chat_character_type_character">Character</string>
|
||||||
<string name="chat_freeze_status_creator">\"🧊 Freeze, everyone!\" The chat has been frozen.</string>
|
<string name="chat_freeze_status_creator">\"🧊 Freeze, everyone!\" The chat has been frozen.</string>
|
||||||
@@ -121,7 +121,7 @@
|
|||||||
<string name="hint_email">Email</string>
|
<string name="hint_email">Email</string>
|
||||||
<string name="hint_password">Password</string>
|
<string name="hint_password">Password</string>
|
||||||
<string name="forgot_password">Forgot your password?</string>
|
<string name="forgot_password">Forgot your password?</string>
|
||||||
<string name="signup_prompt">Not a VoiceOn member yet? Sign up now.</string>
|
<string name="signup_prompt">Not a SodaLive member yet? Sign up now.</string>
|
||||||
<string name="terms_of_service">Terms of Service</string>
|
<string name="terms_of_service">Terms of Service</string>
|
||||||
<string name="privacy_policy">Privacy Policy</string>
|
<string name="privacy_policy">Privacy Policy</string>
|
||||||
<string name="required_label">(Required)</string>
|
<string name="required_label">(Required)</string>
|
||||||
@@ -172,7 +172,7 @@
|
|||||||
<string name="famous_creator">Top creators</string>
|
<string name="famous_creator">Top creators</string>
|
||||||
<string name="latest_content">New</string>
|
<string name="latest_content">New</string>
|
||||||
<string name="view_all">View all</string>
|
<string name="view_all">View all</string>
|
||||||
<string name="voiceon_original_only">Only on VoiceOn</string>
|
<string name="sodalive_original_only">Only on SodaLive</string>
|
||||||
<string name="series_by_weekday">By weekday</string>
|
<string name="series_by_weekday">By weekday</string>
|
||||||
<string name="popular_character_chat">Top character</string>
|
<string name="popular_character_chat">Top character</string>
|
||||||
<string name="weekly_chart">Weekly chart</string>
|
<string name="weekly_chart">Weekly chart</string>
|
||||||
@@ -211,7 +211,7 @@
|
|||||||
<string name="creator_channel_series_progress_count">%1$s/%2$s episodes</string>
|
<string name="creator_channel_series_progress_count">%1$s/%2$s episodes</string>
|
||||||
<string name="creator_channel_series_progress_percent">%1$s%%</string>
|
<string name="creator_channel_series_progress_percent">%1$s%%</string>
|
||||||
<string name="auth_title">Identity verification</string>
|
<string name="auth_title">Identity verification</string>
|
||||||
<string name="auth_desc">VoiceOn’s Open World Character Chat\nis available only to adults who have completed identity verification to protect minors.\nTo use the character chat service, please complete identity verification.</string>
|
<string name="auth_desc">SodaLive’s Open World Character Chat\nis available only to adults who have completed identity verification to protect minors.\nTo use the character chat service, please complete identity verification.</string>
|
||||||
<string name="auth_desc_live">To protect minors,\nonly adults who have completed identity verification\ncan enter live rooms.\nPlease complete identity verification\nto enter the live room.</string>
|
<string name="auth_desc_live">To protect minors,\nonly adults who have completed identity verification\ncan enter live rooms.\nPlease complete identity verification\nto enter the live room.</string>
|
||||||
<string name="auth_go">Verify now</string>
|
<string name="auth_go">Verify now</string>
|
||||||
<string name="deeplink_not_found">Failed to find deep link.</string>
|
<string name="deeplink_not_found">Failed to find deep link.</string>
|
||||||
@@ -295,7 +295,7 @@
|
|||||||
<string name="screen_content_ranking_type_sales_count">Sales</string>
|
<string name="screen_content_ranking_type_sales_count">Sales</string>
|
||||||
<string name="screen_content_ranking_type_comment_count">Comments</string>
|
<string name="screen_content_ranking_type_comment_count">Comments</string>
|
||||||
<string name="screen_content_ranking_type_like_count">Likes</string>
|
<string name="screen_content_ranking_type_like_count">Likes</string>
|
||||||
<string name="content_recommendation_section_original_series">Only on Voice On!</string>
|
<string name="content_recommendation_section_original_series">Only on SodaLive!</string>
|
||||||
<string name="content_recommendation_section_latest_audio">New audio</string>
|
<string name="content_recommendation_section_latest_audio">New audio</string>
|
||||||
<string name="content_recommendation_section_new_and_hot">New&Hot</string>
|
<string name="content_recommendation_section_new_and_hot">New&Hot</string>
|
||||||
<string name="content_recommendation_section_free_audio">Free audio</string>
|
<string name="content_recommendation_section_free_audio">Free audio</string>
|
||||||
@@ -324,7 +324,7 @@
|
|||||||
<string name="home_recommendation_follow_all">Follow all</string>
|
<string name="home_recommendation_follow_all">Follow all</string>
|
||||||
<string name="home_recommendation_follow_all_done">Following all</string>
|
<string name="home_recommendation_follow_all_done">Following all</string>
|
||||||
<string name="home_recommendation_follow_all_confirm_title">Follow all</string>
|
<string name="home_recommendation_follow_all_confirm_title">Follow all</string>
|
||||||
<string name="home_recommendation_follow_all_confirm_description">Would you like to follow all creators in this list?</string>
|
<string name="home_recommendation_follow_all_confirm_description">Follow all recommended creators\nand get their updates!</string>
|
||||||
<string name="home_recommendation_follow_all_confirm_button">Follow</string>
|
<string name="home_recommendation_follow_all_confirm_button">Follow</string>
|
||||||
<string name="home_recommendation_more">More</string>
|
<string name="home_recommendation_more">More</string>
|
||||||
<string name="home_recommendation_collapse">Collapse</string>
|
<string name="home_recommendation_collapse">Collapse</string>
|
||||||
@@ -411,7 +411,6 @@
|
|||||||
<string name="creator_channel_home_donation_empty_title">Be the first to\nsupport this creator!</string>
|
<string name="creator_channel_home_donation_empty_title">Be the first to\nsupport this creator!</string>
|
||||||
<string name="creator_channel_donation_empty_title">No support yet.\nBe the first to support this creator!</string>
|
<string name="creator_channel_donation_empty_title">No support yet.\nBe the first to support this creator!</string>
|
||||||
<string name="creator_channel_donation_empty_owner_title">No donation history yet.</string>
|
<string name="creator_channel_donation_empty_owner_title">No donation history yet.</string>
|
||||||
<string name="creator_channel_donation_can_format">%1$s cans</string>
|
|
||||||
<string name="creator_channel_donation_fallback_message">Supported with %1$d cans.</string>
|
<string name="creator_channel_donation_fallback_message">Supported with %1$d cans.</string>
|
||||||
<string name="creator_channel_owner_fab_open">Create content</string>
|
<string name="creator_channel_owner_fab_open">Create content</string>
|
||||||
<string name="creator_channel_owner_fab_community">Post community</string>
|
<string name="creator_channel_owner_fab_community">Post community</string>
|
||||||
@@ -442,7 +441,7 @@
|
|||||||
<string name="character_detail_original_link">View the original</string>
|
<string name="character_detail_original_link">View the original</string>
|
||||||
<string name="character_detail_personality_title">[Personality & Traits]</string>
|
<string name="character_detail_personality_title">[Personality & Traits]</string>
|
||||||
<string name="character_detail_chat_guide_title">⚠️ Character Chat Guidelines</string>
|
<string name="character_detail_chat_guide_title">⚠️ Character Chat Guidelines</string>
|
||||||
<string name="character_detail_chat_guide_desc">VoiceOn AI Character Chat offers a high level of conversational freedom, allowing you to be whoever you want in the conversation.\nChat as a character within the world, or create your own story with the character as someone entirely new.</string>
|
<string name="character_detail_chat_guide_desc">SodaLive AI Character Chat offers a high level of conversational freedom, allowing you to be whoever you want in the conversation.\nChat as a character within the world, or create your own story with the character as someone entirely new.</string>
|
||||||
<string name="character_detail_chat_guide_notice">※ Character AI Chat is in open beta.\nResponses may sometimes feel awkward or incomplete.\nIf the character feels out of character early in the conversation, please reset the chat and try again.</string>
|
<string name="character_detail_chat_guide_notice">※ Character AI Chat is in open beta.\nResponses may sometimes feel awkward or incomplete.\nIf the character feels out of character early in the conversation, please reset the chat and try again.</string>
|
||||||
<string name="character_detail_comments_label">Comments</string>
|
<string name="character_detail_comments_label">Comments</string>
|
||||||
<string name="character_detail_comment_input_hint">Enter a comment</string>
|
<string name="character_detail_comment_input_hint">Enter a comment</string>
|
||||||
@@ -713,8 +712,8 @@
|
|||||||
<string name="screen_live_room_unknown_error">An unknown error occurred. try again.</string>
|
<string name="screen_live_room_unknown_error">An unknown error occurred. try again.</string>
|
||||||
<string name="screen_live_room_detail_date_format">yyyy.MM.dd EEE hh:mm a</string>
|
<string name="screen_live_room_detail_date_format">yyyy.MM.dd EEE hh:mm a</string>
|
||||||
<string name="screen_live_room_reserver_label">Reserved users</string>
|
<string name="screen_live_room_reserver_label">Reserved users</string>
|
||||||
<string name="screen_live_room_share_message_private">%1$s invited you to a private live on VoiceOn.\n\u203b Join: %2$s\n(Password: %3$s)</string>
|
<string name="screen_live_room_share_message_private">%1$s invited you to a private live on SodaLive.\n\u203b Join: %2$s\n(Password: %3$s)</string>
|
||||||
<string name="screen_live_room_share_message_public">%1$s invited you to a public live on VoiceOn.\n\u203b Join: %2$s</string>
|
<string name="screen_live_room_share_message_public">%1$s invited you to a public live on SodaLive.\n\u203b Join: %2$s</string>
|
||||||
<string name="screen_live_room_dialog_title_no_chat">Chat mute</string>
|
<string name="screen_live_room_dialog_title_no_chat">Chat mute</string>
|
||||||
<string name="screen_live_room_dialog_no_chat_desc">Mute chat for 3 minutes?</string>
|
<string name="screen_live_room_dialog_no_chat_desc">Mute chat for 3 minutes?</string>
|
||||||
<string name="screen_live_room_dialog_skip">Skip</string>
|
<string name="screen_live_room_dialog_skip">Skip</string>
|
||||||
@@ -953,7 +952,7 @@
|
|||||||
<string name="screen_can_payment_method_unified">Unified payment</string>
|
<string name="screen_can_payment_method_unified">Unified payment</string>
|
||||||
<string name="screen_can_payment_method_phone">Mobile payment</string>
|
<string name="screen_can_payment_method_phone">Mobile payment</string>
|
||||||
<string name="screen_can_payment_agree">I agree to the purchase terms and payment</string>
|
<string name="screen_can_payment_agree">I agree to the purchase terms and payment</string>
|
||||||
<string name="screen_can_payment_notice">- Charged cans expire 5 years after charging.\n- Payments can be cancelled within 7 days; cancellations are not allowed once any can is used.\n- Promotional cans provided for free are non-refundable.\n- See the VoiceOn Terms of Service for details.</string>
|
<string name="screen_can_payment_notice">- Charged cans expire 5 years after charging.\n- Payments can be cancelled within 7 days; cancellations are not allowed once any can is used.\n- Promotional cans provided for free are non-refundable.\n- See the SodaLive Terms of Service for details.</string>
|
||||||
<string name="screen_can_payment_amount_label">Payment amount</string>
|
<string name="screen_can_payment_amount_label">Payment amount</string>
|
||||||
<string name="screen_can_payment_pay">Pay</string>
|
<string name="screen_can_payment_pay">Pay</string>
|
||||||
<string name="screen_can_payment_currency_unit">KRW</string>
|
<string name="screen_can_payment_currency_unit">KRW</string>
|
||||||
@@ -975,7 +974,7 @@
|
|||||||
<string name="msg_can_coupon_unknown_error">An unknown error occurred. try again.</string>
|
<string name="msg_can_coupon_unknown_error">An unknown error occurred. try again.</string>
|
||||||
<!-- Service Center -->
|
<!-- Service Center -->
|
||||||
<string name="screen_service_center_title">Customer service</string>
|
<string name="screen_service_center_title">Customer service</string>
|
||||||
<string name="screen_service_center_logo_label">VoiceOn Customer Service</string>
|
<string name="screen_service_center_logo_label">SodaLive Customer Service</string>
|
||||||
<string name="screen_service_center_faq_title">FAQ</string>
|
<string name="screen_service_center_faq_title">FAQ</string>
|
||||||
<string name="screen_service_center_faq_category_all">All</string>
|
<string name="screen_service_center_faq_category_all">All</string>
|
||||||
<string name="screen_service_center_kakao">TALK inquiry</string>
|
<string name="screen_service_center_kakao">TALK inquiry</string>
|
||||||
@@ -1130,7 +1129,7 @@
|
|||||||
<string name="screen_audition_on">ON</string>
|
<string name="screen_audition_on">ON</string>
|
||||||
<string name="screen_audition_off">OFF</string>
|
<string name="screen_audition_off">OFF</string>
|
||||||
<string name="screen_audition_total_count">Total %1$d</string>
|
<string name="screen_audition_total_count">Total %1$d</string>
|
||||||
<string name="screen_audition_how_to_use">How to use VoiceOn audition</string>
|
<string name="screen_audition_how_to_use">How to use SodaLive audition</string>
|
||||||
<string name="screen_audition_how_to_use_more">See details></string>
|
<string name="screen_audition_how_to_use_more">See details></string>
|
||||||
<string name="screen_audition_error_invalid_request">Invalid request.\ntry again.</string>
|
<string name="screen_audition_error_invalid_request">Invalid request.\ntry again.</string>
|
||||||
<string name="screen_audition_detail_info_title">Audition info</string>
|
<string name="screen_audition_detail_info_title">Audition info</string>
|
||||||
@@ -1149,7 +1148,7 @@
|
|||||||
<string name="dialog_audition_apply_audio_file_title">Recording file</string>
|
<string name="dialog_audition_apply_audio_file_title">Recording file</string>
|
||||||
<string name="dialog_audition_apply_phone_title">Contact</string>
|
<string name="dialog_audition_apply_phone_title">Contact</string>
|
||||||
<string name="dialog_audition_apply_phone_hint">Leave a contact number for results</string>
|
<string name="dialog_audition_apply_phone_hint">Leave a contact number for results</string>
|
||||||
<string name="dialog_audition_apply_agreement">I agree to the collection and use of my personal contact information for notification if I pass the VoiceOn audio drama audition.\nYou may refuse to consent, but the audition application will be canceled if you do not agree.</string>
|
<string name="dialog_audition_apply_agreement">I agree to the collection and use of my personal contact information for notification if I pass the SodaLive audio drama audition.\nYou may refuse to consent, but the audition application will be canceled if you do not agree.</string>
|
||||||
<string name="dialog_audition_apply_submit">Submit audition application</string>
|
<string name="dialog_audition_apply_submit">Submit audition application</string>
|
||||||
<string name="dialog_audition_apply_error_invalid_phone">Invalid contact number.\ntry again.</string>
|
<string name="dialog_audition_apply_error_invalid_phone">Invalid contact number.\ntry again.</string>
|
||||||
<string name="dialog_audition_apply_error_need_agreement">You must agree to the collection and use of contact information to apply for the audition.</string>
|
<string name="dialog_audition_apply_error_need_agreement">You must agree to the collection and use of contact information to apply for the audition.</string>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">ボイスオン</string>
|
<string name="app_name">ソーダライブ</string>
|
||||||
<string name="picker_gallery">ギャラリー</string>
|
<string name="picker_gallery">ギャラリー</string>
|
||||||
<string name="picker_camera">カメラ</string>
|
<string name="picker_camera">カメラ</string>
|
||||||
<string name="picker_select">選択</string>
|
<string name="picker_select">選択</string>
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
|
|
||||||
<!-- Chat Room UI strings -->
|
<!-- Chat Room UI strings -->
|
||||||
<string name="chat_notice_clone">AIクローンはクリエイターのデータを基に会話しますが、完全に再現されない場合があります。</string>
|
<string name="chat_notice_clone">AIクローンはクリエイターのデータを基に会話しますが、完全に再現されない場合があります。</string>
|
||||||
<string name="chat_notice_character">ボイスオンのAIキャラトークは自由度が高く、あなたは誰にでもなれます。\n世界観のキャラになりきったり、新しい人物としてあなただけのストーリーを作ってみましょう。\n※オープンベータ中のため、会話がぎこちない場合があります。</string>
|
<string name="chat_notice_character">ソーダライブのAIキャラトークは自由度が高く、あなたは誰にでもなれます。\n世界観のキャラになりきったり、新しい人物としてあなただけのストーリーを作ってみましょう。\n※オープンベータ中のため、会話がぎこちない場合があります。</string>
|
||||||
<string name="chat_character_type_clone">クローン</string>
|
<string name="chat_character_type_clone">クローン</string>
|
||||||
<string name="chat_character_type_character">キャラクター</string>
|
<string name="chat_character_type_character">キャラクター</string>
|
||||||
<string name="chat_freeze_status_creator">「🧊 みんなフリーズ!」チャットを凍結しました。</string>
|
<string name="chat_freeze_status_creator">「🧊 みんなフリーズ!」チャットを凍結しました。</string>
|
||||||
@@ -121,7 +121,7 @@
|
|||||||
<string name="hint_email">メールアドレス</string>
|
<string name="hint_email">メールアドレス</string>
|
||||||
<string name="hint_password">パスワード</string>
|
<string name="hint_password">パスワード</string>
|
||||||
<string name="forgot_password">パスワードをお忘れですか?</string>
|
<string name="forgot_password">パスワードをお忘れですか?</string>
|
||||||
<string name="signup_prompt">ボイスオンは初めてですか?今すぐ登録しましょう。</string>
|
<string name="signup_prompt">ソーダライブは初めてですか?今すぐ登録しましょう。</string>
|
||||||
<string name="terms_of_service">利用規約</string>
|
<string name="terms_of_service">利用規約</string>
|
||||||
<string name="privacy_policy">個人情報の収集・利用への同意</string>
|
<string name="privacy_policy">個人情報の収集・利用への同意</string>
|
||||||
<string name="required_label">(必須)</string>
|
<string name="required_label">(必須)</string>
|
||||||
@@ -172,10 +172,10 @@
|
|||||||
<string name="famous_creator">人気クリエイター</string>
|
<string name="famous_creator">人気クリエイター</string>
|
||||||
<string name="latest_content">最新コンテンツ</string>
|
<string name="latest_content">最新コンテンツ</string>
|
||||||
<string name="view_all">すべて見る</string>
|
<string name="view_all">すべて見る</string>
|
||||||
<string name="voiceon_original_only">ボイスオン限定</string>
|
<string name="sodalive_original_only">ソーダライブ限定</string>
|
||||||
<string name="series_by_weekday">曜日別シリーズ</string>
|
<string name="series_by_weekday">曜日別シリーズ</string>
|
||||||
<string name="popular_character_chat">人気キャラチャット</string>
|
<string name="popular_character_chat">人気キャラチャット</string>
|
||||||
<string name="weekly_chart">ボイスオン週間チャート</string>
|
<string name="weekly_chart">ソーダライブ週間チャート</string>
|
||||||
<string name="recommend_channel">おすすめチャンネル</string>
|
<string name="recommend_channel">おすすめチャンネル</string>
|
||||||
<string name="free_content">無料コンテンツ</string>
|
<string name="free_content">無料コンテンツ</string>
|
||||||
<string name="point_rental_content">ポイントレンタル・コンテンツ</string>
|
<string name="point_rental_content">ポイントレンタル・コンテンツ</string>
|
||||||
@@ -211,7 +211,7 @@
|
|||||||
<string name="creator_channel_series_progress_count">%1$s/%2$s話</string>
|
<string name="creator_channel_series_progress_count">%1$s/%2$s話</string>
|
||||||
<string name="creator_channel_series_progress_percent">%1$s%%</string>
|
<string name="creator_channel_series_progress_percent">%1$s%%</string>
|
||||||
<string name="auth_title">本人確認</string>
|
<string name="auth_title">本人確認</string>
|
||||||
<string name="auth_desc">ボイスオンのオープンワールド・キャラトークは、\n青少年保護のため本人確認済みの\n成人の方のみご利用いただけます。\nサービスを利用するには本人確認をお願いします。</string>
|
<string name="auth_desc">ソーダライブのオープンワールド・キャラトークは、\n青少年保護のため本人確認済みの\n成人の方のみご利用いただけます。\nサービスを利用するには本人確認をお願いします。</string>
|
||||||
<string name="auth_desc_live">青少年保護のため、\n本人確認を完了した成人の方のみ\nライブ入場が可能です。\nライブ入場のために\n本人確認を行ってください。</string>
|
<string name="auth_desc_live">青少年保護のため、\n本人確認を完了した成人の方のみ\nライブ入場が可能です。\nライブ入場のために\n本人確認を行ってください。</string>
|
||||||
<string name="auth_go">本人確認へ進む</string>
|
<string name="auth_go">本人確認へ進む</string>
|
||||||
<string name="deeplink_not_found">ディープリンクが見つかりませんでした。</string>
|
<string name="deeplink_not_found">ディープリンクが見つかりませんでした。</string>
|
||||||
@@ -295,7 +295,7 @@
|
|||||||
<string name="screen_content_ranking_type_sales_count">販売数</string>
|
<string name="screen_content_ranking_type_sales_count">販売数</string>
|
||||||
<string name="screen_content_ranking_type_comment_count">コメント数</string>
|
<string name="screen_content_ranking_type_comment_count">コメント数</string>
|
||||||
<string name="screen_content_ranking_type_like_count">いいね</string>
|
<string name="screen_content_ranking_type_like_count">いいね</string>
|
||||||
<string name="content_recommendation_section_original_series">VOICE ON限定</string>
|
<string name="content_recommendation_section_original_series">SODALIVE限定</string>
|
||||||
<string name="content_recommendation_section_latest_audio">新着オーディオ</string>
|
<string name="content_recommendation_section_latest_audio">新着オーディオ</string>
|
||||||
<string name="content_recommendation_section_new_and_hot">New&Hot</string>
|
<string name="content_recommendation_section_new_and_hot">New&Hot</string>
|
||||||
<string name="content_recommendation_section_free_audio">無料オーディオ</string>
|
<string name="content_recommendation_section_free_audio">無料オーディオ</string>
|
||||||
@@ -324,7 +324,7 @@
|
|||||||
<string name="home_recommendation_follow_all">すべてフォロー</string>
|
<string name="home_recommendation_follow_all">すべてフォロー</string>
|
||||||
<string name="home_recommendation_follow_all_done">すべてフォロー中</string>
|
<string name="home_recommendation_follow_all_done">すべてフォロー中</string>
|
||||||
<string name="home_recommendation_follow_all_confirm_title">すべてフォロー</string>
|
<string name="home_recommendation_follow_all_confirm_title">すべてフォロー</string>
|
||||||
<string name="home_recommendation_follow_all_confirm_description">このリストのクリエイターをすべてフォローしますか?</string>
|
<string name="home_recommendation_follow_all_confirm_description">おすすめのクリエイターをすべてフォローして、\n最新情報を受け取りましょう!</string>
|
||||||
<string name="home_recommendation_follow_all_confirm_button">フォローする</string>
|
<string name="home_recommendation_follow_all_confirm_button">フォローする</string>
|
||||||
<string name="home_recommendation_more">もっと見る</string>
|
<string name="home_recommendation_more">もっと見る</string>
|
||||||
<string name="home_recommendation_collapse">折りたたむ</string>
|
<string name="home_recommendation_collapse">折りたたむ</string>
|
||||||
@@ -411,7 +411,6 @@
|
|||||||
<string name="creator_channel_home_donation_empty_title">最初のサポートをしてみましょう!</string>
|
<string name="creator_channel_home_donation_empty_title">最初のサポートをしてみましょう!</string>
|
||||||
<string name="creator_channel_donation_empty_title">まだサポートがありません。\n最初のサポートをしてみましょう!</string>
|
<string name="creator_channel_donation_empty_title">まだサポートがありません。\n最初のサポートをしてみましょう!</string>
|
||||||
<string name="creator_channel_donation_empty_owner_title">応援履歴がありません</string>
|
<string name="creator_channel_donation_empty_owner_title">応援履歴がありません</string>
|
||||||
<string name="creator_channel_donation_can_format">%1$sCAN</string>
|
|
||||||
<string name="creator_channel_donation_fallback_message">%1$dCANを応援しました。</string>
|
<string name="creator_channel_donation_fallback_message">%1$dCANを応援しました。</string>
|
||||||
<string name="creator_channel_owner_fab_open">コンテンツ作成</string>
|
<string name="creator_channel_owner_fab_open">コンテンツ作成</string>
|
||||||
<string name="creator_channel_owner_fab_community">コミュニティ投稿</string>
|
<string name="creator_channel_owner_fab_community">コミュニティ投稿</string>
|
||||||
@@ -442,7 +441,7 @@
|
|||||||
<string name="character_detail_original_link">原作を見に行く</string>
|
<string name="character_detail_original_link">原作を見に行く</string>
|
||||||
<string name="character_detail_personality_title">[性格・特徴]</string>
|
<string name="character_detail_personality_title">[性格・特徴]</string>
|
||||||
<string name="character_detail_chat_guide_title">⚠️ キャラクタートークガイド</string>
|
<string name="character_detail_chat_guide_title">⚠️ キャラクタートークガイド</string>
|
||||||
<string name="character_detail_chat_guide_desc">ボイスオンのAIキャラトークは自由度が高く、あなたは誰にでもなれます。\n世界観のキャラになりきったり、新しい人物としてあなただけのストーリーを作ってみましょう。</string>
|
<string name="character_detail_chat_guide_desc">ソーダライブのAIキャラトークは自由度が高く、あなたは誰にでもなれます。\n世界観のキャラになりきったり、新しい人物としてあなただけのストーリーを作ってみましょう。</string>
|
||||||
<string name="character_detail_chat_guide_notice">※オープンベータ中のため、会話がぎこちない場合があります。キャラ崩壊を感じたら、リセットして再試行してください。</string>
|
<string name="character_detail_chat_guide_notice">※オープンベータ中のため、会話がぎこちない場合があります。キャラ崩壊を感じたら、リセットして再試行してください。</string>
|
||||||
<string name="character_detail_comments_label">コメント</string>
|
<string name="character_detail_comments_label">コメント</string>
|
||||||
<string name="character_detail_comment_input_hint">コメントを入力してください</string>
|
<string name="character_detail_comment_input_hint">コメントを入力してください</string>
|
||||||
@@ -713,8 +712,8 @@
|
|||||||
<string name="screen_live_room_unknown_error">不明なエラーが発生しました。恐れ入りますが、もう一度お試しください。</string>
|
<string name="screen_live_room_unknown_error">不明なエラーが発生しました。恐れ入りますが、もう一度お試しください。</string>
|
||||||
<string name="screen_live_room_detail_date_format">yyyy年 MM月 dd日 (E) a hh:mm</string>
|
<string name="screen_live_room_detail_date_format">yyyy年 MM月 dd日 (E) a hh:mm</string>
|
||||||
<string name="screen_live_room_reserver_label">予約者</string>
|
<string name="screen_live_room_reserver_label">予約者</string>
|
||||||
<string name="screen_live_room_share_message_private">%1$sさんがボイスオンの非公開ライブに招待しました。\n※ ライブ参加: %2$s\n(入室パスワード: %3$s)</string>
|
<string name="screen_live_room_share_message_private">%1$sさんがソーダライブの非公開ライブに招待しました。\n※ ライブ参加: %2$s\n(入室パスワード: %3$s)</string>
|
||||||
<string name="screen_live_room_share_message_public">%1$sさんがボイスオンの公開ライブに招待しました。\n※ ライブ参加: %2$s</string>
|
<string name="screen_live_room_share_message_public">%1$sさんがソーダライブの公開ライブに招待しました。\n※ ライブ参加: %2$s</string>
|
||||||
<string name="screen_live_room_dialog_title_no_chat">チャット禁止</string>
|
<string name="screen_live_room_dialog_title_no_chat">チャット禁止</string>
|
||||||
<string name="screen_live_room_dialog_no_chat_desc">3分間チャットを禁止しますか?</string>
|
<string name="screen_live_room_dialog_no_chat_desc">3分間チャットを禁止しますか?</string>
|
||||||
<string name="screen_live_room_dialog_skip">スキップ</string>
|
<string name="screen_live_room_dialog_skip">スキップ</string>
|
||||||
@@ -975,7 +974,7 @@
|
|||||||
<string name="msg_can_coupon_unknown_error">不明なエラーが発生しました。恐れ入りますが、もう一度お試しください。</string>
|
<string name="msg_can_coupon_unknown_error">不明なエラーが発生しました。恐れ入りますが、もう一度お試しください。</string>
|
||||||
<!-- Service Center -->
|
<!-- Service Center -->
|
||||||
<string name="screen_service_center_title">サポート</string>
|
<string name="screen_service_center_title">サポート</string>
|
||||||
<string name="screen_service_center_logo_label">ボイスオンサポート</string>
|
<string name="screen_service_center_logo_label">ソーダライブサポート</string>
|
||||||
<string name="screen_service_center_faq_title">よくある質問</string>
|
<string name="screen_service_center_faq_title">よくある質問</string>
|
||||||
<string name="screen_service_center_faq_category_all">すべて</string>
|
<string name="screen_service_center_faq_category_all">すべて</string>
|
||||||
<string name="screen_service_center_kakao">TALK問い合わせ</string>
|
<string name="screen_service_center_kakao">TALK問い合わせ</string>
|
||||||
@@ -1130,7 +1129,7 @@
|
|||||||
<string name="screen_audition_on">ON</string>
|
<string name="screen_audition_on">ON</string>
|
||||||
<string name="screen_audition_off">OFF</string>
|
<string name="screen_audition_off">OFF</string>
|
||||||
<string name="screen_audition_total_count">すべて %1$d個</string>
|
<string name="screen_audition_total_count">すべて %1$d個</string>
|
||||||
<string name="screen_audition_how_to_use">ボイスオンオーディション利用方法</string>
|
<string name="screen_audition_how_to_use">ソーダライブオーディション利用方法</string>
|
||||||
<string name="screen_audition_how_to_use_more">詳しく></string>
|
<string name="screen_audition_how_to_use_more">詳しく></string>
|
||||||
<string name="screen_audition_error_invalid_request">無効なリクエストです。\n恐れ入りますが、もう一度お試しください。</string>
|
<string name="screen_audition_error_invalid_request">無効なリクエストです。\n恐れ入りますが、もう一度お試しください。</string>
|
||||||
<string name="screen_audition_detail_info_title">オーディション情報</string>
|
<string name="screen_audition_detail_info_title">オーディション情報</string>
|
||||||
@@ -1149,7 +1148,7 @@
|
|||||||
<string name="dialog_audition_apply_audio_file_title">録音ファイル</string>
|
<string name="dialog_audition_apply_audio_file_title">録音ファイル</string>
|
||||||
<string name="dialog_audition_apply_phone_title">連絡先</string>
|
<string name="dialog_audition_apply_phone_title">連絡先</string>
|
||||||
<string name="dialog_audition_apply_phone_hint">合格時に連絡可能な連絡先を残してください</string>
|
<string name="dialog_audition_apply_phone_hint">合格時に連絡可能な連絡先を残してください</string>
|
||||||
<string name="dialog_audition_apply_agreement">ボイスオンオーディオドラマオーディション合格時の連絡のため、個人情報(連絡先)の収集・利用に同意します。\n同意を拒否する権利がありますが、その場合応募はキャンセルされます。</string>
|
<string name="dialog_audition_apply_agreement">ソーダライブオーディオドラマオーディション合格時の連絡のため、個人情報(連絡先)の収集・利用に同意します。\n同意を拒否する権利がありますが、その場合応募はキャンセルされます。</string>
|
||||||
<string name="dialog_audition_apply_submit">オーディションに応募する</string>
|
<string name="dialog_audition_apply_submit">オーディションに応募する</string>
|
||||||
<string name="dialog_audition_apply_error_invalid_phone">無効な連絡先です。\nもう一度入力してください。</string>
|
<string name="dialog_audition_apply_error_invalid_phone">無効な連絡先です。\nもう一度入力してください。</string>
|
||||||
<string name="dialog_audition_apply_error_need_agreement">連絡先の収集・利用に同意すると応募可能です。</string>
|
<string name="dialog_audition_apply_error_need_agreement">連絡先の収集・利用に同意すると応募可能です。</string>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">보이스온</string>
|
<string name="app_name">소다라이브</string>
|
||||||
<string name="picker_gallery">갤러리</string>
|
<string name="picker_gallery">갤러리</string>
|
||||||
<string name="picker_camera">카메라</string>
|
<string name="picker_camera">카메라</string>
|
||||||
<string name="picker_select">선택</string>
|
<string name="picker_select">선택</string>
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
|
|
||||||
<!-- Chat Room UI strings (tasks 2.3) -->
|
<!-- Chat Room UI strings (tasks 2.3) -->
|
||||||
<string name="chat_notice_clone">AI Clone은 크리에이터의 정보를 기반으로 대화하지만, 모든 정보를 완벽하게 반영하거나 실제 대화와 일치하지 않을 수 있습니다.</string>
|
<string name="chat_notice_clone">AI Clone은 크리에이터의 정보를 기반으로 대화하지만, 모든 정보를 완벽하게 반영하거나 실제 대화와 일치하지 않을 수 있습니다.</string>
|
||||||
<string name="chat_notice_character">보이스온 AI캐릭터톡은 대화의 자유도가 높아 대화에 참여하는 당신은 누구든 될 수 있습니다.\n세계관 속 캐릭터로 대화를 하거나 새로운 인물로 캐릭터와 당신만의 스토리를 만들어보세요.\n※ AI캐릭터톡은 오픈베타 서비스 중이며, 캐릭터의 대화가 어색하거나 불완전할 수 있습니다.</string>
|
<string name="chat_notice_character">소다라이브 AI캐릭터톡은 대화의 자유도가 높아 대화에 참여하는 당신은 누구든 될 수 있습니다.\n세계관 속 캐릭터로 대화를 하거나 새로운 인물로 캐릭터와 당신만의 스토리를 만들어보세요.\n※ AI캐릭터톡은 오픈베타 서비스 중이며, 캐릭터의 대화가 어색하거나 불완전할 수 있습니다.</string>
|
||||||
<string name="chat_character_type_clone">Clone</string>
|
<string name="chat_character_type_clone">Clone</string>
|
||||||
<string name="chat_character_type_character">Character</string>
|
<string name="chat_character_type_character">Character</string>
|
||||||
<string name="chat_freeze_status_creator">“🧊 모두들 얼음!” 채팅창을 얼렸습니다.</string>
|
<string name="chat_freeze_status_creator">“🧊 모두들 얼음!” 채팅창을 얼렸습니다.</string>
|
||||||
@@ -120,7 +120,7 @@
|
|||||||
<string name="hint_email">이메일</string>
|
<string name="hint_email">이메일</string>
|
||||||
<string name="hint_password">비밀번호</string>
|
<string name="hint_password">비밀번호</string>
|
||||||
<string name="forgot_password">비밀번호를 잊으셨나요?</string>
|
<string name="forgot_password">비밀번호를 잊으셨나요?</string>
|
||||||
<string name="signup_prompt">보이스온 회원이 아닌가요? 지금 가입하세요.</string>
|
<string name="signup_prompt">소다라이브 회원이 아닌가요? 지금 가입하세요.</string>
|
||||||
<string name="terms_of_service">이용약관</string>
|
<string name="terms_of_service">이용약관</string>
|
||||||
<string name="privacy_policy">개인정보수집 및 이용동의</string>
|
<string name="privacy_policy">개인정보수집 및 이용동의</string>
|
||||||
<string name="required_label">(필수)</string>
|
<string name="required_label">(필수)</string>
|
||||||
@@ -171,7 +171,7 @@
|
|||||||
<string name="famous_creator">인기 크리에이터</string>
|
<string name="famous_creator">인기 크리에이터</string>
|
||||||
<string name="latest_content">최신 콘텐츠</string>
|
<string name="latest_content">최신 콘텐츠</string>
|
||||||
<string name="view_all">전체보기</string>
|
<string name="view_all">전체보기</string>
|
||||||
<string name="voiceon_original_only">오직 보이스온에서만</string>
|
<string name="sodalive_original_only">오직 소다라이브에서만</string>
|
||||||
<string name="series_by_weekday">요일별 시리즈</string>
|
<string name="series_by_weekday">요일별 시리즈</string>
|
||||||
<string name="popular_character_chat">인기 캐릭터 채팅</string>
|
<string name="popular_character_chat">인기 캐릭터 채팅</string>
|
||||||
<string name="weekly_chart">보온 주간 차트</string>
|
<string name="weekly_chart">보온 주간 차트</string>
|
||||||
@@ -210,7 +210,7 @@
|
|||||||
<string name="creator_channel_series_progress_count">%1$s/%2$s화</string>
|
<string name="creator_channel_series_progress_count">%1$s/%2$s화</string>
|
||||||
<string name="creator_channel_series_progress_percent">%1$s%%</string>
|
<string name="creator_channel_series_progress_percent">%1$s%%</string>
|
||||||
<string name="auth_title">본인인증</string>
|
<string name="auth_title">본인인증</string>
|
||||||
<string name="auth_desc">보이스온의 오픈월드 캐릭터톡은\n청소년 보호를 위해 본인인증한\n성인만 이용이 가능합니다.\n캐릭터톡 서비스를 이용하시려면\n본인인증을 하고 이용해주세요.</string>
|
<string name="auth_desc">소다라이브의 오픈월드 캐릭터톡은\n청소년 보호를 위해 본인인증한\n성인만 이용이 가능합니다.\n캐릭터톡 서비스를 이용하시려면\n본인인증을 하고 이용해주세요.</string>
|
||||||
<string name="auth_desc_live">청소년 보호를 위해\n본인인증을 완료한\n성인만 라이브 입장이 가능합니다.\n라이브 입장을 위해\n본인인증을 진행해 주세요.</string>
|
<string name="auth_desc_live">청소년 보호를 위해\n본인인증을 완료한\n성인만 라이브 입장이 가능합니다.\n라이브 입장을 위해\n본인인증을 진행해 주세요.</string>
|
||||||
<string name="auth_go">본인인증 하러가기</string>
|
<string name="auth_go">본인인증 하러가기</string>
|
||||||
<string name="deeplink_not_found">딥링크를 찾을 수 없습니다.</string>
|
<string name="deeplink_not_found">딥링크를 찾을 수 없습니다.</string>
|
||||||
@@ -294,7 +294,7 @@
|
|||||||
<string name="screen_content_ranking_type_sales_count">판매량</string>
|
<string name="screen_content_ranking_type_sales_count">판매량</string>
|
||||||
<string name="screen_content_ranking_type_comment_count">댓글수</string>
|
<string name="screen_content_ranking_type_comment_count">댓글수</string>
|
||||||
<string name="screen_content_ranking_type_like_count">좋아요</string>
|
<string name="screen_content_ranking_type_like_count">좋아요</string>
|
||||||
<string name="content_recommendation_section_original_series">오직 보이스온에서만!</string>
|
<string name="content_recommendation_section_original_series">오직 소다라이브에서만!</string>
|
||||||
<string name="content_recommendation_section_latest_audio">새로 올라온 오디오</string>
|
<string name="content_recommendation_section_latest_audio">새로 올라온 오디오</string>
|
||||||
<string name="content_recommendation_section_new_and_hot">New&Hot</string>
|
<string name="content_recommendation_section_new_and_hot">New&Hot</string>
|
||||||
<string name="content_recommendation_section_free_audio">무료 오디오</string>
|
<string name="content_recommendation_section_free_audio">무료 오디오</string>
|
||||||
@@ -322,8 +322,8 @@
|
|||||||
<string name="home_recommendation_activity_community">커뮤니티</string>
|
<string name="home_recommendation_activity_community">커뮤니티</string>
|
||||||
<string name="home_recommendation_follow_all">모두 팔로우 하기</string>
|
<string name="home_recommendation_follow_all">모두 팔로우 하기</string>
|
||||||
<string name="home_recommendation_follow_all_done">모두 팔로우 완료</string>
|
<string name="home_recommendation_follow_all_done">모두 팔로우 완료</string>
|
||||||
<string name="home_recommendation_follow_all_confirm_title">모두 팔로우</string>
|
<string name="home_recommendation_follow_all_confirm_title">모두 팔로우 하기</string>
|
||||||
<string name="home_recommendation_follow_all_confirm_description">목록의 크리에이터를 모두 팔로우하시겠어요?</string>
|
<string name="home_recommendation_follow_all_confirm_description">추천 크리에이터를 모두 팔로우하고,\n소식을 받아보세요!</string>
|
||||||
<string name="home_recommendation_follow_all_confirm_button">팔로우 하기</string>
|
<string name="home_recommendation_follow_all_confirm_button">팔로우 하기</string>
|
||||||
<string name="home_recommendation_more">더보기</string>
|
<string name="home_recommendation_more">더보기</string>
|
||||||
<string name="home_recommendation_collapse">접기</string>
|
<string name="home_recommendation_collapse">접기</string>
|
||||||
@@ -410,7 +410,6 @@
|
|||||||
<string name="creator_channel_home_donation_empty_title">처음으로 크리에이터를\n후원해 보세요!</string>
|
<string name="creator_channel_home_donation_empty_title">처음으로 크리에이터를\n후원해 보세요!</string>
|
||||||
<string name="creator_channel_donation_empty_title">아직 후원이 없습니다.\n처음으로 크리에이터를 후원해 보세요!</string>
|
<string name="creator_channel_donation_empty_title">아직 후원이 없습니다.\n처음으로 크리에이터를 후원해 보세요!</string>
|
||||||
<string name="creator_channel_donation_empty_owner_title">후원 내역이 없습니다</string>
|
<string name="creator_channel_donation_empty_owner_title">후원 내역이 없습니다</string>
|
||||||
<string name="creator_channel_donation_can_format">%1$s캔</string>
|
|
||||||
<string name="creator_channel_donation_fallback_message">%1$d캔을 후원하였습니다.</string>
|
<string name="creator_channel_donation_fallback_message">%1$d캔을 후원하였습니다.</string>
|
||||||
<string name="creator_channel_owner_fab_open">콘텐츠 만들기</string>
|
<string name="creator_channel_owner_fab_open">콘텐츠 만들기</string>
|
||||||
<string name="creator_channel_owner_fab_community">커뮤니티 글 올리기</string>
|
<string name="creator_channel_owner_fab_community">커뮤니티 글 올리기</string>
|
||||||
@@ -441,7 +440,7 @@
|
|||||||
<string name="character_detail_original_link">원작 보러가기</string>
|
<string name="character_detail_original_link">원작 보러가기</string>
|
||||||
<string name="character_detail_personality_title">[성격 및 특징]</string>
|
<string name="character_detail_personality_title">[성격 및 특징]</string>
|
||||||
<string name="character_detail_chat_guide_title">⚠️ 캐릭터톡 대화 가이드</string>
|
<string name="character_detail_chat_guide_title">⚠️ 캐릭터톡 대화 가이드</string>
|
||||||
<string name="character_detail_chat_guide_desc">보이스온 AI캐릭터톡은 대화의 자유도가 높아 대화에 참여하는 당신은 누구든 될 수 있습니다.\n세계관 속 캐릭터로 대화를 하거나 새로운 인물로 캐릭터와 당신만의 스토리를 만들어보세요.</string>
|
<string name="character_detail_chat_guide_desc">소다라이브 AI캐릭터톡은 대화의 자유도가 높아 대화에 참여하는 당신은 누구든 될 수 있습니다.\n세계관 속 캐릭터로 대화를 하거나 새로운 인물로 캐릭터와 당신만의 스토리를 만들어보세요.</string>
|
||||||
<string name="character_detail_chat_guide_notice">※ AI캐릭터톡은 오픈베타 서비스 중이며, 캐릭터의 대화가 어색하거나 불완전할 수 있습니다. 대화 초반에 캐릭터 붕괴가 느껴진다면 대화를 리셋하고 다시 시도해보세요.</string>
|
<string name="character_detail_chat_guide_notice">※ AI캐릭터톡은 오픈베타 서비스 중이며, 캐릭터의 대화가 어색하거나 불완전할 수 있습니다. 대화 초반에 캐릭터 붕괴가 느껴진다면 대화를 리셋하고 다시 시도해보세요.</string>
|
||||||
<string name="character_detail_comments_label">댓글</string>
|
<string name="character_detail_comments_label">댓글</string>
|
||||||
<string name="character_detail_comment_input_hint">댓글을 입력해보세요</string>
|
<string name="character_detail_comment_input_hint">댓글을 입력해보세요</string>
|
||||||
@@ -712,8 +711,8 @@
|
|||||||
<string name="screen_live_room_unknown_error">알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.</string>
|
<string name="screen_live_room_unknown_error">알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.</string>
|
||||||
<string name="screen_live_room_detail_date_format">yyyy년 MM월 dd일 (E) a hh시 mm분</string>
|
<string name="screen_live_room_detail_date_format">yyyy년 MM월 dd일 (E) a hh시 mm분</string>
|
||||||
<string name="screen_live_room_reserver_label">예약자</string>
|
<string name="screen_live_room_reserver_label">예약자</string>
|
||||||
<string name="screen_live_room_share_message_private">%1$s님이 귀하를 보이스온의 비공개라이브에 초대하였습니다.\n※ 라이브 참여: %2$s\n(입장 비밀번호 : %3$s)</string>
|
<string name="screen_live_room_share_message_private">%1$s님이 귀하를 소다라이브의 비공개라이브에 초대하였습니다.\n※ 라이브 참여: %2$s\n(입장 비밀번호 : %3$s)</string>
|
||||||
<string name="screen_live_room_share_message_public">%1$s님이 귀하를 보이스온의 공개라이브에 초대하였습니다.\n※ 라이브 참여: %2$s</string>
|
<string name="screen_live_room_share_message_public">%1$s님이 귀하를 소다라이브의 공개라이브에 초대하였습니다.\n※ 라이브 참여: %2$s</string>
|
||||||
<string name="screen_live_room_dialog_title_no_chat">채팅금지</string>
|
<string name="screen_live_room_dialog_title_no_chat">채팅금지</string>
|
||||||
<string name="screen_live_room_dialog_no_chat_desc">3분간 채팅금지를 하겠습니까?</string>
|
<string name="screen_live_room_dialog_no_chat_desc">3분간 채팅금지를 하겠습니까?</string>
|
||||||
<string name="screen_live_room_dialog_skip">건너뛰기</string>
|
<string name="screen_live_room_dialog_skip">건너뛰기</string>
|
||||||
@@ -952,7 +951,7 @@
|
|||||||
<string name="screen_can_payment_method_unified">통합 결제</string>
|
<string name="screen_can_payment_method_unified">통합 결제</string>
|
||||||
<string name="screen_can_payment_method_phone">휴대폰 결제</string>
|
<string name="screen_can_payment_method_phone">휴대폰 결제</string>
|
||||||
<string name="screen_can_payment_agree">구매조건 확인 및 결제 진행 동의</string>
|
<string name="screen_can_payment_agree">구매조건 확인 및 결제 진행 동의</string>
|
||||||
<string name="screen_can_payment_notice">- 충전된 캔의 유효기간은 충전 후 5년 입니다.\n- 결제 취소는 결제 후 7일 이내에만 할 수 있습니다.\n 단, 캔의 일부를 사용하면 결제 취소할 수 없습니다.\n- 광고성 이벤트 등 회사가 무료로 지급한 \n포인트는 환불되지 않습니다.\n- 자세한 내용은 보이스온 이용약관에서 확인할 수 있습니다.</string>
|
<string name="screen_can_payment_notice">- 충전된 캔의 유효기간은 충전 후 5년 입니다.\n- 결제 취소는 결제 후 7일 이내에만 할 수 있습니다.\n 단, 캔의 일부를 사용하면 결제 취소할 수 없습니다.\n- 광고성 이벤트 등 회사가 무료로 지급한 \n포인트는 환불되지 않습니다.\n- 자세한 내용은 소다라이브 이용약관에서 확인할 수 있습니다.</string>
|
||||||
<string name="screen_can_payment_amount_label">결제금액</string>
|
<string name="screen_can_payment_amount_label">결제금액</string>
|
||||||
<string name="screen_can_payment_pay">결제하기</string>
|
<string name="screen_can_payment_pay">결제하기</string>
|
||||||
<string name="screen_can_payment_currency_unit">원</string>
|
<string name="screen_can_payment_currency_unit">원</string>
|
||||||
@@ -974,7 +973,7 @@
|
|||||||
<string name="msg_can_coupon_unknown_error">알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.</string>
|
<string name="msg_can_coupon_unknown_error">알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.</string>
|
||||||
<!-- Service Center -->
|
<!-- Service Center -->
|
||||||
<string name="screen_service_center_title">고객센터</string>
|
<string name="screen_service_center_title">고객센터</string>
|
||||||
<string name="screen_service_center_logo_label">보이스온 고객센터</string>
|
<string name="screen_service_center_logo_label">소다라이브 고객센터</string>
|
||||||
<string name="screen_service_center_faq_title">자주 묻는 질문</string>
|
<string name="screen_service_center_faq_title">자주 묻는 질문</string>
|
||||||
<string name="screen_service_center_faq_category_all">전체</string>
|
<string name="screen_service_center_faq_category_all">전체</string>
|
||||||
<string name="screen_service_center_kakao">TALK 문의</string>
|
<string name="screen_service_center_kakao">TALK 문의</string>
|
||||||
@@ -1129,7 +1128,7 @@
|
|||||||
<string name="screen_audition_on">ON</string>
|
<string name="screen_audition_on">ON</string>
|
||||||
<string name="screen_audition_off">OFF</string>
|
<string name="screen_audition_off">OFF</string>
|
||||||
<string name="screen_audition_total_count">전체 %1$d개</string>
|
<string name="screen_audition_total_count">전체 %1$d개</string>
|
||||||
<string name="screen_audition_how_to_use">보이스온 오디션 이용방법</string>
|
<string name="screen_audition_how_to_use">소다라이브 오디션 이용방법</string>
|
||||||
<string name="screen_audition_how_to_use_more">자세히></string>
|
<string name="screen_audition_how_to_use_more">자세히></string>
|
||||||
<string name="screen_audition_error_invalid_request">잘못된 요청입니다.\n다시 시도해 주세요.</string>
|
<string name="screen_audition_error_invalid_request">잘못된 요청입니다.\n다시 시도해 주세요.</string>
|
||||||
<string name="screen_audition_detail_info_title">오디션 정보</string>
|
<string name="screen_audition_detail_info_title">오디션 정보</string>
|
||||||
@@ -1148,7 +1147,7 @@
|
|||||||
<string name="dialog_audition_apply_audio_file_title">녹음파일</string>
|
<string name="dialog_audition_apply_audio_file_title">녹음파일</string>
|
||||||
<string name="dialog_audition_apply_phone_title">연락처</string>
|
<string name="dialog_audition_apply_phone_title">연락처</string>
|
||||||
<string name="dialog_audition_apply_phone_hint">합격 시 연락받을 연락처를 남겨주세요</string>
|
<string name="dialog_audition_apply_phone_hint">합격 시 연락받을 연락처를 남겨주세요</string>
|
||||||
<string name="dialog_audition_apply_agreement">보이스온 오디오 드라마 오디션 합격 시 개인 연락을 위한 개인 정보(연락처) 수집 및 활용에 동의합니다.\n오디션 지원자는 개인정보 수집 및 활용 동의에 거부할 권리가 있으며 비동의 시 오디션 지원은 취소됩니다.</string>
|
<string name="dialog_audition_apply_agreement">소다라이브 오디오 드라마 오디션 합격 시 개인 연락을 위한 개인 정보(연락처) 수집 및 활용에 동의합니다.\n오디션 지원자는 개인정보 수집 및 활용 동의에 거부할 권리가 있으며 비동의 시 오디션 지원은 취소됩니다.</string>
|
||||||
<string name="dialog_audition_apply_submit">오디션 지원하기</string>
|
<string name="dialog_audition_apply_submit">오디션 지원하기</string>
|
||||||
<string name="dialog_audition_apply_error_invalid_phone">잘못된 연락처 입니다.\n다시 입력해 주세요.</string>
|
<string name="dialog_audition_apply_error_invalid_phone">잘못된 연락처 입니다.\n다시 입력해 주세요.</string>
|
||||||
<string name="dialog_audition_apply_error_need_agreement">연락처 수집 및 활용에 동의하셔야 오디션 지원이 가능합니다.</string>
|
<string name="dialog_audition_apply_error_need_agreement">연락처 수집 및 활용에 동의하셔야 오디션 지원이 가능합니다.</string>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package kr.co.vividnext.sodalive.common
|
|||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.res.Configuration
|
||||||
import androidx.test.core.app.ApplicationProvider
|
import androidx.test.core.app.ApplicationProvider
|
||||||
import kr.co.vividnext.sodalive.R
|
import kr.co.vividnext.sodalive.R
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
@@ -57,6 +58,21 @@ class RelativeTimeFormatterTest {
|
|||||||
assertEquals(context.getString(R.string.character_comment_time_just_now), text)
|
assertEquals(context.getString(R.string.character_comment_time_just_now), text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `epoch millis는 한국어 영어 일본어 context에서 locale별 경과 분 resource를 반환한다`() {
|
||||||
|
listOf(Locale.KOREAN, Locale.ENGLISH, Locale.JAPANESE).forEach { locale ->
|
||||||
|
val localizedContext = context.createConfigurationContext(
|
||||||
|
Configuration(context.resources.configuration).apply { setLocale(locale) }
|
||||||
|
)
|
||||||
|
val twoMinutesAgoMillis = System.currentTimeMillis() - 2 * MINUTE
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
localizedContext.getString(R.string.character_comment_time_minutes, 2),
|
||||||
|
formatUtcRelativeTimeText(localizedContext, twoMinutesAgoMillis.toString())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun utcIso(millis: Long): String {
|
private fun utcIso(millis: Long): String {
|
||||||
return SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US).apply {
|
return SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US).apply {
|
||||||
timeZone = TimeZone.getTimeZone("UTC")
|
timeZone = TimeZone.getTimeZone("UTC")
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import io.reactivex.rxjava3.plugins.RxJavaPlugins
|
|||||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||||
import kr.co.vividnext.sodalive.common.UtcRelativeTimeTextFormatter
|
|
||||||
import kr.co.vividnext.sodalive.audio_content.comment.ModifyCommentRequest
|
import kr.co.vividnext.sodalive.audio_content.comment.ModifyCommentRequest
|
||||||
import kr.co.vividnext.sodalive.explorer.profile.creator_community.CreatorCommunityRepository
|
import kr.co.vividnext.sodalive.explorer.profile.creator_community.CreatorCommunityRepository
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.detail.CreatorChannelCommunityDetailUiState
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.detail.CreatorChannelCommunityDetailUiState
|
||||||
@@ -58,7 +57,7 @@ class CreatorChannelCommunityDetailViewModelTest {
|
|||||||
SharedPreferenceManager.userId = 10L
|
SharedPreferenceManager.userId = 10L
|
||||||
repository = org.mockito.kotlin.mock()
|
repository = org.mockito.kotlin.mock()
|
||||||
legacyRepository = org.mockito.kotlin.mock()
|
legacyRepository = org.mockito.kotlin.mock()
|
||||||
viewModel = CreatorChannelCommunityDetailViewModel(repository, legacyRepository, testFormatter)
|
viewModel = CreatorChannelCommunityDetailViewModel(repository, legacyRepository)
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
@@ -84,6 +83,7 @@ class CreatorChannelCommunityDetailViewModelTest {
|
|||||||
|
|
||||||
val state = viewModel.detailStateLiveData.requireValue() as CreatorChannelCommunityDetailUiState.Content
|
val state = viewModel.detailStateLiveData.requireValue() as CreatorChannelCommunityDetailUiState.Content
|
||||||
assertEquals(POST_ID, state.post.postId)
|
assertEquals(POST_ID, state.post.postId)
|
||||||
|
assertEquals("2026-07-08T00:00:00Z", state.post.createdAtUtc)
|
||||||
assertEquals("body", state.post.content)
|
assertEquals("body", state.post.content)
|
||||||
assertEquals("image.png", state.post.imageUrl)
|
assertEquals("image.png", state.post.imageUrl)
|
||||||
assertEquals("audio.mp3", state.post.audioUrl)
|
assertEquals("audio.mp3", state.post.audioUrl)
|
||||||
@@ -729,6 +729,5 @@ class CreatorChannelCommunityDetailViewModelTest {
|
|||||||
const val POST_ID = 200L
|
const val POST_ID = 200L
|
||||||
const val COMMENT_ID = 11L
|
const val COMMENT_ID = 11L
|
||||||
const val AUTH_TOKEN = "Bearer test-token"
|
const val AUTH_TOKEN = "Bearer test-token"
|
||||||
val testFormatter = UtcRelativeTimeTextFormatter { "방금 전" }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
package kr.co.vividnext.sodalive.v2.creator.channel.community
|
package kr.co.vividnext.sodalive.v2.creator.channel.community
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.test.core.app.ApplicationProvider
|
|
||||||
import kr.co.vividnext.sodalive.R
|
import kr.co.vividnext.sodalive.R
|
||||||
import kr.co.vividnext.sodalive.common.AndroidUtcRelativeTimeTextFormatter
|
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.data.CreatorChannelCommunityPostResponse
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.data.CreatorChannelCommunityPostResponse
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.model.CreatorChannelCommunityImageMode
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.model.CreatorChannelCommunityImageMode
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.model.CreatorChannelCommunityViewMode
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.model.CreatorChannelCommunityViewMode
|
||||||
@@ -14,17 +10,9 @@ import org.junit.Assert.assertFalse
|
|||||||
import org.junit.Assert.assertNull
|
import org.junit.Assert.assertNull
|
||||||
import org.junit.Assert.assertTrue
|
import org.junit.Assert.assertTrue
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
|
||||||
import org.robolectric.RobolectricTestRunner
|
|
||||||
import org.robolectric.annotation.Config
|
|
||||||
|
|
||||||
@RunWith(RobolectricTestRunner::class)
|
|
||||||
@Config(sdk = [28], application = Application::class)
|
|
||||||
class CreatorChannelCommunityMapperTest {
|
class CreatorChannelCommunityMapperTest {
|
||||||
|
|
||||||
private val context: Context = ApplicationProvider.getApplicationContext()
|
|
||||||
private val relativeTimeTextFormatter = AndroidUtcRelativeTimeTextFormatter(context)
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `보기 방식은 label과 icon resource를 가진다`() {
|
fun `보기 방식은 label과 icon resource를 가진다`() {
|
||||||
assertEquals(R.string.creator_channel_community_view_mode_list, CreatorChannelCommunityViewMode.List.labelResId)
|
assertEquals(R.string.creator_channel_community_view_mode_list, CreatorChannelCommunityViewMode.List.labelResId)
|
||||||
@@ -34,21 +22,22 @@ class CreatorChannelCommunityMapperTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `게시글 기본 필드와 상대 시간 notice 댓글 표시 상태를 매핑한다`() {
|
fun `게시글 기본 필드와 raw UTC notice 댓글 표시 상태를 매핑한다`() {
|
||||||
|
val createdAtUtc = "2026-06-21T00:00:00Z"
|
||||||
val item = listOf(
|
val item = listOf(
|
||||||
communityPost(
|
communityPost(
|
||||||
creatorProfileUrl = "profile.png",
|
creatorProfileUrl = "profile.png",
|
||||||
createdAtUtc = System.currentTimeMillis().toString(),
|
createdAtUtc = createdAtUtc,
|
||||||
isPinned = true,
|
isPinned = true,
|
||||||
isCommentAvailable = true
|
isCommentAvailable = true
|
||||||
)
|
)
|
||||||
).toCommunityPostUiModels(relativeTimeTextFormatter, isOwner = false, currentUserId = 99L).single()
|
).toCommunityPostUiModels(isOwner = false, currentUserId = 99L).single()
|
||||||
|
|
||||||
assertEquals(1L, item.postId)
|
assertEquals(1L, item.postId)
|
||||||
assertEquals(10L, item.creatorId)
|
assertEquals(10L, item.creatorId)
|
||||||
assertEquals("creator", item.creatorNickname)
|
assertEquals("creator", item.creatorNickname)
|
||||||
assertEquals("profile.png", item.creatorProfileUrl)
|
assertEquals("profile.png", item.creatorProfileUrl)
|
||||||
assertEquals(context.getString(R.string.character_comment_time_just_now), item.createdAtText)
|
assertEquals(createdAtUtc, item.createdAtUtc)
|
||||||
assertEquals("hello community", item.content)
|
assertEquals("hello community", item.content)
|
||||||
assertEquals(3, item.likeCount)
|
assertEquals(3, item.likeCount)
|
||||||
assertEquals(4, item.commentCount)
|
assertEquals(4, item.commentCount)
|
||||||
@@ -59,7 +48,7 @@ class CreatorChannelCommunityMapperTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `댓글 불가 게시글은 댓글 표시 상태가 false다`() {
|
fun `댓글 불가 게시글은 댓글 표시 상태가 false다`() {
|
||||||
val item = listOf(communityPost(isCommentAvailable = false))
|
val item = listOf(communityPost(isCommentAvailable = false))
|
||||||
.toCommunityPostUiModels(relativeTimeTextFormatter, isOwner = false, currentUserId = 99L)
|
.toCommunityPostUiModels(isOwner = false, currentUserId = 99L)
|
||||||
.single()
|
.single()
|
||||||
|
|
||||||
assertFalse(item.showComment)
|
assertFalse(item.showComment)
|
||||||
@@ -69,7 +58,7 @@ class CreatorChannelCommunityMapperTest {
|
|||||||
fun `유료 미구매 타인 게시글은 잠금 상태이고 이미지를 숨긴다`() {
|
fun `유료 미구매 타인 게시글은 잠금 상태이고 이미지를 숨긴다`() {
|
||||||
val item = listOf(
|
val item = listOf(
|
||||||
communityPost(price = 100, existOrdered = false, imageUrl = "image.png", audioUrl = "audio.mp3")
|
communityPost(price = 100, existOrdered = false, imageUrl = "image.png", audioUrl = "audio.mp3")
|
||||||
).toCommunityPostUiModels(relativeTimeTextFormatter, isOwner = false, currentUserId = 99L).single()
|
).toCommunityPostUiModels(isOwner = false, currentUserId = 99L).single()
|
||||||
|
|
||||||
assertTrue(item.isLocked)
|
assertTrue(item.isLocked)
|
||||||
assertNull(item.imageUrl)
|
assertNull(item.imageUrl)
|
||||||
@@ -84,7 +73,7 @@ class CreatorChannelCommunityMapperTest {
|
|||||||
existOrdered = false,
|
existOrdered = false,
|
||||||
isCommentAvailable = true
|
isCommentAvailable = true
|
||||||
)
|
)
|
||||||
).toCommunityPostUiModels(relativeTimeTextFormatter, isOwner = false, currentUserId = 99L).single()
|
).toCommunityPostUiModels(isOwner = false, currentUserId = 99L).single()
|
||||||
|
|
||||||
assertTrue(item.isLocked)
|
assertTrue(item.isLocked)
|
||||||
assertFalse(item.showReaction)
|
assertFalse(item.showReaction)
|
||||||
@@ -94,10 +83,10 @@ class CreatorChannelCommunityMapperTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `고정 게시글 여부는 UI 모델에 보존한다`() {
|
fun `고정 게시글 여부는 UI 모델에 보존한다`() {
|
||||||
val pinnedItem = listOf(communityPost(isPinned = true))
|
val pinnedItem = listOf(communityPost(isPinned = true))
|
||||||
.toCommunityPostUiModels(relativeTimeTextFormatter, isOwner = true, currentUserId = 10L)
|
.toCommunityPostUiModels(isOwner = true, currentUserId = 10L)
|
||||||
.single()
|
.single()
|
||||||
val normalItem = listOf(communityPost(isPinned = false))
|
val normalItem = listOf(communityPost(isPinned = false))
|
||||||
.toCommunityPostUiModels(relativeTimeTextFormatter, isOwner = true, currentUserId = 10L)
|
.toCommunityPostUiModels(isOwner = true, currentUserId = 10L)
|
||||||
.single()
|
.single()
|
||||||
|
|
||||||
assertTrue(pinnedItem.isPinned)
|
assertTrue(pinnedItem.isPinned)
|
||||||
@@ -109,16 +98,16 @@ class CreatorChannelCommunityMapperTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `메뉴 권한은 작성자 무료 구매 여부에 따라 계산하고 고정 상태를 보존한다`() {
|
fun `메뉴 권한은 작성자 무료 구매 여부에 따라 계산하고 고정 상태를 보존한다`() {
|
||||||
val authorPaidNotOrdered = listOf(communityPost(price = 100, existOrdered = false, isPinned = true))
|
val authorPaidNotOrdered = listOf(communityPost(price = 100, existOrdered = false, isPinned = true))
|
||||||
.toCommunityPostUiModels(relativeTimeTextFormatter, isOwner = false, currentUserId = 10L)
|
.toCommunityPostUiModels(isOwner = false, currentUserId = 10L)
|
||||||
.single()
|
.single()
|
||||||
val visitorFree = listOf(communityPost(price = 0, existOrdered = false))
|
val visitorFree = listOf(communityPost(price = 0, existOrdered = false))
|
||||||
.toCommunityPostUiModels(relativeTimeTextFormatter, isOwner = false, currentUserId = 99L)
|
.toCommunityPostUiModels(isOwner = false, currentUserId = 99L)
|
||||||
.single()
|
.single()
|
||||||
val visitorPaidOrdered = listOf(communityPost(price = 100, existOrdered = true))
|
val visitorPaidOrdered = listOf(communityPost(price = 100, existOrdered = true))
|
||||||
.toCommunityPostUiModels(relativeTimeTextFormatter, isOwner = false, currentUserId = 99L)
|
.toCommunityPostUiModels(isOwner = false, currentUserId = 99L)
|
||||||
.single()
|
.single()
|
||||||
val visitorPaidNotOrdered = listOf(communityPost(price = 100, existOrdered = false))
|
val visitorPaidNotOrdered = listOf(communityPost(price = 100, existOrdered = false))
|
||||||
.toCommunityPostUiModels(relativeTimeTextFormatter, isOwner = false, currentUserId = 99L)
|
.toCommunityPostUiModels(isOwner = false, currentUserId = 99L)
|
||||||
.single()
|
.single()
|
||||||
|
|
||||||
assertTrue(authorPaidNotOrdered.menuItem.isOwner)
|
assertTrue(authorPaidNotOrdered.menuItem.isOwner)
|
||||||
@@ -134,16 +123,16 @@ class CreatorChannelCommunityMapperTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `본인 채널에 본인이 쓴 유료 게시글에서만 top price를 표시한다`() {
|
fun `본인 채널에 본인이 쓴 유료 게시글에서만 top price를 표시한다`() {
|
||||||
val ownerPaid = listOf(communityPost(price = 100, creatorId = 10L))
|
val ownerPaid = listOf(communityPost(price = 100, creatorId = 10L))
|
||||||
.toCommunityPostUiModels(relativeTimeTextFormatter, isOwner = true, currentUserId = 10L)
|
.toCommunityPostUiModels(isOwner = true, currentUserId = 10L)
|
||||||
.single()
|
.single()
|
||||||
val ownerFree = listOf(communityPost(price = 0, creatorId = 10L))
|
val ownerFree = listOf(communityPost(price = 0, creatorId = 10L))
|
||||||
.toCommunityPostUiModels(relativeTimeTextFormatter, isOwner = true, currentUserId = 10L)
|
.toCommunityPostUiModels(isOwner = true, currentUserId = 10L)
|
||||||
.single()
|
.single()
|
||||||
val otherCreator = listOf(communityPost(price = 100, creatorId = 11L))
|
val otherCreator = listOf(communityPost(price = 100, creatorId = 11L))
|
||||||
.toCommunityPostUiModels(relativeTimeTextFormatter, isOwner = true, currentUserId = 10L)
|
.toCommunityPostUiModels(isOwner = true, currentUserId = 10L)
|
||||||
.single()
|
.single()
|
||||||
val otherChannel = listOf(communityPost(price = 100, creatorId = 10L))
|
val otherChannel = listOf(communityPost(price = 100, creatorId = 10L))
|
||||||
.toCommunityPostUiModels(relativeTimeTextFormatter, isOwner = false, currentUserId = 10L)
|
.toCommunityPostUiModels(isOwner = false, currentUserId = 10L)
|
||||||
.single()
|
.single()
|
||||||
|
|
||||||
assertTrue(ownerPaid.showOwnerTopPrice)
|
assertTrue(ownerPaid.showOwnerTopPrice)
|
||||||
@@ -155,7 +144,7 @@ class CreatorChannelCommunityMapperTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `grid text-only preview는 줄바꿈을 공백으로 바꾸고 trim 후 24자까지만 사용한다`() {
|
fun `grid text-only preview는 줄바꿈을 공백으로 바꾸고 trim 후 24자까지만 사용한다`() {
|
||||||
val item = listOf(communityPost(content = "\n123456789012345678901234567890\n"))
|
val item = listOf(communityPost(content = "\n123456789012345678901234567890\n"))
|
||||||
.toCommunityPostUiModels(relativeTimeTextFormatter, isOwner = false, currentUserId = 99L)
|
.toCommunityPostUiModels(isOwner = false, currentUserId = 99L)
|
||||||
.single()
|
.single()
|
||||||
|
|
||||||
assertEquals("123456789012345678901234", item.gridPreviewText)
|
assertEquals("123456789012345678901234", item.gridPreviewText)
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import io.reactivex.rxjava3.plugins.RxJavaPlugins
|
|||||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||||
import io.reactivex.rxjava3.subjects.SingleSubject
|
import io.reactivex.rxjava3.subjects.SingleSubject
|
||||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||||
import kr.co.vividnext.sodalive.common.AndroidUtcRelativeTimeTextFormatter
|
|
||||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.data.CreatorChannelCommunityPostResponse
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.data.CreatorChannelCommunityPostResponse
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.data.CreatorChannelCommunityTabResponse
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.data.CreatorChannelCommunityTabResponse
|
||||||
@@ -45,7 +44,7 @@ class CreatorChannelCommunityPaginationTest {
|
|||||||
SharedPreferenceManager.init(context)
|
SharedPreferenceManager.init(context)
|
||||||
SharedPreferenceManager.token = "test-token"
|
SharedPreferenceManager.token = "test-token"
|
||||||
repository = org.mockito.kotlin.mock()
|
repository = org.mockito.kotlin.mock()
|
||||||
viewModel = CreatorChannelCommunityViewModel(repository, AndroidUtcRelativeTimeTextFormatter(context))
|
viewModel = CreatorChannelCommunityViewModel(repository)
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
|||||||
@@ -0,0 +1,179 @@
|
|||||||
|
package kr.co.vividnext.sodalive.v2.creator.channel.community
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.test.core.app.ApplicationProvider
|
||||||
|
import kr.co.vividnext.sodalive.R
|
||||||
|
import kr.co.vividnext.sodalive.common.ImageLoaderProvider
|
||||||
|
import kr.co.vividnext.sodalive.common.formatUtcRelativeTimeText
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.detail.CreatorChannelCommunityCommentUiModel
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.detail.CreatorChannelCommunityReplyUiModel
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.detail.reply.ui.CreatorChannelCommunityReplyAdapter
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.detail.ui.CreatorChannelCommunityCommentAdapter
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.model.CreatorChannelCommunityImageMode
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.model.CreatorChannelCommunityMenuItem
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.model.CreatorChannelCommunityPostUiModel
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.ui.CreatorChannelCommunityListAdapter
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.robolectric.RobolectricTestRunner
|
||||||
|
import org.robolectric.RuntimeEnvironment
|
||||||
|
import org.robolectric.annotation.Config
|
||||||
|
import java.io.File
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner::class)
|
||||||
|
@Config(sdk = [28], application = Application::class)
|
||||||
|
class CreatorChannelCommunityRelativeTimeLocaleTest {
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
if (!ImageLoaderProvider.isInitialized) {
|
||||||
|
ImageLoaderProvider.init(RuntimeEnvironment.getApplication())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `영어와 일본어 목록 댓글 답글 bind는 raw UTC를 현재 View locale 상대 시간으로 표시한다`() {
|
||||||
|
listOf(Locale.ENGLISH, Locale.JAPANESE).forEach(::assertAdapterRelativeTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `영어와 일본어 상세와 답글 화면은 raw UTC를 현재 View locale 상대 시간으로 표시한다`() {
|
||||||
|
val detailSource = projectFile(
|
||||||
|
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/detail/" +
|
||||||
|
"CreatorChannelCommunityDetailActivity.kt"
|
||||||
|
).readText()
|
||||||
|
val replySource = projectFile(
|
||||||
|
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/detail/reply/" +
|
||||||
|
"CreatorChannelCommunityReplyActivity.kt"
|
||||||
|
).readText()
|
||||||
|
|
||||||
|
assertTrue(
|
||||||
|
Regex(
|
||||||
|
"tvCreatorChannelCommunityDetailTime\\.text\\s*=\\s*" +
|
||||||
|
"formatUtcRelativeTimeText\\(tvCreatorChannelCommunityDetailTime\\.context, post\\.createdAtUtc\\)"
|
||||||
|
).containsMatchIn(detailSource)
|
||||||
|
)
|
||||||
|
assertTrue(
|
||||||
|
Regex(
|
||||||
|
"layoutCreatorChannelCommunityReplyParent\\.tvCreatorChannelCommunityCommentTime\\.text\\s*=\\s*" +
|
||||||
|
"formatUtcRelativeTimeText\\(\\s*" +
|
||||||
|
"layoutCreatorChannelCommunityReplyParent\\.tvCreatorChannelCommunityCommentTime\\.context,\\s*" +
|
||||||
|
"content\\.parentComment\\.createdAtUtc\\s*\\)"
|
||||||
|
).containsMatchIn(replySource)
|
||||||
|
)
|
||||||
|
listOf(Locale.ENGLISH, Locale.JAPANESE).forEach { locale ->
|
||||||
|
val context = localizedContext(locale)
|
||||||
|
assertEquals(
|
||||||
|
context.getString(R.string.character_comment_time_just_now),
|
||||||
|
formatUtcRelativeTimeText(context, System.currentTimeMillis().toString())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assertAdapterRelativeTime(locale: Locale) {
|
||||||
|
val context = localizedContext(locale)
|
||||||
|
val nowUtc = System.currentTimeMillis().toString()
|
||||||
|
val expected = context.getString(R.string.character_comment_time_just_now)
|
||||||
|
|
||||||
|
val listAdapter = CreatorChannelCommunityListAdapter()
|
||||||
|
val listHolder = listAdapter.onCreateViewHolder(FrameLayout(context), 0)
|
||||||
|
listAdapter.submitItems(listOf(post(nowUtc)))
|
||||||
|
listAdapter.onBindViewHolder(listHolder, 0)
|
||||||
|
assertEquals(
|
||||||
|
expected,
|
||||||
|
listHolder.itemView.findViewById<TextView>(R.id.tv_creator_channel_community_list_time).text.toString()
|
||||||
|
)
|
||||||
|
|
||||||
|
val commentAdapter = CreatorChannelCommunityCommentAdapter(onReplyClick = {})
|
||||||
|
val commentHolder = commentAdapter.onCreateViewHolder(FrameLayout(context), 0)
|
||||||
|
commentAdapter.submitItems(listOf(comment(nowUtc)))
|
||||||
|
commentAdapter.onBindViewHolder(commentHolder, 0)
|
||||||
|
assertEquals(
|
||||||
|
expected,
|
||||||
|
commentHolder.itemView.findViewById<TextView>(R.id.tv_creator_channel_community_comment_time).text.toString()
|
||||||
|
)
|
||||||
|
|
||||||
|
val replyAdapter = CreatorChannelCommunityReplyAdapter(onModifyClick = {}, onDeleteClick = {})
|
||||||
|
val replyHolder = replyAdapter.onCreateViewHolder(FrameLayout(context), 0)
|
||||||
|
replyAdapter.submitItems(listOf(reply(nowUtc)))
|
||||||
|
replyAdapter.onBindViewHolder(replyHolder, 0)
|
||||||
|
assertEquals(
|
||||||
|
expected,
|
||||||
|
replyHolder.itemView.findViewById<TextView>(R.id.tv_creator_channel_community_reply_item_time).text.toString()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun post(createdAtUtc: String) = CreatorChannelCommunityPostUiModel(
|
||||||
|
postId = 1L,
|
||||||
|
creatorId = 2L,
|
||||||
|
creatorNickname = "creator",
|
||||||
|
creatorProfileUrl = "",
|
||||||
|
createdAtUtc = createdAtUtc,
|
||||||
|
content = "post",
|
||||||
|
imageUrl = null,
|
||||||
|
price = 0,
|
||||||
|
existOrdered = true,
|
||||||
|
likeCount = 0,
|
||||||
|
commentCount = 0,
|
||||||
|
showReaction = true,
|
||||||
|
showComment = true,
|
||||||
|
showNotice = false,
|
||||||
|
isPinned = false,
|
||||||
|
isLocked = false,
|
||||||
|
showOwnerTopPrice = false,
|
||||||
|
gridPreviewText = "post",
|
||||||
|
imageMode = CreatorChannelCommunityImageMode.TextPreview,
|
||||||
|
menuItem = CreatorChannelCommunityMenuItem(1L, 2L, 3L, 0, true, false)
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun comment(createdAtUtc: String) = CreatorChannelCommunityCommentUiModel(
|
||||||
|
commentId = 1L,
|
||||||
|
memberId = 2L,
|
||||||
|
memberNickname = "member",
|
||||||
|
memberProfileUrl = "",
|
||||||
|
createdAtUtc = createdAtUtc,
|
||||||
|
comment = "comment",
|
||||||
|
likeCount = 0,
|
||||||
|
replyCount = 0,
|
||||||
|
isLiked = false,
|
||||||
|
isMine = false,
|
||||||
|
isCreatorOwner = false,
|
||||||
|
latestReply = null
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun reply(createdAtUtc: String) = CreatorChannelCommunityReplyUiModel(
|
||||||
|
replyId = 1L,
|
||||||
|
commentId = 2L,
|
||||||
|
memberId = 3L,
|
||||||
|
memberNickname = "member",
|
||||||
|
memberProfileUrl = "",
|
||||||
|
createdAtUtc = createdAtUtc,
|
||||||
|
comment = "reply",
|
||||||
|
likeCount = 0,
|
||||||
|
isLiked = false,
|
||||||
|
isMine = false,
|
||||||
|
isCreatorOwner = false
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun localizedContext(locale: Locale): Context {
|
||||||
|
val context = ApplicationProvider.getApplicationContext<Context>()
|
||||||
|
val configuration = Configuration(context.resources.configuration)
|
||||||
|
configuration.setLocale(locale)
|
||||||
|
return context.createConfigurationContext(configuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun projectFile(path: String): File = File(projectRoot(), path)
|
||||||
|
|
||||||
|
private fun projectRoot(): File {
|
||||||
|
return generateSequence(File(System.getProperty("user.dir") ?: ".").absoluteFile) { it.parentFile }
|
||||||
|
.first { File(it, "settings.gradle").exists() }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,7 +14,6 @@ import io.reactivex.rxjava3.subjects.PublishSubject
|
|||||||
import kr.co.vividnext.sodalive.audio_content.comment.ModifyCommentRequest
|
import kr.co.vividnext.sodalive.audio_content.comment.ModifyCommentRequest
|
||||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||||
import kr.co.vividnext.sodalive.common.UtcRelativeTimeTextFormatter
|
|
||||||
import kr.co.vividnext.sodalive.explorer.profile.creator_community.CreatorCommunityRepository
|
import kr.co.vividnext.sodalive.explorer.profile.creator_community.CreatorCommunityRepository
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.detail.CreatorChannelCommunityCommentUiModel
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.detail.CreatorChannelCommunityCommentUiModel
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.detail.reply.CreatorChannelCommunityReplyUiState
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.detail.reply.CreatorChannelCommunityReplyUiState
|
||||||
@@ -46,7 +45,6 @@ class CreatorChannelCommunityReplyViewModelTest {
|
|||||||
private lateinit var repository: CreatorChannelRepository
|
private lateinit var repository: CreatorChannelRepository
|
||||||
private lateinit var legacyRepository: CreatorCommunityRepository
|
private lateinit var legacyRepository: CreatorCommunityRepository
|
||||||
private lateinit var viewModel: CreatorChannelCommunityReplyViewModel
|
private lateinit var viewModel: CreatorChannelCommunityReplyViewModel
|
||||||
private lateinit var formattedInputs: MutableList<String?>
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
@@ -57,15 +55,7 @@ class CreatorChannelCommunityReplyViewModelTest {
|
|||||||
SharedPreferenceManager.userId = 10L
|
SharedPreferenceManager.userId = 10L
|
||||||
repository = org.mockito.kotlin.mock()
|
repository = org.mockito.kotlin.mock()
|
||||||
legacyRepository = org.mockito.kotlin.mock()
|
legacyRepository = org.mockito.kotlin.mock()
|
||||||
formattedInputs = mutableListOf()
|
viewModel = CreatorChannelCommunityReplyViewModel(repository, legacyRepository)
|
||||||
viewModel = CreatorChannelCommunityReplyViewModel(
|
|
||||||
repository,
|
|
||||||
legacyRepository,
|
|
||||||
UtcRelativeTimeTextFormatter { value ->
|
|
||||||
formattedInputs.add(value)
|
|
||||||
"relative:$value"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
@@ -84,9 +74,9 @@ class CreatorChannelCommunityReplyViewModelTest {
|
|||||||
val state = viewModel.replyStateLiveData.requireValue() as CreatorChannelCommunityReplyUiState.Content
|
val state = viewModel.replyStateLiveData.requireValue() as CreatorChannelCommunityReplyUiState.Content
|
||||||
assertEquals(COMMENT_ID, state.parentComment.commentId)
|
assertEquals(COMMENT_ID, state.parentComment.commentId)
|
||||||
assertEquals("부모 댓글", state.parentComment.comment)
|
assertEquals("부모 댓글", state.parentComment.comment)
|
||||||
assertEquals("relative:2026-07-08T00:00:00Z", state.parentComment.createdAtText)
|
assertEquals("2026-07-08T00:00:00Z", state.parentComment.createdAtUtc)
|
||||||
assertEquals(listOf(101L, 102L), state.replies.map { it.replyId })
|
assertEquals(listOf(101L, 102L), state.replies.map { it.replyId })
|
||||||
assertTrue(formattedInputs.contains("2026-07-08T00:00:00Z"))
|
assertEquals(listOf("2026-07-08T00:00:00Z", "2026-07-08T00:00:00Z"), state.replies.map { it.createdAtUtc })
|
||||||
assertEquals(0, state.replyPage)
|
assertEquals(0, state.replyPage)
|
||||||
assertTrue(state.hasNextReply)
|
assertTrue(state.hasNextReply)
|
||||||
verify(repository).getCommunityCommentReplies(
|
verify(repository).getCommunityCommentReplies(
|
||||||
@@ -499,7 +489,6 @@ class CreatorChannelCommunityReplyViewModelTest {
|
|||||||
memberId = 10L,
|
memberId = 10L,
|
||||||
memberNickname = "member",
|
memberNickname = "member",
|
||||||
memberProfileUrl = "member.png",
|
memberProfileUrl = "member.png",
|
||||||
createdAtText = "이미 포맷된 시간",
|
|
||||||
createdAtUtc = "2026-07-08T00:00:00Z",
|
createdAtUtc = "2026-07-08T00:00:00Z",
|
||||||
comment = "부모 댓글",
|
comment = "부모 댓글",
|
||||||
likeCount = 1,
|
likeCount = 1,
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import io.reactivex.rxjava3.core.Single
|
|||||||
import io.reactivex.rxjava3.plugins.RxJavaPlugins
|
import io.reactivex.rxjava3.plugins.RxJavaPlugins
|
||||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||||
import kr.co.vividnext.sodalive.common.AndroidUtcRelativeTimeTextFormatter
|
|
||||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.data.CreatorChannelCommunityPostResponse
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.data.CreatorChannelCommunityPostResponse
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.data.CreatorChannelCommunityTabResponse
|
import kr.co.vividnext.sodalive.v2.creator.channel.community.data.CreatorChannelCommunityTabResponse
|
||||||
@@ -45,7 +44,7 @@ class CreatorChannelCommunityViewModelTest {
|
|||||||
SharedPreferenceManager.init(context)
|
SharedPreferenceManager.init(context)
|
||||||
SharedPreferenceManager.token = "test-token"
|
SharedPreferenceManager.token = "test-token"
|
||||||
repository = org.mockito.kotlin.mock()
|
repository = org.mockito.kotlin.mock()
|
||||||
viewModel = CreatorChannelCommunityViewModel(repository, AndroidUtcRelativeTimeTextFormatter(context))
|
viewModel = CreatorChannelCommunityViewModel(repository)
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
@@ -66,6 +65,7 @@ class CreatorChannelCommunityViewModelTest {
|
|||||||
assertEquals(0, state.page)
|
assertEquals(0, state.page)
|
||||||
assertEquals(CreatorChannelCommunityViewModel.DEFAULT_PAGE_SIZE, state.size)
|
assertEquals(CreatorChannelCommunityViewModel.DEFAULT_PAGE_SIZE, state.size)
|
||||||
assertEquals(listOf(1L), state.communityPosts.map { it.postId })
|
assertEquals(listOf(1L), state.communityPosts.map { it.postId })
|
||||||
|
assertEquals("2026-06-21T00:00:00Z", state.communityPosts.single().createdAtUtc)
|
||||||
verifyGetCommunity()
|
verifyGetCommunity()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -94,8 +94,8 @@ class CreatorChannelDonationActionTest {
|
|||||||
assertTrue(adapter.contains("if (!isOwner) View.OnClickListener { onEmptyDonationClick() } else null"))
|
assertTrue(adapter.contains("if (!isOwner) View.OnClickListener { onEmptyDonationClick() } else null"))
|
||||||
assertTrue(adapter.contains("bindCreatorChannelDonationCard("))
|
assertTrue(adapter.contains("bindCreatorChannelDonationCard("))
|
||||||
assertTrue(adapter.contains("headerColorResId = item.headerColorResId"))
|
assertTrue(adapter.contains("headerColorResId = item.headerColorResId"))
|
||||||
assertTrue(adapter.contains("createdAtText = item.createdAtText"))
|
assertTrue(adapter.contains("createdAtText = formatUtcRelativeTimeText(context, item.createdAtUtc)"))
|
||||||
assertTrue(adapter.contains("message = item.message"))
|
assertTrue(adapter.contains("R.string.creator_channel_donation_fallback_message"))
|
||||||
assertTrue(adapter.contains("GridLayoutManager(itemView.context, 4)"))
|
assertTrue(adapter.contains("GridLayoutManager(itemView.context, 4)"))
|
||||||
assertTrue(rankingAdapter.contains("item.profileImageUrl"))
|
assertTrue(rankingAdapter.contains("item.profileImageUrl"))
|
||||||
assertTrue(rankingAdapter.contains("item.rank.toString()"))
|
assertTrue(rankingAdapter.contains("item.rank.toString()"))
|
||||||
|
|||||||
@@ -1,14 +1,21 @@
|
|||||||
package kr.co.vividnext.sodalive.v2.creator.channel.donation
|
package kr.co.vividnext.sodalive.v2.creator.channel.donation
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
|
import android.content.res.Configuration
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import android.widget.FrameLayout
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.test.core.app.ApplicationProvider
|
import androidx.test.core.app.ApplicationProvider
|
||||||
import kr.co.vividnext.sodalive.R
|
import kr.co.vividnext.sodalive.R
|
||||||
|
import kr.co.vividnext.sodalive.common.ImageLoaderProvider
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.donation.model.CreatorChannelDonationUiModel
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.donation.ui.CreatorChannelDonationAdapter
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.ui.bindCreatorChannelDonationCard
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Assert.assertNotNull
|
import org.junit.Assert.assertNotNull
|
||||||
import org.junit.Assert.assertSame
|
import org.junit.Assert.assertSame
|
||||||
import org.junit.Assert.assertTrue
|
import org.junit.Assert.assertTrue
|
||||||
@@ -17,6 +24,7 @@ import org.junit.runner.RunWith
|
|||||||
import org.robolectric.RobolectricTestRunner
|
import org.robolectric.RobolectricTestRunner
|
||||||
import org.robolectric.annotation.Config
|
import org.robolectric.annotation.Config
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
@RunWith(RobolectricTestRunner::class)
|
@RunWith(RobolectricTestRunner::class)
|
||||||
@Config(sdk = [28], application = Application::class)
|
@Config(sdk = [28], application = Application::class)
|
||||||
@@ -167,6 +175,63 @@ class CreatorChannelDonationFragmentLayoutTest {
|
|||||||
assertTrue(donationCardBinder.contains("createdAtView.setTextColor(context.getColor(R.color.gray_700))"))
|
assertTrue(donationCardBinder.contains("createdAtView.setTextColor(context.getColor(R.color.gray_700))"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `후원 can badge는 한국어 영어 일본어에서 단위 없이 포맷된 숫자만 표시한다`() {
|
||||||
|
listOf(Locale.KOREAN, Locale.ENGLISH, Locale.JAPANESE)
|
||||||
|
.forEach { locale ->
|
||||||
|
val application = ApplicationProvider.getApplicationContext<Application>()
|
||||||
|
if (!ImageLoaderProvider.isInitialized) ImageLoaderProvider.init(application)
|
||||||
|
val context = application.createConfigurationContext(
|
||||||
|
Configuration(application.resources.configuration).apply { setLocale(locale) }
|
||||||
|
)
|
||||||
|
val canView = TextView(context)
|
||||||
|
bindCreatorChannelDonationCard(
|
||||||
|
View(context), ImageView(context), TextView(context), TextView(context), canView,
|
||||||
|
TextView(context), R.color.gray_200, "", "member", "time", 1000, "message"
|
||||||
|
)
|
||||||
|
assertEquals("1,000", canView.text.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `후원 adapter는 item view locale로 기본 문구와 상대 시간을 표시한다`() {
|
||||||
|
val application = ApplicationProvider.getApplicationContext<Application>()
|
||||||
|
if (!ImageLoaderProvider.isInitialized) ImageLoaderProvider.init(application)
|
||||||
|
val twoMinutesAgoMillis = System.currentTimeMillis() - 2 * MINUTE
|
||||||
|
|
||||||
|
listOf(Locale.KOREAN, Locale.ENGLISH, Locale.JAPANESE).forEach { locale ->
|
||||||
|
val context = application.createConfigurationContext(
|
||||||
|
Configuration(application.resources.configuration).apply { setLocale(locale) }
|
||||||
|
)
|
||||||
|
val adapter = CreatorChannelDonationAdapter()
|
||||||
|
adapter.submitItems(
|
||||||
|
rankings = emptyList(),
|
||||||
|
donations = listOf(
|
||||||
|
CreatorChannelDonationUiModel(
|
||||||
|
nickname = "member",
|
||||||
|
profileImageUrl = "",
|
||||||
|
can = 50,
|
||||||
|
message = "",
|
||||||
|
createdAtUtc = twoMinutesAgoMillis.toString(),
|
||||||
|
headerColorResId = R.color.gray_200
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val viewHolder = adapter.onCreateViewHolder(FrameLayout(context), adapter.getItemViewType(0))
|
||||||
|
|
||||||
|
adapter.onBindViewHolder(viewHolder, 0)
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
context.getString(R.string.creator_channel_donation_fallback_message, 50),
|
||||||
|
viewHolder.itemView.findViewById<TextView>(R.id.tv_creator_channel_donation_message).text.toString()
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
context.getString(R.string.character_comment_time_minutes, 2),
|
||||||
|
viewHolder.itemView.findViewById<TextView>(R.id.tv_creator_channel_donation_created_at).text.toString()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `후원 문자열은 한국어 영어 일본어에 추가된다`() {
|
fun `후원 문자열은 한국어 영어 일본어에 추가된다`() {
|
||||||
val ko = projectFile("app/src/main/res/values/strings.xml").readText()
|
val ko = projectFile("app/src/main/res/values/strings.xml").readText()
|
||||||
@@ -194,4 +259,8 @@ class CreatorChannelDonationFragmentLayoutTest {
|
|||||||
return candidates.firstOrNull { it.exists() }
|
return candidates.firstOrNull { it.exists() }
|
||||||
?: error("Project file not found: $relativePath")
|
?: error("Project file not found: $relativePath")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
const val MINUTE = 60_000L
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,15 @@
|
|||||||
package kr.co.vividnext.sodalive.v2.creator.channel.donation
|
package kr.co.vividnext.sodalive.v2.creator.channel.donation
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.test.core.app.ApplicationProvider
|
|
||||||
import kr.co.vividnext.sodalive.R
|
import kr.co.vividnext.sodalive.R
|
||||||
import kr.co.vividnext.sodalive.common.AndroidUtcRelativeTimeTextFormatter
|
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.donation.data.CreatorChannelDonationResponse
|
import kr.co.vividnext.sodalive.v2.creator.channel.donation.data.CreatorChannelDonationResponse
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.donation.data.MemberDonationRankingResponse
|
import kr.co.vividnext.sodalive.v2.creator.channel.donation.data.MemberDonationRankingResponse
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.donation.model.toDonationRankingUiModels
|
import kr.co.vividnext.sodalive.v2.creator.channel.donation.model.toDonationRankingUiModels
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.donation.model.toDonationUiModels
|
import kr.co.vividnext.sodalive.v2.creator.channel.donation.model.toDonationUiModels
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
|
||||||
import org.robolectric.RobolectricTestRunner
|
|
||||||
import org.robolectric.annotation.Config
|
|
||||||
|
|
||||||
@RunWith(RobolectricTestRunner::class)
|
|
||||||
@Config(sdk = [28], application = Application::class)
|
|
||||||
class CreatorChannelDonationMapperTest {
|
class CreatorChannelDonationMapperTest {
|
||||||
|
|
||||||
private val context: Context = ApplicationProvider.getApplicationContext()
|
|
||||||
private val relativeTimeTextFormatter = AndroidUtcRelativeTimeTextFormatter(context)
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `ranking rank는 응답 순서 기준 1부터 시작하고 profileImage를 profileImageUrl로 매핑한다`() {
|
fun `ranking rank는 응답 순서 기준 1부터 시작하고 profileImage를 profileImageUrl로 매핑한다`() {
|
||||||
val rankings = listOf(
|
val rankings = listOf(
|
||||||
@@ -35,28 +23,40 @@ class CreatorChannelDonationMapperTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `후원 message가 blank이면 can을 포함한 fallback 문구를 사용한다`() {
|
fun `후원 mapper는 message 원문을 보존한다`() {
|
||||||
val item = listOf(donation(can = 50, message = ""))
|
val item = listOf(donation(can = 50, message = ""))
|
||||||
.toDonationUiModels(context, relativeTimeTextFormatter)
|
.toDonationUiModels()
|
||||||
.single()
|
.single()
|
||||||
|
|
||||||
assertEquals(context.getString(R.string.creator_channel_donation_fallback_message, 50), item.message)
|
assertEquals("", item.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `후원 createdAtUtc는 상대 시간으로 매핑하고 can 기준 header color를 계산한다`() {
|
fun `message는 API 원문을 유지한다`() {
|
||||||
val item = listOf(donation(can = 501, createdAtUtc = System.currentTimeMillis().toString()))
|
val items = listOf(
|
||||||
.toDonationUiModels(context, relativeTimeTextFormatter)
|
donation(can = 50, message = ""),
|
||||||
|
donation(can = 50, message = "응원합니다")
|
||||||
|
).toDonationUiModels()
|
||||||
|
|
||||||
|
assertEquals("", items[0].message)
|
||||||
|
assertEquals("응원합니다", items[1].message)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `후원 mapper는 createdAtUtc 원문을 보존하고 can 기준 header color를 계산한다`() {
|
||||||
|
val createdAtUtc = System.currentTimeMillis().toString()
|
||||||
|
val item = listOf(donation(can = 501, createdAtUtc = createdAtUtc))
|
||||||
|
.toDonationUiModels()
|
||||||
.single()
|
.single()
|
||||||
|
|
||||||
assertEquals(context.getString(R.string.character_comment_time_just_now), item.createdAtText)
|
assertEquals(createdAtUtc, item.createdAtUtc)
|
||||||
assertEquals(R.color.red_400, item.headerColorResId)
|
assertEquals(R.color.red_400, item.headerColorResId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `후원 header color는 501캔부터 red_400으로 매핑한다`() {
|
fun `후원 header color는 501캔부터 red_400으로 매핑한다`() {
|
||||||
val items = listOf(donation(can = 500), donation(can = 501))
|
val items = listOf(donation(can = 500), donation(can = 501))
|
||||||
.toDonationUiModels(context, relativeTimeTextFormatter)
|
.toDonationUiModels()
|
||||||
|
|
||||||
assertEquals(R.color.creator_channel_donation_cyan, items[0].headerColorResId)
|
assertEquals(R.color.creator_channel_donation_cyan, items[0].headerColorResId)
|
||||||
assertEquals(R.color.red_400, items[1].headerColorResId)
|
assertEquals(R.color.red_400, items[1].headerColorResId)
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import io.reactivex.rxjava3.plugins.RxJavaPlugins
|
|||||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||||
import io.reactivex.rxjava3.subjects.SingleSubject
|
import io.reactivex.rxjava3.subjects.SingleSubject
|
||||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||||
import kr.co.vividnext.sodalive.common.AndroidUtcRelativeTimeTextFormatter
|
|
||||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||||
import kr.co.vividnext.sodalive.common.SodaLiveApplicationHolder
|
import kr.co.vividnext.sodalive.common.SodaLiveApplicationHolder
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelRepository
|
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelRepository
|
||||||
@@ -48,7 +47,7 @@ class CreatorChannelDonationPaginationTest {
|
|||||||
SodaLiveApplicationHolder.init(context as Application)
|
SodaLiveApplicationHolder.init(context as Application)
|
||||||
SharedPreferenceManager.token = "test-token"
|
SharedPreferenceManager.token = "test-token"
|
||||||
repository = org.mockito.kotlin.mock()
|
repository = org.mockito.kotlin.mock()
|
||||||
viewModel = CreatorChannelDonationViewModel(repository, AndroidUtcRelativeTimeTextFormatter(context))
|
viewModel = CreatorChannelDonationViewModel(repository)
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import io.reactivex.rxjava3.core.Single
|
|||||||
import io.reactivex.rxjava3.plugins.RxJavaPlugins
|
import io.reactivex.rxjava3.plugins.RxJavaPlugins
|
||||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||||
import kr.co.vividnext.sodalive.common.AndroidUtcRelativeTimeTextFormatter
|
|
||||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||||
import kr.co.vividnext.sodalive.common.SodaLiveApplicationHolder
|
import kr.co.vividnext.sodalive.common.SodaLiveApplicationHolder
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelRepository
|
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelRepository
|
||||||
@@ -48,7 +47,7 @@ class CreatorChannelDonationViewModelTest {
|
|||||||
SharedPreferenceManager.token = "test-token"
|
SharedPreferenceManager.token = "test-token"
|
||||||
SharedPreferenceManager.can = 200
|
SharedPreferenceManager.can = 200
|
||||||
repository = org.mockito.kotlin.mock()
|
repository = org.mockito.kotlin.mock()
|
||||||
viewModel = CreatorChannelDonationViewModel(repository, AndroidUtcRelativeTimeTextFormatter(context))
|
viewModel = CreatorChannelDonationViewModel(repository)
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import io.reactivex.rxjava3.schedulers.Schedulers
|
|||||||
import kr.co.vividnext.sodalive.R
|
import kr.co.vividnext.sodalive.R
|
||||||
import kr.co.vividnext.sodalive.common.SodaLiveApplicationHolder
|
import kr.co.vividnext.sodalive.common.SodaLiveApplicationHolder
|
||||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||||
import kr.co.vividnext.sodalive.common.AndroidUtcRelativeTimeTextFormatter
|
|
||||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelRepository
|
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelRepository
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.data.CreatorChannelFanTalkReplyResponse
|
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.data.CreatorChannelFanTalkReplyResponse
|
||||||
@@ -47,7 +46,7 @@ class CreatorChannelFanTalkActionTest {
|
|||||||
SharedPreferenceManager.token = "test-token"
|
SharedPreferenceManager.token = "test-token"
|
||||||
SharedPreferenceManager.userId = 10L
|
SharedPreferenceManager.userId = 10L
|
||||||
repository = org.mockito.kotlin.mock()
|
repository = org.mockito.kotlin.mock()
|
||||||
viewModel = CreatorChannelFanTalkViewModel(repository, AndroidUtcRelativeTimeTextFormatter(context))
|
viewModel = CreatorChannelFanTalkViewModel(repository)
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
package kr.co.vividnext.sodalive.v2.creator.channel.fantalk
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.test.core.app.ApplicationProvider
|
||||||
|
import kr.co.vividnext.sodalive.R
|
||||||
|
import kr.co.vividnext.sodalive.common.ImageLoaderProvider
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.model.CreatorChannelFanTalkReplyUiModel
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.model.CreatorChannelFanTalkRightAction
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.model.CreatorChannelFanTalkUiModel
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.ui.CreatorChannelFanTalkAdapter
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.robolectric.RobolectricTestRunner
|
||||||
|
import org.robolectric.RuntimeEnvironment
|
||||||
|
import org.robolectric.annotation.Config
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner::class)
|
||||||
|
@Config(sdk = [28], application = Application::class)
|
||||||
|
class CreatorChannelFanTalkAdapterLocaleTest {
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
if (!ImageLoaderProvider.isInitialized) {
|
||||||
|
ImageLoaderProvider.init(RuntimeEnvironment.getApplication())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `영어와 일본어 bind는 오염된 신고 문구를 현재 locale 문자열로 복원한다`() {
|
||||||
|
assertReportText(Locale.ENGLISH, "Report")
|
||||||
|
assertReportText(Locale.JAPANESE, "通報")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `영어와 일본어 목록 bind는 원글과 답글 raw UTC를 현재 View locale 상대 시간으로 표시한다`() {
|
||||||
|
assertRelativeTimeText(Locale.ENGLISH)
|
||||||
|
assertRelativeTimeText(Locale.JAPANESE)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assertRelativeTimeText(locale: Locale) {
|
||||||
|
val context = localizedContext(locale)
|
||||||
|
val parent = FrameLayout(context)
|
||||||
|
val adapter = CreatorChannelFanTalkAdapter()
|
||||||
|
val holder = adapter.onCreateViewHolder(parent, 0)
|
||||||
|
val nowUtc = System.currentTimeMillis().toString()
|
||||||
|
adapter.submitItems(listOf(fanTalk(nowUtc)))
|
||||||
|
|
||||||
|
adapter.onBindViewHolder(holder, 0)
|
||||||
|
|
||||||
|
val expected = context.getString(R.string.character_comment_time_just_now)
|
||||||
|
assertEquals(
|
||||||
|
expected,
|
||||||
|
holder.itemView.findViewById<TextView>(R.id.tv_creator_channel_fantalk_time).text.toString()
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
expected,
|
||||||
|
holder.itemView.findViewById<TextView>(R.id.tv_creator_channel_fantalk_reply_time).text.toString()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assertReportText(locale: Locale, expected: String) {
|
||||||
|
val context = localizedContext(locale)
|
||||||
|
val parent = FrameLayout(context)
|
||||||
|
val adapter = CreatorChannelFanTalkAdapter()
|
||||||
|
val holder = adapter.onCreateViewHolder(parent, 0)
|
||||||
|
val report = holder.itemView.findViewById<TextView>(R.id.tv_creator_channel_fantalk_report)
|
||||||
|
report.text = "신고"
|
||||||
|
adapter.submitItems(listOf(fanTalk()))
|
||||||
|
|
||||||
|
adapter.onBindViewHolder(holder, 0)
|
||||||
|
|
||||||
|
assertEquals(expected, report.text.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun localizedContext(locale: Locale): Context {
|
||||||
|
val context = ApplicationProvider.getApplicationContext<Context>()
|
||||||
|
val configuration = Configuration(context.resources.configuration)
|
||||||
|
configuration.setLocale(locale)
|
||||||
|
return context.createConfigurationContext(configuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fanTalk(createdAtUtc: String = "2026-06-21T00:00:00Z") = CreatorChannelFanTalkUiModel(
|
||||||
|
fanTalkId = 1L,
|
||||||
|
writerId = 2L,
|
||||||
|
writerNickname = "writer",
|
||||||
|
writerProfileImageUrl = "",
|
||||||
|
content = "fan talk",
|
||||||
|
createdAtUtc = createdAtUtc,
|
||||||
|
reply = CreatorChannelFanTalkReplyUiModel(
|
||||||
|
fanTalkId = 3L,
|
||||||
|
writerId = 4L,
|
||||||
|
writerNickname = "creator",
|
||||||
|
writerProfileImageUrl = "",
|
||||||
|
content = "reply",
|
||||||
|
createdAtUtc = createdAtUtc
|
||||||
|
),
|
||||||
|
rightAction = CreatorChannelFanTalkRightAction.Report
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,10 +1,5 @@
|
|||||||
package kr.co.vividnext.sodalive.v2.creator.channel.fantalk
|
package kr.co.vividnext.sodalive.v2.creator.channel.fantalk
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.test.core.app.ApplicationProvider
|
|
||||||
import kr.co.vividnext.sodalive.R
|
|
||||||
import kr.co.vividnext.sodalive.common.AndroidUtcRelativeTimeTextFormatter
|
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.data.CreatorChannelFanTalkReplyResponse
|
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.data.CreatorChannelFanTalkReplyResponse
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.data.CreatorChannelFanTalkResponse
|
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.data.CreatorChannelFanTalkResponse
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.model.CreatorChannelFanTalkRightAction
|
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.model.CreatorChannelFanTalkRightAction
|
||||||
@@ -13,35 +8,33 @@ import org.junit.Assert.assertEquals
|
|||||||
import org.junit.Assert.assertNull
|
import org.junit.Assert.assertNull
|
||||||
import org.junit.Assert.assertTrue
|
import org.junit.Assert.assertTrue
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
|
||||||
import org.robolectric.RobolectricTestRunner
|
|
||||||
import org.robolectric.annotation.Config
|
|
||||||
|
|
||||||
@RunWith(RobolectricTestRunner::class)
|
|
||||||
@Config(sdk = [28], application = Application::class)
|
|
||||||
class CreatorChannelFanTalkMapperTest {
|
class CreatorChannelFanTalkMapperTest {
|
||||||
|
|
||||||
private val context: Context = ApplicationProvider.getApplicationContext()
|
|
||||||
private val relativeTimeTextFormatter = AndroidUtcRelativeTimeTextFormatter(context)
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `기본 필드와 상대 시간을 매핑한다`() {
|
fun `기본 필드와 원글 답글 raw UTC를 변경 없이 매핑한다`() {
|
||||||
val item = listOf(fanTalk(createdAtUtc = System.currentTimeMillis().toString()))
|
val item = listOf(
|
||||||
.toFanTalkUiModels(relativeTimeTextFormatter, isOwner = false, currentUserId = 99L)
|
fanTalk(
|
||||||
|
createdAtUtc = "2026-06-21T00:00:00Z",
|
||||||
|
creatorReplies = listOf(reply(createdAtUtc = "2026-06-21T00:01:00Z"))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.toFanTalkUiModels(isOwner = false, currentUserId = 99L)
|
||||||
.single()
|
.single()
|
||||||
|
|
||||||
assertEquals(1L, item.fanTalkId)
|
assertEquals(1L, item.fanTalkId)
|
||||||
assertEquals(10L, item.writerId)
|
assertEquals(10L, item.writerId)
|
||||||
assertEquals("writer", item.writerNickname)
|
assertEquals("writer", item.writerNickname)
|
||||||
assertEquals("profile.png", item.writerProfileImageUrl)
|
assertEquals("profile.png", item.writerProfileImageUrl)
|
||||||
assertEquals(context.getString(R.string.character_comment_time_just_now), item.createdAtText)
|
assertEquals("2026-06-21T00:00:00Z", item.createdAtUtc)
|
||||||
|
assertEquals("2026-06-21T00:01:00Z", item.reply?.createdAtUtc)
|
||||||
assertEquals("hello fan talk", item.content)
|
assertEquals("hello fan talk", item.content)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `creatorReplies가 비어 있으면 reply는 null이다`() {
|
fun `creatorReplies가 비어 있으면 reply는 null이다`() {
|
||||||
val item = listOf(fanTalk(creatorReplies = emptyList()))
|
val item = listOf(fanTalk(creatorReplies = emptyList()))
|
||||||
.toFanTalkUiModels(relativeTimeTextFormatter, isOwner = false, currentUserId = 99L)
|
.toFanTalkUiModels(isOwner = false, currentUserId = 99L)
|
||||||
.single()
|
.single()
|
||||||
|
|
||||||
assertNull(item.reply)
|
assertNull(item.reply)
|
||||||
@@ -56,7 +49,7 @@ class CreatorChannelFanTalkMapperTest {
|
|||||||
reply(fanTalkId = 3L, content = "second reply")
|
reply(fanTalkId = 3L, content = "second reply")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
).toFanTalkUiModels(relativeTimeTextFormatter, isOwner = false, currentUserId = 99L).single()
|
).toFanTalkUiModels(isOwner = false, currentUserId = 99L).single()
|
||||||
|
|
||||||
assertEquals(2L, item.reply?.fanTalkId)
|
assertEquals(2L, item.reply?.fanTalkId)
|
||||||
assertEquals("first reply", item.reply?.content)
|
assertEquals("first reply", item.reply?.content)
|
||||||
@@ -65,7 +58,7 @@ class CreatorChannelFanTalkMapperTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `내가 쓴 글이면 수정과 삭제 owner more action이다`() {
|
fun `내가 쓴 글이면 수정과 삭제 owner more action이다`() {
|
||||||
val item = listOf(fanTalk(writerId = 10L))
|
val item = listOf(fanTalk(writerId = 10L))
|
||||||
.toFanTalkUiModels(relativeTimeTextFormatter, isOwner = false, currentUserId = 10L)
|
.toFanTalkUiModels(isOwner = false, currentUserId = 10L)
|
||||||
.single()
|
.single()
|
||||||
|
|
||||||
val action = item.rightAction as CreatorChannelFanTalkRightAction.OwnerMore
|
val action = item.rightAction as CreatorChannelFanTalkRightAction.OwnerMore
|
||||||
@@ -76,7 +69,7 @@ class CreatorChannelFanTalkMapperTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `내 채널의 타인 글이면 삭제만 가능한 owner more action이다`() {
|
fun `내 채널의 타인 글이면 삭제만 가능한 owner more action이다`() {
|
||||||
val item = listOf(fanTalk(writerId = 11L))
|
val item = listOf(fanTalk(writerId = 11L))
|
||||||
.toFanTalkUiModels(relativeTimeTextFormatter, isOwner = true, currentUserId = 10L)
|
.toFanTalkUiModels(isOwner = true, currentUserId = 10L)
|
||||||
.single()
|
.single()
|
||||||
|
|
||||||
val action = item.rightAction as CreatorChannelFanTalkRightAction.OwnerMore
|
val action = item.rightAction as CreatorChannelFanTalkRightAction.OwnerMore
|
||||||
@@ -87,7 +80,7 @@ class CreatorChannelFanTalkMapperTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `내가 쓴 글도 아니고 내 채널도 아니면 신고 action이다`() {
|
fun `내가 쓴 글도 아니고 내 채널도 아니면 신고 action이다`() {
|
||||||
val item = listOf(fanTalk(writerId = 11L))
|
val item = listOf(fanTalk(writerId = 11L))
|
||||||
.toFanTalkUiModels(relativeTimeTextFormatter, isOwner = false, currentUserId = 10L)
|
.toFanTalkUiModels(isOwner = false, currentUserId = 10L)
|
||||||
.single()
|
.single()
|
||||||
|
|
||||||
assertTrue(item.rightAction is CreatorChannelFanTalkRightAction.Report)
|
assertTrue(item.rightAction is CreatorChannelFanTalkRightAction.Report)
|
||||||
@@ -96,7 +89,7 @@ class CreatorChannelFanTalkMapperTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `원글과 답글 content가 빈 문자열이어도 item을 유지한다`() {
|
fun `원글과 답글 content가 빈 문자열이어도 item을 유지한다`() {
|
||||||
val item = listOf(fanTalk(content = "", creatorReplies = listOf(reply(content = ""))))
|
val item = listOf(fanTalk(content = "", creatorReplies = listOf(reply(content = ""))))
|
||||||
.toFanTalkUiModels(relativeTimeTextFormatter, isOwner = false, currentUserId = 99L)
|
.toFanTalkUiModels(isOwner = false, currentUserId = 99L)
|
||||||
.single()
|
.single()
|
||||||
|
|
||||||
assertEquals("", item.content)
|
assertEquals("", item.content)
|
||||||
@@ -123,13 +116,14 @@ class CreatorChannelFanTalkMapperTest {
|
|||||||
private fun reply(
|
private fun reply(
|
||||||
fanTalkId: Long = 2L,
|
fanTalkId: Long = 2L,
|
||||||
writerId: Long = 20L,
|
writerId: Long = 20L,
|
||||||
content: String = "reply"
|
content: String = "reply",
|
||||||
|
createdAtUtc: String = "2026-06-21T00:00:00Z"
|
||||||
) = CreatorChannelFanTalkReplyResponse(
|
) = CreatorChannelFanTalkReplyResponse(
|
||||||
fanTalkId = fanTalkId,
|
fanTalkId = fanTalkId,
|
||||||
writerId = writerId,
|
writerId = writerId,
|
||||||
writerNickname = "creator",
|
writerNickname = "creator",
|
||||||
writerProfileImageUrl = "creator.png",
|
writerProfileImageUrl = "creator.png",
|
||||||
content = content,
|
content = content,
|
||||||
createdAtUtc = "2026-06-21T00:00:00Z"
|
createdAtUtc = createdAtUtc
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import io.reactivex.rxjava3.plugins.RxJavaPlugins
|
|||||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||||
import io.reactivex.rxjava3.subjects.SingleSubject
|
import io.reactivex.rxjava3.subjects.SingleSubject
|
||||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||||
import kr.co.vividnext.sodalive.common.AndroidUtcRelativeTimeTextFormatter
|
|
||||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelRepository
|
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelRepository
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.data.CreatorChannelFanTalkResponse
|
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.data.CreatorChannelFanTalkResponse
|
||||||
@@ -46,7 +45,7 @@ class CreatorChannelFanTalkPaginationTest {
|
|||||||
SharedPreferenceManager.token = "test-token"
|
SharedPreferenceManager.token = "test-token"
|
||||||
SharedPreferenceManager.userId = 10L
|
SharedPreferenceManager.userId = 10L
|
||||||
repository = org.mockito.kotlin.mock()
|
repository = org.mockito.kotlin.mock()
|
||||||
viewModel = CreatorChannelFanTalkViewModel(repository, AndroidUtcRelativeTimeTextFormatter(context))
|
viewModel = CreatorChannelFanTalkViewModel(repository)
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import io.reactivex.rxjava3.core.Single
|
|||||||
import io.reactivex.rxjava3.plugins.RxJavaPlugins
|
import io.reactivex.rxjava3.plugins.RxJavaPlugins
|
||||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||||
import kr.co.vividnext.sodalive.common.AndroidUtcRelativeTimeTextFormatter
|
|
||||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelRepository
|
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelRepository
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.data.CreatorChannelFanTalkResponse
|
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.data.CreatorChannelFanTalkResponse
|
||||||
@@ -45,7 +44,7 @@ class CreatorChannelFanTalkViewModelTest {
|
|||||||
SharedPreferenceManager.token = "test-token"
|
SharedPreferenceManager.token = "test-token"
|
||||||
SharedPreferenceManager.userId = 10L
|
SharedPreferenceManager.userId = 10L
|
||||||
repository = org.mockito.kotlin.mock()
|
repository = org.mockito.kotlin.mock()
|
||||||
viewModel = CreatorChannelFanTalkViewModel(repository, AndroidUtcRelativeTimeTextFormatter(context))
|
viewModel = CreatorChannelFanTalkViewModel(repository)
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
|||||||