realize that i need to call the serveer, not become the server - i was mentally drained when making that i presume

This commit is contained in:
odweta
2026-09-04 11:26:28 +02:00
parent d16fab2dc6
commit cb36335bdb
18 changed files with 308 additions and 10 deletions
+9
View File
@@ -46,6 +46,7 @@ dependencies {
implementation(libs.androidx.compose.ui.tooling.preview)
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.navigation.compose)
testImplementation(libs.junit)
androidTestImplementation(platform(libs.androidx.compose.bom))
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
@@ -56,4 +57,12 @@ dependencies {
implementation(libs.hilt.android)
ksp(libs.hilt.android.compiler)
// retrofit
implementation(libs.retrofit)
// GSON
implementation(libs.converter.gson)
// coroutines
implementation(libs.kotlinx.coroutines.android)
implementation(libs.kotlinx.coroutines.core)
}
+2 -1
View File
@@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools">
<application
android:name=".MainApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
@@ -12,7 +13,7 @@
android:supportsRtl="true"
android:theme="@style/Theme.MiniTicketSystem">
<activity
android:name=".MainActivity"
android:name=".activities.MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.MiniTicketSystem"
@@ -0,0 +1,7 @@
package app.odweta.mts
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
class MainApplication : Application()
@@ -1,25 +1,23 @@
package app.odweta.mts
package app.odweta.mts.activities
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.navigation.compose.rememberNavController
import app.odweta.mts.nav.NavigationHost
import app.odweta.mts.ui.screens.list.ListScreen
import app.odweta.mts.ui.theme.MiniTicketSystemTheme
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
MiniTicketSystemTheme {
NavigationHost()
}
}
}
@@ -0,0 +1,22 @@
package app.odweta.mts.data
import app.odweta.mts.data.api.TicketApi
import app.odweta.mts.data.model.Ticket
import app.odweta.mts.data.network.RetrofitClient
import retrofit2.Retrofit
import javax.inject.Inject
class TicketRepository @Inject constructor(
private val retrofit: Retrofit = RetrofitClient.getClient(),
private val ticketApi: TicketApi
) {
suspend fun successfulUsersResponse() {
val usersResponse = ticketApi.getTickets().execute()
val successful = usersResponse.isSuccessful
val httpStatusCode = usersResponse.code()
val httpStatusMessage = usersResponse.message()
val tickets: List<Ticket>? = usersResponse.body()
}
}
@@ -0,0 +1,20 @@
package app.odweta.mts.data.api
import app.odweta.mts.data.model.Ticket
import retrofit2.Call
import retrofit2.Response
import retrofit2.http.GET
import retrofit2.http.PATCH
import retrofit2.http.POST
import retrofit2.http.Path
interface TicketApi {
@GET("/tickets")
suspend fun getTickets(): Call<List<Ticket>>
@POST("/tickets")
suspend fun createTicket(): Call<Ticket>
@PATCH("/tickets/{id}")
suspend fun updateTicket(@Path("id") id: Long)
}
@@ -0,0 +1,12 @@
package app.odweta.mts.data.model
import java.time.LocalDateTime
data class Ticket(
private val id: Long,
private val title: String,
private val description: String,
private val status: TicketStatus,
private val createdAt: LocalDateTime,
private val updatedAt: LocalDateTime
)
@@ -0,0 +1,7 @@
package app.odweta.mts.data.model
enum class TicketStatus {
Open,
InProgress,
Closed
}
@@ -0,0 +1,29 @@
package app.odweta.mts.data.network
import app.odweta.mts.data.network.interceptors.AuthorizationInterceptor
import app.odweta.mts.data.network.interceptors.RequestInterceptor
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
object RetrofitClient {
private const val BASE_URL = "http://localhost:8090/"
val okHttpClient = OkHttpClient()
.newBuilder()
.addInterceptor(RequestInterceptor)
.addInterceptor(AuthorizationInterceptor)
.build()
fun getClient(): Retrofit =
Retrofit.Builder()
.client(okHttpClient)
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
/*
TODO: add token generation to the server
TODO: add a real auth call for that token in the AuthorizationInterceptor
*/
@@ -0,0 +1,16 @@
package app.odweta.mts.data.network.interceptors
import okhttp3.Interceptor
import okhttp3.Response
import java.util.UUID
object AuthorizationInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val requestWithHeader = chain.request()
.newBuilder()
.header(
"Authorization", UUID.randomUUID().toString()
).build()
return chain.proceed(requestWithHeader)
}
}
@@ -0,0 +1,12 @@
package app.odweta.mts.data.network.interceptors
import okhttp3.Interceptor
import okhttp3.Response
object RequestInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
println("Outgoing request to ${request.url()}")
return chain.proceed(request)
}
}
@@ -0,0 +1,46 @@
package app.odweta.mts.nav
import androidx.compose.animation.SharedTransitionLayout
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.scaleIn
import androidx.compose.animation.scaleOut
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.toRoute
import app.odweta.mts.ui.screens.list.ListScreen
import app.odweta.mts.ui.screens.create.CreateScreen
import app.odweta.mts.ui.screens.detail.DetailScreen
@Composable
fun NavigationHost() {
val navController = rememberNavController()
val startDestination = remember { Route.ListScreen }
NavHost(
navController = navController,
startDestination = startDestination
) {
composable<Route.ListScreen> {
ListScreen(navController = navController)
}
composable<Route.CreateScreen> {
CreateScreen(navController = navController)
}
composable<Route.DetailScreen> { backStackEntry ->
val ticketId = backStackEntry.arguments?.getLong("ticketId")
?: throw IllegalArgumentException("Missing required argument: ticketId for DetailScreen")
DetailScreen(
navController = navController,
ticketId = ticketId
)
}
}
}
@@ -0,0 +1,16 @@
package app.odweta.mts.nav
import kotlinx.serialization.Serializable
sealed interface Route {
@Serializable
data object ListScreen : Route
@Serializable
data object CreateScreen : Route
@Serializable
data class DetailScreen(
val ticketId: Long
) : Route
}
@@ -0,0 +1,26 @@
package app.odweta.mts.ui.screens.create
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.navigation.NavController
@Composable
fun CreateScreen(
navController: NavController,
modifier: Modifier = Modifier
) {
Column(
modifier = Modifier.fillMaxSize()
) {
Text("create a ticket")
TextButton(onClick = {
navController.popBackStack()
}) {
Text("back!")
}
}
}
@@ -0,0 +1,4 @@
package app.odweta.mts.ui.screens.create
class CreateScreenViewModel {
}
@@ -0,0 +1,27 @@
package app.odweta.mts.ui.screens.detail
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.navigation.NavController
@Composable
fun DetailScreen(
navController: NavController,
ticketId: Long,
modifier: Modifier = Modifier
) {
Column(
modifier = Modifier.fillMaxSize()
) {
Text("detail of ticket id $ticketId")
TextButton(onClick = {
navController.popBackStack()
}) {
Text("back!")
}
}
}
@@ -0,0 +1,36 @@
package app.odweta.mts.ui.screens.list
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.navigation.NavController
import app.odweta.mts.nav.Route
@Composable
fun ListScreen(
navController: NavController,
modifier: Modifier = Modifier
) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("all of the tickets")
TextButton(onClick = {
navController.navigate(Route.CreateScreen)
}) {
Text("to create screen!")
}
TextButton(onClick = {
navController.navigate(Route.DetailScreen(1))
}) {
Text("to detail screen!")
}
}
}
+10
View File
@@ -1,19 +1,25 @@
[versions]
agp = "9.3.1"
converterGson = "3.0.0"
coreKtx = "1.10.1"
hiltAndroid = "2.60.1"
junit = "4.13.2"
junitVersion = "1.1.5"
espressoCore = "3.5.1"
kotlinxCoroutinesAndroid = "1.11.0"
kotlinxCoroutinesCore = "1.11.0"
lifecycleRuntimeKtx = "2.6.1"
activityCompose = "1.8.0"
kotlin = "2.2.10"
composeBom = "2026.02.01"
ksp = "2.3.11"
dagger-hilt = "2.60.1"
navigationCompose = "2.9.8"
retrofit = "3.0.0"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "converterGson" }
hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hiltAndroid" }
hilt-android-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hiltAndroid" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
@@ -29,6 +35,10 @@ androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "u
androidx-compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
androidx-compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" }
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinxCoroutinesAndroid" }
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutinesCore" }
retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }