-
Notifications
You must be signed in to change notification settings - Fork 1
/
VoteBox.kt
203 lines (192 loc) · 6.82 KB
/
VoteBox.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
package com.dluvian.voyage.ui.components
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.AssistChipDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import com.dluvian.voyage.R
import com.dluvian.voyage.core.ClickDownvote
import com.dluvian.voyage.core.ClickNeutralizeVote
import com.dluvian.voyage.core.ClickUpvote
import com.dluvian.voyage.core.EventIdHex
import com.dluvian.voyage.core.Fn
import com.dluvian.voyage.core.OnUpdate
import com.dluvian.voyage.core.PubkeyHex
import com.dluvian.voyage.core.getSignerLauncher
import com.dluvian.voyage.data.interactor.Downvote
import com.dluvian.voyage.data.interactor.NoVote
import com.dluvian.voyage.data.interactor.Upvote
import com.dluvian.voyage.data.interactor.Vote
import com.dluvian.voyage.ui.theme.DenimBlue
import com.dluvian.voyage.ui.theme.DownvoteColor
import com.dluvian.voyage.ui.theme.DownvoteIcon
import com.dluvian.voyage.ui.theme.DownvoteOffIcon
import com.dluvian.voyage.ui.theme.LeftRoundedChip
import com.dluvian.voyage.ui.theme.Orange
import com.dluvian.voyage.ui.theme.RightRoundedChip
import com.dluvian.voyage.ui.theme.RoundedChip
import com.dluvian.voyage.ui.theme.UpvoteColor
import com.dluvian.voyage.ui.theme.UpvoteIcon
import com.dluvian.voyage.ui.theme.UpvoteOffIcon
import com.dluvian.voyage.ui.theme.spacing
@Composable
fun VoteBox(
postId: EventIdHex,
authorPubkey: PubkeyHex,
myVote: Vote,
upvoteCount: Int,
downvoteCount: Int,
onUpdate: OnUpdate,
) {
VoteButtonsAndTally(
postId = postId,
authorPubkey = authorPubkey,
myVote = myVote,
upvoteCount = upvoteCount,
downvoteCount = downvoteCount,
onUpdate = onUpdate
)
}
@Composable
private fun VoteButtonsAndTally(
postId: EventIdHex,
authorPubkey: PubkeyHex,
myVote: Vote,
upvoteCount: Int,
downvoteCount: Int,
onUpdate: OnUpdate,
) {
val signerLauncher = getSignerLauncher(onUpdate = onUpdate)
Row(
modifier = Modifier
.padding(horizontal = spacing.medium)
.border(
border = AssistChipDefaults
.assistChipBorder(enabled = true)
.copy(
brush = getTalliedBrush(
isNeutral = myVote is NoVote,
upvoteCount = upvoteCount,
downvoteCount = downvoteCount
)
),
shape = RoundedChip
),
verticalAlignment = Alignment.CenterVertically,
) {
VoteButton(
isUpvote = true,
isActive = myVote is Upvote,
tint = if (myVote is Upvote) Orange else MaterialTheme.colorScheme.onSurfaceVariant,
description = stringResource(id = R.string.upvote),
onClick = {
if (myVote is Upvote)
onUpdate(
ClickNeutralizeVote(
postId = postId,
mention = authorPubkey,
signerLauncher = signerLauncher
)
)
else
onUpdate(
ClickUpvote(
postId = postId,
mention = authorPubkey,
signerLauncher = signerLauncher
)
)
}
)
val tally = remember(upvoteCount, downvoteCount) { upvoteCount - downvoteCount }
Text(
text = "$tally",
fontWeight = if (myVote.isNeutral()) FontWeight.Normal else FontWeight.SemiBold
)
VoteButton(
isUpvote = false,
isActive = myVote is Downvote,
tint = if (myVote is Downvote) DenimBlue else MaterialTheme.colorScheme.onSurfaceVariant,
description = stringResource(id = R.string.downvote),
onClick = {
if (myVote is Downvote)
onUpdate(
ClickNeutralizeVote(
postId = postId,
mention = authorPubkey,
signerLauncher = signerLauncher
)
)
else
onUpdate(
ClickDownvote(
postId = postId,
mention = authorPubkey,
signerLauncher = signerLauncher
)
)
})
}
}
@Composable
private fun getTalliedBrush(isNeutral: Boolean, upvoteCount: Int, downvoteCount: Int): Brush {
val neutral = SolidColor(MaterialTheme.colorScheme.onSurfaceVariant)
if (isNeutral) return neutral
val totalVotes = remember(upvoteCount, downvoteCount) { upvoteCount + downvoteCount }
return if (totalVotes > 0) {
val upvoteWeight = remember(totalVotes, upvoteCount) {
upvoteCount.toFloat() / totalVotes.toFloat()
}
val aboveZeroPair = remember(upvoteCount, downvoteCount) {
Pair(upvoteCount > 0, downvoteCount > 0)
}
when (aboveZeroPair) {
Pair(true, true) -> {
Brush.horizontalGradient(
0f to UpvoteColor,
upvoteWeight to UpvoteColor,
upvoteWeight to DownvoteColor,
1f to DownvoteColor
)
}
Pair(true, false) -> SolidColor(UpvoteColor)
Pair(false, true) -> SolidColor(DownvoteColor)
else -> neutral
}
} else neutral
}
@Composable
private fun VoteButton(
isUpvote: Boolean,
isActive: Boolean,
tint: Color,
description: String,
onClick: Fn
) {
Icon(
modifier = Modifier
.clip(if (isUpvote) LeftRoundedChip else RightRoundedChip)
.clickable(onClick = onClick)
.padding(horizontal = spacing.xl),
imageVector = if (isUpvote) {
if (isActive) UpvoteIcon else UpvoteOffIcon
} else {
if (isActive) DownvoteIcon else DownvoteOffIcon
},
contentDescription = description,
tint = tint
)
}