-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Feat] 리액션 모아서 보내기 #246
[Feat] 리액션 모아서 보내기 #246
Changes from all commits
b574697
569c214
5bb70d1
b5519f6
9941d5a
bfde35e
d679cfd
200e1df
c9e699a
5832ece
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package team.ppac.common.kotlin.model | ||
|
||
import java.util.concurrent.atomic.AtomicBoolean | ||
import java.util.concurrent.atomic.AtomicInteger | ||
|
||
class ReactionState { | ||
private val _isFirstClickEvent = AtomicBoolean(true) | ||
private val _reactionCount = AtomicInteger(0) | ||
private val _isUpdating = AtomicBoolean(false) | ||
private var lastClickTime: Long = 0 | ||
|
||
val reactionCount: Int | ||
get() = _reactionCount.get() | ||
|
||
val isUpdating: Boolean | ||
get() = _isUpdating.get() | ||
|
||
val isFirstClickEvent: Boolean | ||
get() = _isFirstClickEvent.get() | ||
|
||
fun setIsFirstClickEvent(value: Boolean) { | ||
_isFirstClickEvent.set(value) | ||
} | ||
|
||
fun addReactionCount(count: Int) { | ||
_reactionCount.addAndGet(count) | ||
} | ||
|
||
fun startUpdate() { | ||
_isUpdating.compareAndSet(false, true) | ||
} | ||
|
||
fun endUpdate() { | ||
_isUpdating.set(false) | ||
} | ||
|
||
fun releaseState() { | ||
_reactionCount.set(0) | ||
_isFirstClickEvent.set(true) | ||
} | ||
|
||
fun isDoubleClickEvent(): Boolean { | ||
val currentClickTime: Long = System.currentTimeMillis() | ||
return if (currentClickTime - lastClickTime <= DOUBLE_CLICK_INTERVAL) { | ||
true | ||
} else { | ||
lastClickTime = System.currentTimeMillis() | ||
false | ||
} | ||
} | ||
|
||
companion object { | ||
private const val DOUBLE_CLICK_INTERVAL = 400 | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package team.ppac.data.mapper | ||
|
||
import team.ppac.domain.model.ReactionMeme | ||
import team.ppac.remote.model.response.meme.ReactionMemeResponse | ||
|
||
internal fun ReactionMemeResponse.toReactionMeme(): ReactionMeme = ReactionMeme( | ||
count = count, | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,13 +15,15 @@ import team.ppac.common.kotlin.model.MultipleEventsCutter | |
@SuppressLint("UnnecessaryComposedModifier") | ||
fun Modifier.noRippleClickable( | ||
enabled: Boolean = true, | ||
isDebounceClick: Boolean = true, | ||
onClickLabel: String? = null, | ||
role: Role? = null, | ||
onClick: () -> Unit, | ||
onClick: () -> Unit | ||
): Modifier = composed { | ||
this then singleClickable( | ||
indication = null, | ||
enabled = enabled, | ||
isDebounceClick = isDebounceClick, | ||
onClickLabel = onClickLabel, | ||
role = role, | ||
onClick = onClick | ||
|
@@ -31,37 +33,43 @@ fun Modifier.noRippleClickable( | |
@SuppressLint("UnnecessaryComposedModifier") | ||
fun Modifier.rippleClickable( | ||
rippleColor: Color = Color.Unspecified, | ||
isDebounceClick: Boolean = true, | ||
enabled: Boolean = true, | ||
onClickLabel: String? = null, | ||
role: Role? = null, | ||
onClick: () -> Unit, | ||
onClick: () -> Unit | ||
): Modifier = composed { | ||
this then singleClickable( | ||
indication = rememberRipple(color = rippleColor), | ||
enabled = enabled, | ||
isDebounceClick = isDebounceClick, | ||
onClickLabel = onClickLabel, | ||
role = role, | ||
onClick = onClick | ||
onClick = onClick, | ||
) | ||
} | ||
|
||
@SuppressLint("ModifierFactoryUnreferencedReceiver") | ||
private fun Modifier.singleClickable( | ||
indication: Indication?, | ||
enabled: Boolean = true, | ||
debounceMillis: Long = 300L, | ||
isDebounceClick: Boolean = true, | ||
onClickLabel: String? = null, | ||
role: Role? = null, | ||
debounceMillis: Long = 300L, | ||
onClick: () -> Unit, | ||
): Modifier = composed { | ||
val multipleEventsCutter = remember { MultipleEventsCutter(debounceMillis) } | ||
|
||
clickable( | ||
interactionSource = remember { MutableInteractionSource() }, | ||
indication = indication, | ||
enabled = enabled, | ||
onClickLabel = onClickLabel, | ||
role = role, | ||
onClick = { multipleEventsCutter.processEvent(onClick) }, | ||
onClick = if (isDebounceClick) { | ||
{ multipleEventsCutter.processEvent(onClick) } | ||
} else { | ||
{ onClick() } | ||
}, | ||
Comment on lines
-65
to
+73
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. singleClickable 모디파이어가 디바운스를 의도하고 만들어진 모디파이어인데 isDebounceClick이 추가된 이유는 무엇인가여 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ㅋㅋ버튼 연타할려면 디바운스 없어야해가지구 isDebounceClick 이 false면 디바운스 미적용 true면 적요으로 할려고 했숩니다! 기존에 있는 Clickable extension이랑 함께 쓸려고 flag로 구분함! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아예 그럼 별도로 디바운스가 없는 단순 clickable을 만드는게 더 좋아보여용, 아니면 기본 clickable을 사용하던가?? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오께이 리팩토링으로 잡겠습니다 |
||
) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package team.ppac.domain.model | ||
|
||
data class ReactionMeme ( | ||
val count: Int | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,17 @@ | ||
package team.ppac.domain.usecase | ||
|
||
import team.ppac.domain.model.ReactionMeme | ||
import team.ppac.domain.repository.MemeRepository | ||
import javax.inject.Inject | ||
|
||
interface ReactMemeUseCase { | ||
suspend operator fun invoke(memeId: String): Boolean | ||
suspend operator fun invoke(memeId: String, count: Int): ReactionMeme | ||
} | ||
|
||
internal class ReactMemeUseCaseImpl @Inject constructor( | ||
private val memeRepository: MemeRepository, | ||
) : ReactMemeUseCase { | ||
override suspend fun invoke(memeId: String): Boolean { | ||
return memeRepository.reactMeme(memeId) | ||
override suspend fun invoke(memeId: String, count: Int): ReactionMeme { | ||
return memeRepository.reactMeme(memeId, count) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package team.ppac.remote.model.request.meme | ||
|
||
import com.squareup.moshi.Json | ||
import com.squareup.moshi.JsonClass | ||
|
||
@JsonClass(generateAdapter = true) | ||
data class ReactMemeRequest ( | ||
@field:Json(name = "count") | ||
val count: Int, | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package team.ppac.remote.model.response.meme | ||
|
||
import com.squareup.moshi.Json | ||
import com.squareup.moshi.JsonClass | ||
|
||
@JsonClass(generateAdapter = true) | ||
data class ReactionMemeResponse ( | ||
@field:Json(name = "count") | ||
val count: Int, | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
각각 타입들이 좀 특이한거같은데 설명 한번 부탁함당
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
private val _isFirstClickEvent = AtomicBoolean(true)
private val _reactionCount = AtomicInteger(0)
private val _isUpdating = AtomicBoolean(false):
Atomic한이유는 코루틴을 병렬로 실행하면서 저기 있는 값들을 변경하는데 동시에 변경하려고 할때 안정성을 보장하려고 다른 코루틴이 읽거나 쓸때 접근하지 못하도록 lock건거입니다!
저거 안하면 병렬 타이밍 이슈로 클릭횟수보다 counting된게 적거나 많고 그러더라구욧
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
신기방기하구만유