Skip to content

Commit

Permalink
Merge pull request #371 from bounswe/development
Browse files Browse the repository at this point in the history
Merge dev to main for internal milestone
  • Loading branch information
EmreBatuhan authored Nov 14, 2023
2 parents 0fd62a0 + e51a398 commit b946fa5
Show file tree
Hide file tree
Showing 94 changed files with 4,270 additions and 566 deletions.
10 changes: 10 additions & 0 deletions prediction-polls/android/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ android {
applicationIdSuffix = ".debug"
isMinifyEnabled = false
buildConfigField("String", "BASE_URL", gradleLocalProperties(rootDir).getProperty("base_url"))
buildConfigField("String", "GOOGLE_CLIENT_ID", gradleLocalProperties(rootDir).getProperty("google_client_id"))
}
create("staging") {
applicationIdSuffix = ".staging"
Expand All @@ -39,6 +40,7 @@ android {
"proguard-rules.pro"
)
buildConfigField("String", "BASE_URL", gradleLocalProperties(rootDir).getProperty("base_url"))
buildConfigField("String", "GOOGLE_CLIENT_ID", gradleLocalProperties(rootDir).getProperty("google_client_id"))
}
release {
isMinifyEnabled = true
Expand All @@ -47,6 +49,8 @@ android {
"proguard-rules.pro"
)
buildConfigField("String", "BASE_URL", gradleLocalProperties(rootDir).getProperty("base_url"))
buildConfigField("String", "GOOGLE_CLIENT_ID", gradleLocalProperties(rootDir).getProperty("google_client_id"))
signingConfig = signingConfigs.getByName("debug")
}
}
compileOptions {
Expand Down Expand Up @@ -124,4 +128,10 @@ dependencies {
// Retrofit
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-gson:2.9.0")

// Coil
implementation("io.coil-kt:coil-compose:2.4.0")

// Easy Google Login
implementation("com.github.stevdza-san:OneTapCompose:1.0.9")
}
2 changes: 2 additions & 0 deletions prediction-polls/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />


<application
android:name=".core.BaseApplication"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import androidx.activity.compose.setContent
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.rememberNavController
import com.bounswe.predictionpolls.ui.feed.feedScreen
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
Expand All @@ -25,6 +26,7 @@ class MainActivity : ComponentActivity() {
loginScreen(navController)
signupScreen(navController)
feedScreen(navController)
leaderboardScreen(navController)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.bounswe.predictionpolls.data.remote.request
package com.bounswe.predictionpolls.data.remote.model.request

data class LoginRequest(
val username: String,
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 LogoutRequest(
val refreshToken: String
)
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.bounswe.predictionpolls.data.remote.request
package com.bounswe.predictionpolls.data.remote.model.request

data class RefreshAccessTokenRequest(
val refreshToken: String
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 SignInWithGoogleRequest(
val googleId: String,
)
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.bounswe.predictionpolls.data.remote.request
package com.bounswe.predictionpolls.data.remote.model.request

data class SignupRequest(
val email: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.bounswe.predictionpolls.data.remote.response
package com.bounswe.predictionpolls.data.remote.model.response

data class LoginResponse(
val accessToken: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.bounswe.predictionpolls.data.remote.response
package com.bounswe.predictionpolls.data.remote.model.response

data class RefreshAccessTokenResponse(
val accessToken: String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ package com.bounswe.predictionpolls.data.remote.repositories

import com.bounswe.predictionpolls.core.BaseRepository
import com.bounswe.predictionpolls.data.remote.TokenManager
import com.bounswe.predictionpolls.data.remote.request.LoginRequest
import com.bounswe.predictionpolls.data.remote.request.LogoutRequest
import com.bounswe.predictionpolls.data.remote.request.RefreshAccessTokenRequest
import com.bounswe.predictionpolls.data.remote.request.SignupRequest
import com.bounswe.predictionpolls.data.remote.model.request.LoginRequest
import com.bounswe.predictionpolls.data.remote.model.request.LogoutRequest
import com.bounswe.predictionpolls.data.remote.model.request.RefreshAccessTokenRequest
import com.bounswe.predictionpolls.data.remote.model.request.SignInWithGoogleRequest
import com.bounswe.predictionpolls.data.remote.model.request.SignupRequest
import com.bounswe.predictionpolls.data.remote.services.AuthService
import javax.inject.Inject

Expand Down Expand Up @@ -60,4 +61,16 @@ class AuthRepository @Inject constructor(
}
return newToken
}

suspend fun loginWithGoogle(
idToken: String
) {
val loginWithGoogleRequest = SignInWithGoogleRequest(idToken)
execute {
authService.loginWithGoogle(loginWithGoogleRequest).let {
tokenManager.accessToken = it.accessToken
tokenManager.refreshToken = it.refreshToken
}
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package com.bounswe.predictionpolls.data.remote.services

import com.bounswe.predictionpolls.data.remote.request.LoginRequest
import com.bounswe.predictionpolls.data.remote.request.LogoutRequest
import com.bounswe.predictionpolls.data.remote.request.RefreshAccessTokenRequest
import com.bounswe.predictionpolls.data.remote.request.SignupRequest
import com.bounswe.predictionpolls.data.remote.response.LoginResponse
import com.bounswe.predictionpolls.data.remote.response.RefreshAccessTokenResponse
import com.bounswe.predictionpolls.data.remote.model.request.LoginRequest
import com.bounswe.predictionpolls.data.remote.model.request.LogoutRequest
import com.bounswe.predictionpolls.data.remote.model.request.RefreshAccessTokenRequest
import com.bounswe.predictionpolls.data.remote.model.request.SignInWithGoogleRequest
import com.bounswe.predictionpolls.data.remote.model.request.SignupRequest
import com.bounswe.predictionpolls.data.remote.model.response.LoginResponse
import com.bounswe.predictionpolls.data.remote.model.response.RefreshAccessTokenResponse
import retrofit2.http.Body
import retrofit2.http.POST

Expand All @@ -29,4 +30,9 @@ interface AuthService {
suspend fun refreshAccessToken(
@Body refreshAccessTokenRequest: RefreshAccessTokenRequest
): RefreshAccessTokenResponse

@POST("/auth/google")
suspend fun loginWithGoogle(
@Body loginWithGoogleRequest: SignInWithGoogleRequest
): LoginResponse
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package com.bounswe.predictionpolls.extensions

import android.os.Build
import java.text.SimpleDateFormat
import java.time.format.DateTimeFormatter
import java.util.Locale

fun String.isValidEmail(): Boolean {
val emailRegex = Regex(
pattern = "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}",
Expand All @@ -8,13 +13,32 @@ fun String.isValidEmail(): Boolean {
return emailRegex.matches(this)
}

// TODO handle date validation better
fun String.isValidDate(): Boolean {
if (this.length != 8) return false
val day = this.substring(0, 2).toIntOrNull() ?: return false
val month = this.substring(2, 4).toIntOrNull() ?: return false

if (day !in 1..31) return false
if (month !in 1..12) return false
return true
val year = this.substring(4, 8)
val month = this.substring(2, 4)
val day = this.substring(0, 2)

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
val dateString = "$day/$month/$year"
val formatter = SimpleDateFormat("dd/MM/yyyy", Locale.ENGLISH).apply {
isLenient = false
}
return try {
formatter.parse(dateString)
true
} catch (e: Exception) {
false
}
} else {
val dateString = "$year-$month-$day"
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
return try {
formatter.parse(dateString)
true
} catch (e: Exception) {
false
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Icon
Expand All @@ -25,6 +26,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
Expand All @@ -42,6 +44,7 @@ fun CustomInputField(
text: String = "",
onTextChanged: (String) -> Unit = {},
isError: Boolean = false,
error: String? = null,
shape: Shape = MaterialTheme.shapes.medium,
@DrawableRes trailingIconId: Int? = null,
@StringRes trailingIconContentDescription: Int? = null,
Expand All @@ -52,44 +55,56 @@ fun CustomInputField(
),
visualTransformation: VisualTransformation = VisualTransformation.None,
) {
TextField(
modifier = modifier.border(1.dp, borderColor.copy(alpha = 0.2f), shape),
value = text,
onValueChange = onTextChanged,
label = {
CustomInputFieldText(
labelId = labelId,
color = borderColor,
)
},
colors = TextFieldDefaults.colors(
errorTextColor = MaterialTheme.colorScheme.error,
focusedTextColor = borderColor,
unfocusedTextColor = borderColor,
unfocusedContainerColor = backgroundColor,
focusedContainerColor = backgroundColor,
unfocusedIndicatorColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent,
errorIndicatorColor = Color.Transparent
),
isError = isError,
shape = shape,
textStyle = MaterialTheme.typography.bodyMedium.copy(
fontSize = 14.sp,
textDecoration = TextDecoration.None
),
trailingIcon = {
CustomInputFieldTrailingIcon(
trailingIconId = trailingIconId,
trailingIconContentDescription = trailingIconContentDescription,
onTrailingIconClicked = onTrailingIconClicked,
)
},
keyboardActions = keyboardActions,
keyboardOptions = keyboardOptions,
visualTransformation = visualTransformation,
)
Column(
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
TextField(
modifier = modifier.border(1.dp, borderColor.copy(alpha = 0.2f), shape),
value = text,
onValueChange = onTextChanged,
label = if (labelId != null) {
{
CustomInputFieldText(
labelId = labelId,
color = borderColor
)
}
} else null,
colors = TextFieldDefaults.colors(
errorTextColor = MaterialTheme.colorScheme.error,
focusedTextColor = borderColor,
unfocusedTextColor = borderColor,
unfocusedContainerColor = backgroundColor,
focusedContainerColor = backgroundColor,
unfocusedIndicatorColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent,
errorIndicatorColor = Color.Transparent,
errorContainerColor = backgroundColor,
errorTrailingIconColor = Color.Unspecified
),
isError = isError,
shape = shape,
textStyle = MaterialTheme.typography.bodyMedium.copy(
fontSize = 14.sp,
textDecoration = TextDecoration.None
),
trailingIcon = {
CustomInputFieldTrailingIcon(
trailingIconId = trailingIconId,
trailingIconContentDescription = trailingIconContentDescription,
onTrailingIconClicked = onTrailingIconClicked,
)
},
keyboardActions = keyboardActions,
keyboardOptions = keyboardOptions,
visualTransformation = visualTransformation,
)
if (isError) ErrorText(
modifier = Modifier.padding(start = 16.dp),
error = error
)
}
}

@Composable
Expand All @@ -104,6 +119,24 @@ fun CustomInputFieldText(
)
}

@Composable
fun ErrorText(
modifier: Modifier = Modifier,
error: String? = null,
) {
Text(
modifier = modifier,
text = error.orEmpty(),
color = MaterialTheme.colorScheme.error,
style = MaterialTheme.typography.bodyMedium.copy(
fontSize = 12.sp,
textDecoration = TextDecoration.None
),
textAlign = TextAlign.Center
)
}


@Composable
fun CustomInputFieldTrailingIcon(
@DrawableRes trailingIconId: Int? = null,
Expand Down Expand Up @@ -162,6 +195,18 @@ fun CustomInputFieldPreview() {
keyboardType = KeyboardType.Number
)
)
CustomInputField(
text = "105123",
labelId = R.string.signup_birthday_label,
trailingIconId = R.drawable.ic_calendar,
trailingIconContentDescription = R.string.cd_calendar,
visualTransformation = DateTransformation(),
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Number
),
isError = true,
error = "Please enter a valid date."
)
}
}
}
Loading

0 comments on commit b946fa5

Please sign in to comment.