Skip to content

Commit

Permalink
Merge pull request #658 from bounswe/development
Browse files Browse the repository at this point in the history
Development
  • Loading branch information
Sefik-Palazoglu authored Dec 25, 2023
2 parents ec78b60 + fd3cdc0 commit 5543f2a
Show file tree
Hide file tree
Showing 254 changed files with 24,566 additions and 1,497 deletions.
11 changes: 10 additions & 1 deletion prediction-polls/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- For Android 13 (API level 33) and above -->
<uses-permission
android:name="android.permission.READ_MEDIA_IMAGES"
android:minSdkVersion="33" />

<!-- For below Android 13 -->
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />


<application
Expand All @@ -15,9 +24,9 @@
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:usesCleartextTraffic="true"
android:supportsRtl="true"
android:theme="@style/Theme.PredictionPolls"
android:usesCleartextTraffic="true"
tools:targetApi="31">
<activity
android:name=".MainActivity"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,49 @@ import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.Text
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavOptions
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.bounswe.predictionpolls.data.remote.TokenManager
import com.bounswe.predictionpolls.ui.common.CommonAppbar
import com.bounswe.predictionpolls.ui.common.NavigationDrawer
import com.bounswe.predictionpolls.ui.create.createPollScreen
import com.bounswe.predictionpolls.ui.editProfile.editProfileScreen
import com.bounswe.predictionpolls.ui.feed.feedScreen
import com.bounswe.predictionpolls.ui.forgotPassword.forgotPasswordScreen
import com.bounswe.predictionpolls.ui.leaderboard.leaderboardScreen
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
import com.bounswe.predictionpolls.ui.theme.PredictionPollsTheme
import com.bounswe.predictionpolls.ui.vote.pollVoteScreen
import com.bounswe.predictionpolls.utils.NavItem
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.collectLatest
import javax.inject.Inject

val EXTRA_ROUTES_WITH_DRAWER = listOf(
MODERATION_ROUTE,
)

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
@Inject
Expand All @@ -43,8 +57,13 @@ class MainActivity : ComponentActivity() {
super.onCreate(savedInstanceState)
setContent {
PredictionPollsTheme {
val mainActivityViewModel: MainActivityViewModel = hiltViewModel()
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 @@ -83,11 +102,19 @@ class MainActivity : ComponentActivity() {
}
) { toggleDrawerState ->
Column {
val mainState by mainActivityViewModel.state.collectAsStateWithLifecycle()
CommonAppbar(
isVisible = currentRoute.value in routesWithDrawer,
onMenuClick = { toggleDrawerState() },
onNotificationClick = { /*TODO implement notification */ }
points = mainState.points
)

LaunchedEffect(Unit) {
navController.currentBackStackEntryFlow.collectLatest {
mainActivityViewModel.fetchCurrentUserProfile()
}
}

NavHost(navController = navController, startDestination = MAIN_ROUTE) {
mainScreen(navController)
loginScreen(navController)
Expand All @@ -97,14 +124,14 @@ class MainActivity : ComponentActivity() {
createPollScreen()
profileScreen(navController)
pollVoteScreen(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") }
myProfileScreen(navController)
editProfileScreen(navController)
forgotPasswordScreen(navController)
moderationApplyScreen(navController)
moderationScreen(navController)
moderationVoteScreen(navController)
}
}

}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.bounswe.predictionpolls

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.bounswe.predictionpolls.common.Result
import com.bounswe.predictionpolls.domain.profile.GetCurrentUserProfileUseCase
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import javax.inject.Inject


data class MainActivityState(val points: Int? = null)

@HiltViewModel
class MainActivityViewModel @Inject constructor(private val fetchCurrentUserProfileUseCase: GetCurrentUserProfileUseCase) :
ViewModel() {


private val _state = MutableStateFlow(MainActivityState())
val state = _state.asStateFlow()


init {
viewModelScope.launch(Dispatchers.IO) {
fetchCurrentUserProfile()
}
}



suspend fun fetchCurrentUserProfile() {
when (val result = fetchCurrentUserProfileUseCase()) {
is Result.Success -> {
val profileInfo = result.data
_state.update {
it.copy(points = profileInfo.points)
}
}

is Result.Error -> {
// handle error
}
}
}
}
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
@@ -0,0 +1,42 @@
package com.bounswe.predictionpolls.data.editProfile

import com.google.gson.annotations.SerializedName
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.asRequestBody
import retrofit2.http.Body
import retrofit2.http.Multipart
import retrofit2.http.PATCH
import retrofit2.http.POST
import retrofit2.http.Part
import java.io.File


interface EditProfileApi {

@Multipart
@POST("profiles/profilePhoto")
suspend fun uploadProfilePhoto(
@Part image: MultipartBody.Part,
@Part("caption") caption: RequestBody
)

@PATCH("profiles")
suspend fun updateProfile(@Body body: EditProfileRequest)
}


data class EditProfileRequest(
val userId: Int?,
val username: String?,
val email: String?,
@SerializedName("profile_picture")
val profilePicture: String?,
val points: Int?,
val biography: String?,
val birthday: String?,
val isHidden: Boolean?
)

fun prepareFilePart(file: File): RequestBody = file.asRequestBody("image/*".toMediaTypeOrNull())
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.bounswe.predictionpolls.data.editProfile

import android.util.Log
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.RequestBody
import java.io.File
import javax.inject.Inject

class EditProfileRepository @Inject constructor(
private val editProfileApi: EditProfileApi
) {

suspend fun uploadProfilePhoto(file: File, caption: String) {

// Create RequestBody instances for image and caption
val requestFile = RequestBody.create("image/*".toMediaTypeOrNull(), file)
val imageBody = MultipartBody.Part.createFormData("image", file.name, requestFile)
val captionBody = RequestBody.create("text/plain".toMediaTypeOrNull(), caption)

try {
editProfileApi.uploadProfilePhoto(imageBody, captionBody)
} catch (error: Exception) {
// Handle network error
Log.e("TAG", "uploadProfilePhoto: $error", )
}
}

suspend fun updateProfile(body: EditProfileRequest) {
editProfileApi.updateProfile(body)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import com.bounswe.predictionpolls.domain.poll.PollOption
import com.google.gson.*
import com.google.gson.annotations.SerializedName
import com.google.gson.reflect.TypeToken
import kotlinx.collections.immutable.toImmutableList
import java.lang.reflect.Type
import kotlinx.collections.immutable.toImmutableList

data class PollResponse(
@SerializedName("error")
Expand Down Expand Up @@ -54,11 +54,13 @@ data class PollResponse(
rejectionText = rejectVotes,
commentCount = 0,
tags = tags,
pollCreatorUsername = creatorUsername,
inputType = when (contPollType) {
"numeric" -> ContinuousVoteInputType.Decimal
"date" -> ContinuousVoteInputType.Date
else -> ContinuousVoteInputType.Text
}
},
isOpen = isOpen
)
}

Expand All @@ -78,9 +80,11 @@ data class PollResponse(
pollCreatorName = creatorName,
pollQuestionTitle = question,
rejectionText = rejectVotes,
pollCreatorUsername = creatorUsername,
commentCount = 0,
tags = tags,
options = options.toImmutableList()
options = options.toImmutableList(),
isOpen = isOpen
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,30 @@
package com.bounswe.predictionpolls.data.profile

import com.bounswe.predictionpolls.data.profile.model.FollowRequest
import com.bounswe.predictionpolls.data.profile.model.GetFollowersRequest
import com.bounswe.predictionpolls.data.profile.model.ProfileInfoResponse
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.Query

interface ProfileApi {

@GET("profiles")
suspend fun fetchProfileInfo(@Query("username") username: String): ProfileInfoResponse

@POST("profiles/follow")
suspend fun followUser(@Body followRequest: FollowRequest)

@POST("profiles/unfollow")
suspend fun unfollowUser(@Body followRequest: FollowRequest)

@POST("profiles/follower")
suspend fun fetchFollowers(@Body getFollowersRequest: GetFollowersRequest): FollowerResponse

@POST("profiles/followed")
suspend fun fetchFollowed(@Body getFollowersRequest: GetFollowersRequest): FollowerResponse

@GET("profiles/myProfile")
suspend fun fetchCurrentUserProfileInfo(): ProfileInfoResponse
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,9 @@ interface ProfileInfoRemoteDataSource {

suspend fun fetchProfileInfo(username: String): Result<ProfileInfoResponse>
suspend fun fetchCurrentUserProfileInfo(): Result<ProfileInfoResponse>

suspend fun followUser(followerId: String, followedId: String): Result<Unit>
suspend fun unfollowUser(followerId: String, followedId: String): Result<Unit>
suspend fun fetchFollowers(userId: String): Result<List<String>>
suspend fun fetchFollowed(userId: String): Result<List<String>>
}
Loading

0 comments on commit 5543f2a

Please sign in to comment.