Skip to content

Commit

Permalink
feat: add profile page + dark/light theme + general refactor
Browse files Browse the repository at this point in the history
Signed-off-by: Stefano Cappa <[email protected]>
  • Loading branch information
Ks89 committed Jan 12, 2025
1 parent 814103e commit ec81071
Show file tree
Hide file tree
Showing 24 changed files with 911 additions and 225 deletions.
4 changes: 4 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ dependencies {
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.material3)

// coil image library
implementation(libs.coil.compose)
implementation(libs.coil.network.okhttp)

// Retrofit
implementation(libs.retrofit)
implementation(libs.converter.gson)
Expand Down
28 changes: 18 additions & 10 deletions app/src/main/java/eu/homeanthill/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,16 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import org.koin.androidx.compose.koinViewModel
import eu.homeanthill.ui.theme.AppTheme
import eu.homeanthill.ui.navigation.Graph
import eu.homeanthill.ui.navigation.MainRoute
import eu.homeanthill.ui.screens.home.HomeScreen
import eu.homeanthill.ui.screens.home.HomeViewModel
import eu.homeanthill.ui.screens.login.LoginScreen
import eu.homeanthill.ui.screens.login.LoginViewModel
import eu.homeanthill.ui.theme.HomeAnthillTheme
import org.koin.androidx.compose.koinViewModel
import eu.homeanthill.ui.screens.profile.ProfileScreen
import eu.homeanthill.ui.screens.profile.ProfileViewModel

class MainActivity : ComponentActivity() {
companion object {
Expand Down Expand Up @@ -55,17 +57,15 @@ class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d(TAG, "secret env - API_BASE_URL = ${BuildConfig.API_BASE_URL}")

enableEdgeToEdge()
setContent {
HomeAnthillTheme {
AppTheme(dynamicColor = false) {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
color = MaterialTheme.colorScheme.background,
) {
val navController = rememberNavController()

NavHost(
navController = navController,
route = Graph.MAIN,
Expand All @@ -76,21 +76,29 @@ class MainActivity : ComponentActivity() {
) {
val loginViewModel = koinViewModel<LoginViewModel>()
val loginUiState by loginViewModel.loginUiState.collectAsStateWithLifecycle()

LoginScreen(
loginUiState = loginUiState,
loginViewModel = loginViewModel,
navController = navController
navController = navController,

)
}
composable(
route = MainRoute.Home.name
) {
val homeViewModel = koinViewModel<HomeViewModel>()
val homeUiState by homeViewModel.homeUiState.collectAsStateWithLifecycle()

HomeScreen(
homeUiState = homeUiState,
navController = navController,
)
}
composable(
route = MainRoute.Profile.name
) {
val profileViewModel = koinViewModel<ProfileViewModel>()
val profileUiState by profileViewModel.profileUiState.collectAsStateWithLifecycle()
ProfileScreen(
profileUiState = profileUiState,
navController = navController
)
}
Expand Down
7 changes: 0 additions & 7 deletions app/src/main/java/eu/homeanthill/api/model/LoggedUser.kt

This file was deleted.

This file was deleted.

23 changes: 23 additions & 0 deletions app/src/main/java/eu/homeanthill/api/model/Profile.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package eu.homeanthill.api.model

import com.google.gson.annotations.SerializedName

data class GitHub(
@SerializedName("id") val id: Number,
@SerializedName("login") val login: String,
@SerializedName("name") val name: String,
@SerializedName("email") val email: String,
@SerializedName("avatarURL") val avatarURL: String
)

data class Profile(
@SerializedName("id") val id: String,
@SerializedName("createdAt") val createdAt: String,
@SerializedName("modifiedAt") val modifiedAt: String,
@SerializedName("github") val github: GitHub,
@SerializedName("fcmToken") var fcmToken: String?,
)

data class ProfileAPITokenResponse(
@SerializedName("apiToken") val apiToken: String,
)
12 changes: 0 additions & 12 deletions app/src/main/java/eu/homeanthill/api/requests/LoginAppServices.kt

This file was deleted.

20 changes: 20 additions & 0 deletions app/src/main/java/eu/homeanthill/api/requests/ProfileServices.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package eu.homeanthill.api.requests

import retrofit2.Response
import retrofit2.http.GET
import retrofit2.http.Headers
import retrofit2.http.POST

import eu.homeanthill.api.model.Profile
import eu.homeanthill.api.model.ProfileAPITokenResponse
import retrofit2.http.Path

interface ProfileServices {
@Headers("Accept: application/json")
@GET("profile")
suspend fun getProfile(): Response<Profile>

@Headers("Accept: application/json")
@POST("profile/{id}/tokens")
suspend fun postRegenApiToken(@Path("id") id: String): Response<ProfileAPITokenResponse>
}
18 changes: 14 additions & 4 deletions app/src/main/java/eu/homeanthill/di/KoinModules.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,36 @@ import eu.homeanthill.BuildConfig
import eu.homeanthill.api.AuthInterceptor
import eu.homeanthill.api.SendSavedCookiesInterceptor
import eu.homeanthill.api.requests.FCMTokenServices
import eu.homeanthill.api.requests.LoginServices
import eu.homeanthill.api.requests.ProfileServices
import eu.homeanthill.repository.FCMTokenRepository
import eu.homeanthill.repository.LoginRepository
import eu.homeanthill.repository.ProfileRepository
import eu.homeanthill.ui.screens.home.HomeViewModel
import eu.homeanthill.ui.screens.profile.ProfileViewModel
import eu.homeanthill.ui.screens.login.LoginViewModel


val viewModelModule = module {
viewModel { LoginViewModel(loginRepository = get()) }
viewModel { HomeViewModel(loginRepository = get(), fcmTokenRepository = get()) }
viewModel {
HomeViewModel(
loginRepository = get(),
profileRepository = get(),
fcmTokenRepository = get()
)
}
viewModel { ProfileViewModel(profileRepository = get()) }
}

val repositoryModule = module {
single { FCMTokenRepository(fcmTokenService = get()) }
factory { LoginRepository(context = androidContext()) }
single { FCMTokenRepository(fcmTokenService = get()) }
single { ProfileRepository(profileService = get()) }
}

val apiModule = module {
single { get<Retrofit>().create(FCMTokenServices::class.java) }
single { get<Retrofit>().create(LoginServices::class.java) }
single { get<Retrofit>().create(ProfileServices::class.java) }
}

val retrofitModule = module {
Expand Down
69 changes: 35 additions & 34 deletions app/src/main/java/eu/homeanthill/repository/LoginRepository.kt
Original file line number Diff line number Diff line change
@@ -1,27 +1,16 @@
package eu.homeanthill.repository

import android.content.Context
import com.google.gson.Gson
import com.google.gson.JsonSyntaxException

import eu.homeanthill.api.model.LoggedUser
import eu.homeanthill.api.model.Profile

class LoginRepository(private val context: Context) {
fun login(jwt: String) {
val sharedPreference = context.getSharedPreferences("home-anthill", Context.MODE_PRIVATE)
val editor = sharedPreference.edit()
editor.putString("jwt", jwt)
editor.apply()
}

fun isLoggedIn(): Boolean {
val sharedPreference = context.getSharedPreferences("home-anthill", Context.MODE_PRIVATE)
return sharedPreference.contains("jwt")
}

fun setJWT(jwt: String) {
val sharedPreference = context.getSharedPreferences("home-anthill", Context.MODE_PRIVATE)
val editor = sharedPreference.edit()
editor.putString("jwt", jwt)
editor.apply()
fun hasJWT(): Boolean {
return context
.getSharedPreferences("home-anthill", Context.MODE_PRIVATE)
.contains("jwt")
}

fun getJWT(): String? {
Expand All @@ -30,26 +19,38 @@ class LoginRepository(private val context: Context) {
return jwt
}

fun logout() {
val sharedPreference = context.getSharedPreferences("home-anthill", Context.MODE_PRIVATE)
val editor = sharedPreference.edit()
editor.remove("apiToken")
editor.apply()
}
// setJWT is missing because it's manually set in MainActivity

fun setFCMToken(fcmToken: String) {
val sharedPreference = context.getSharedPreferences("home-anthill", Context.MODE_PRIVATE)
val editor = sharedPreference.edit()
editor.putString("fcmToken", fcmToken)
editor.apply()
context
.getSharedPreferences("home-anthill", Context.MODE_PRIVATE)
.edit()
.putString("fcmToken", fcmToken)
.apply()
}

fun getLoggedUser(): LoggedUser {
fun getFCMToken(): String? {
return context
.getSharedPreferences("home-anthill", Context.MODE_PRIVATE)
.getString("fcmToken", null)
}

fun getLoggedProfile(): Profile? {
val sharedPreference = context.getSharedPreferences("home-anthill", Context.MODE_PRIVATE)
val fcmToken: String? = sharedPreference.getString("fcmToken", null)
val loggedUser: LoggedUser = LoggedUser(
fcmToken = fcmToken,
)
return loggedUser
val json: String = sharedPreference.getString("profile", null) ?: return null
return try {
Gson().fromJson(json, Profile::class.java)
} catch (err: JsonSyntaxException) {
null
}
}

fun setLoggedProfile(profile: Profile) {
val json = Gson().toJson(profile)
context
.getSharedPreferences("home-anthill", Context.MODE_PRIVATE)
.edit()
.putString("profile", json)
.apply()
}
}
27 changes: 27 additions & 0 deletions app/src/main/java/eu/homeanthill/repository/ProfileRepository.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package eu.homeanthill.repository

import eu.homeanthill.api.model.ProfileAPITokenResponse
import java.io.IOException

import eu.homeanthill.api.model.Profile
import eu.homeanthill.api.requests.ProfileServices

class ProfileRepository(private val profileService: ProfileServices) {
suspend fun repoGetProfile(): Profile {
val result = profileService.getProfile()
if (result.isSuccessful) {
return result.body()!!
} else {
throw Exception(IOException("Error repoGetProfile"))
}
}

suspend fun repoPostRegenAPIToken(id: String): ProfileAPITokenResponse {
val result = profileService.postRegenApiToken(id)
if (result.isSuccessful) {
return result.body()!!
} else {
throw Exception(IOException("Error repoPostRegenAPIToken"))
}
}
}
Loading

0 comments on commit ec81071

Please sign in to comment.