Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Mobile/feature/moderation new #621

Merged
merged 13 commits into from
Dec 24, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ import com.bounswe.predictionpolls.ui.login.loginScreen
import com.bounswe.predictionpolls.ui.main.MAIN_ROUTE
import com.bounswe.predictionpolls.ui.main.mainScreen
import com.bounswe.predictionpolls.ui.main.navigateToMainScreen
import com.bounswe.predictionpolls.ui.moderation.apply.moderationApplyScreen
import com.bounswe.predictionpolls.ui.moderation.list.MODERATION_ROUTE
import com.bounswe.predictionpolls.ui.moderation.list.moderationScreen
import com.bounswe.predictionpolls.ui.moderation.vote.moderationVoteScreen
import com.bounswe.predictionpolls.ui.profile.myProfileScreen
import com.bounswe.predictionpolls.ui.profile.profileScreen
import com.bounswe.predictionpolls.ui.signup.signupScreen
Expand All @@ -37,6 +41,10 @@ import com.bounswe.predictionpolls.utils.NavItem
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject

val EXTRA_ROUTES_WITH_DRAWER = listOf(
MODERATION_ROUTE,
)

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
@Inject
Expand All @@ -47,7 +55,11 @@ class MainActivity : ComponentActivity() {
setContent {
PredictionPollsTheme {
val navController = rememberNavController()
val routesWithDrawer = remember { NavItem.entries.map { it.route }.toSet() }
val routesWithDrawer = remember {
NavItem.entries.map { it.route }.toSet().union(
EXTRA_ROUTES_WITH_DRAWER
)
}
val currentBackStack = navController.currentBackStackEntryAsState()
val currentRoute = rememberUpdatedState(currentBackStack.value?.destination?.route)
val isUserLoggedIn = tokenManager.isLoggedIn.collectAsState(initial = false)
Expand Down Expand Up @@ -104,11 +116,13 @@ class MainActivity : ComponentActivity() {
myProfileScreen(navController)
editProfileScreen(navController)
forgotPasswordScreen(navController)
moderationApplyScreen(navController)
moderationScreen(navController)
moderationVoteScreen(navController)

// TODO: Remove placeholders
composable("settings") { Text(text = "Settings Page WIP") }
composable("notifications") { Text(text = "Notifications Page WIP") }
composable("moderation") { Text(text = "Moderation Page WIP") }
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.bounswe.predictionpolls.core

import android.util.Log
import java.io.IOException
import retrofit2.HttpException

Expand All @@ -26,6 +27,8 @@ abstract class BaseRepository {
else -> {
IOException("Unexpected error occurred. Please try again.")
}
}.also {
Log.d("BaseRepository", "handleException: ${exception.message}")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ data class ProfileInfoResponse(
val biography: String?,
val isHidden: Int?,
val points: Int?,
val isMod: Int?,
val birthday: String?,
) {

Expand All @@ -36,6 +37,7 @@ data class ProfileInfoResponse(
biography,
persistentListOf(),
birthday,
isMod == 1,
isHidden == 1
)
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.bounswe.predictionpolls.data.remote.model.request

data class ModeratorAppointRequest(
val userId: Int
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.bounswe.predictionpolls.data.remote.model.request

import com.google.gson.annotations.SerializedName

data class ModeratorRequest(
@SerializedName("request_id")
val requestId: Int,
val choice: Any
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.bounswe.predictionpolls.data.remote.model.request

data class ModeratorTagRequest(
val topic: String,
val isSelected: Boolean
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package com.bounswe.predictionpolls.data.remote.model.response

import com.bounswe.predictionpolls.domain.moderation.ModeratorPoll
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.google.gson.JsonParseException
import com.google.gson.annotations.SerializedName
import com.google.gson.reflect.TypeToken
import java.lang.reflect.Type

data class ModeratorRequestResponse(
@SerializedName("request_id")
val requestId: Int,
@SerializedName("request_type")
val requestType: String,
@SerializedName("poll")
val poll: Poll
) {
data class Poll(
@SerializedName("id")
val id: Int,
@SerializedName("question")
val question: String,
@SerializedName("tags")
val tags: List<String>,
@SerializedName("creatorName")
val creatorName: String,
@SerializedName("creatorUsername")
val creatorUsername: String,
@SerializedName("creatorImage")
val creatorImage: String?,
@SerializedName("pollType")
val pollType: String,
@SerializedName("closingDate")
val closingDate: String,
@SerializedName("rejectVotes")
val rejectVotes: String,
@SerializedName("isOpen")
val isOpen: Boolean,
@SerializedName("comments")
val comments: List<Comment>,
@SerializedName("options")
val options: List<Option>?,
@SerializedName("cont_poll_type")
val contPollType: String?
) {
data class Comment(
@SerializedName("id")
val id: Int,
@SerializedName("content")
val content: String
) {
fun toComment(): ModeratorPoll.Poll.Comment {
return ModeratorPoll.Poll.Comment(
id = this.id,
content = this.content
)
}
}

data class Option(
@SerializedName("id")
val id: Int,
@SerializedName("choice_text")
val choiceText: String,
@SerializedName("poll_id")
val pollId: Int,
@SerializedName("voter_count")
val voterCount: Int
) {
fun toOption(): ModeratorPoll.Poll.Option {
return ModeratorPoll.Poll.Option(
id = this.id,
choiceText = this.choiceText,
pollId = this.pollId,
voterCount = this.voterCount
)
}
}

fun toPoll(): ModeratorPoll.Poll {
return ModeratorPoll.Poll(
id = this.id,
question = this.question,
tags = this.tags,
creatorName = this.creatorName,
creatorUsername = this.creatorUsername,
creatorImage = this.creatorImage,
pollType = this.pollType,
closingDate = this.closingDate,
rejectVotes = this.rejectVotes,
isOpen = this.isOpen,
comments = this.comments.map { it.toComment() },
options = this.options.orEmpty().map { it.toOption() },
contPollType = this.contPollType
)
}
}

fun toModeratorPoll(): ModeratorPoll {
return ModeratorPoll(
requestId = this.requestId,
requestType = when (this.requestType) {
"report" -> ModeratorPoll.RequestType.REPORT
"discrete" -> ModeratorPoll.RequestType.DISCRETE
else -> ModeratorPoll.RequestType.CONTINUOUS
},
poll = this.poll.toPoll()
)
}
}

class ModeratorRequestPollDeserializer : JsonDeserializer<ModeratorRequestResponse.Poll> {
override fun deserialize(
json: JsonElement,
typeOfT: Type,
context: JsonDeserializationContext
): ModeratorRequestResponse.Poll {
val jsonObject = json.asJsonObject

val id = jsonObject.get("id").asInt
val question = jsonObject.get("question").asString
val tags = context.deserialize(
jsonObject.get("tags"),
object : TypeToken<List<String>>() {}.type
) as List<String>
val creatorName = jsonObject.get("creatorName").asString
val creatorUsername = jsonObject.get("creatorUsername").asString
val creatorImage = try {
jsonObject.get("creatorImage").asString
} catch (e: Exception) {
null
}
val pollType = jsonObject.get("pollType").asString
val closingDate = jsonObject.get("closingDate").asString
val rejectVotes = jsonObject.get("rejectVotes").asString
val isOpen = jsonObject.get("isOpen").asBoolean
val comments = context.deserialize(
jsonObject.get("comments"),
object : TypeToken<List<ModeratorRequestResponse.Poll.Comment>>() {}.type
) as List<ModeratorRequestResponse.Poll.Comment>
val contPollType = jsonObject.get("cont_poll_type")?.asString

var options: List<ModeratorRequestResponse.Poll.Option> = emptyList()
try {
options = context.deserialize(
jsonObject.get("options"),
object : TypeToken<List<ModeratorRequestResponse.Poll.Option>>() {}.type
) as List<ModeratorRequestResponse.Poll.Option>
} catch (e: JsonParseException) {
// If deserialization fails, 'options' will remain null
}

return ModeratorRequestResponse.Poll(
id = id,
question = question,
tags = tags,
creatorName = creatorName,
creatorUsername = creatorUsername,
creatorImage = creatorImage,
pollType = pollType,
closingDate = closingDate,
rejectVotes = rejectVotes,
isOpen = isOpen,
comments = comments,
options = options,
contPollType = contPollType
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.bounswe.predictionpolls.data.remote.model.response

import com.bounswe.predictionpolls.domain.moderation.ModeratorTag

data class ModeratorTagResponse(
val topic: String,
val isSelected: Int
){
fun toModeratorTag(): ModeratorTag {
return ModeratorTag(
topic = this.topic,
isSelected = this.isSelected == 1
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.bounswe.predictionpolls.data.remote.repositories

import com.bounswe.predictionpolls.core.BaseRepository
import com.bounswe.predictionpolls.data.remote.model.request.ModeratorRequest
import com.bounswe.predictionpolls.data.remote.model.request.ModeratorTagRequest
import com.bounswe.predictionpolls.data.remote.services.ModerationService
import com.bounswe.predictionpolls.domain.moderation.ModeratorPoll
import com.bounswe.predictionpolls.domain.moderation.ModeratorTag
import javax.inject.Inject

class ModerationRepository @Inject constructor(
private val moderationService: ModerationService
): ModerationRepositoryInterface, BaseRepository() {
override suspend fun requestPromotion() {
execute {
moderationService.requestPromotion()
}
}

override suspend fun getTags(): List<ModeratorTag> {
return execute {
moderationService.getTags().map { it.toModeratorTag() }
}
}

override suspend fun updateTag(myTag: ModeratorTag) {
execute {
val request = ModeratorTagRequest(myTag.topic, myTag.isSelected)
moderationService.updateTag(request)
}
}

override suspend fun getRequests(): List<ModeratorPoll> {
return execute {
moderationService.getRequests().map { it.toModeratorPoll() }
}
}

override suspend fun concludeRequest(moderatorPoll: ModeratorPoll, choice: Any) {
execute {
val request = ModeratorRequest(
moderatorPoll.requestId,
choice
)
moderationService.concludeRequest(request)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.bounswe.predictionpolls.data.remote.repositories

import com.bounswe.predictionpolls.domain.moderation.ModeratorPoll
import com.bounswe.predictionpolls.domain.moderation.ModeratorTag

interface ModerationRepositoryInterface {
suspend fun requestPromotion()
suspend fun getTags(): List<ModeratorTag>
suspend fun updateTag(myTag: ModeratorTag)
suspend fun getRequests(): List<ModeratorPoll>
suspend fun concludeRequest(moderatorPoll: ModeratorPoll, choice: Any)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.bounswe.predictionpolls.data.remote.services

import com.bounswe.predictionpolls.data.remote.model.request.ModeratorAppointRequest
import com.bounswe.predictionpolls.data.remote.model.request.ModeratorRequest
import com.bounswe.predictionpolls.data.remote.model.request.ModeratorTagRequest
import com.bounswe.predictionpolls.data.remote.model.response.ModeratorRequestResponse
import com.bounswe.predictionpolls.data.remote.model.response.ModeratorTagResponse
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST

interface ModerationService {
@POST("/moderators/appoint")
suspend fun appointModerator(
@Body moderatorAppointRequest: ModeratorAppointRequest
)

@POST("/moderators/request-promotion")
suspend fun requestPromotion()

@GET("/moderators/my-tags")
suspend fun getTags(): List<ModeratorTagResponse>

@POST("/moderators/my-tags")
suspend fun updateTag(
@Body myTag: ModeratorTagRequest
)

@GET("/moderators/my-requests")
suspend fun getRequests(): List<ModeratorRequestResponse>

@POST("/moderators/my-requests")
suspend fun concludeRequest(
@Body request: ModeratorRequest
)
}
Loading