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:
@@ -46,6 +46,7 @@ dependencies {
|
|||||||
implementation(libs.androidx.compose.ui.tooling.preview)
|
implementation(libs.androidx.compose.ui.tooling.preview)
|
||||||
implementation(libs.androidx.core.ktx)
|
implementation(libs.androidx.core.ktx)
|
||||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||||
|
implementation(libs.androidx.navigation.compose)
|
||||||
testImplementation(libs.junit)
|
testImplementation(libs.junit)
|
||||||
androidTestImplementation(platform(libs.androidx.compose.bom))
|
androidTestImplementation(platform(libs.androidx.compose.bom))
|
||||||
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
|
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
|
||||||
@@ -56,4 +57,12 @@ dependencies {
|
|||||||
|
|
||||||
implementation(libs.hilt.android)
|
implementation(libs.hilt.android)
|
||||||
ksp(libs.hilt.android.compiler)
|
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)
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<application
|
<application
|
||||||
|
android:name=".MainApplication"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
android:fullBackupContent="@xml/backup_rules"
|
android:fullBackupContent="@xml/backup_rules"
|
||||||
@@ -12,7 +13,7 @@
|
|||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.MiniTicketSystem">
|
android:theme="@style/Theme.MiniTicketSystem">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".activities.MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:theme="@style/Theme.MiniTicketSystem"
|
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()
|
||||||
+7
-9
@@ -1,25 +1,23 @@
|
|||||||
package app.odweta.mts
|
package app.odweta.mts.activities
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.navigation.compose.rememberNavController
|
||||||
import androidx.compose.foundation.layout.padding
|
import app.odweta.mts.nav.NavigationHost
|
||||||
import androidx.compose.material3.Scaffold
|
import app.odweta.mts.ui.screens.list.ListScreen
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
|
||||||
import app.odweta.mts.ui.theme.MiniTicketSystemTheme
|
import app.odweta.mts.ui.theme.MiniTicketSystemTheme
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
enableEdgeToEdge()
|
enableEdgeToEdge()
|
||||||
setContent {
|
setContent {
|
||||||
MiniTicketSystemTheme {
|
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
|
||||||
|
*/
|
||||||
+16
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
@@ -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!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,19 +1,25 @@
|
|||||||
[versions]
|
[versions]
|
||||||
agp = "9.3.1"
|
agp = "9.3.1"
|
||||||
|
converterGson = "3.0.0"
|
||||||
coreKtx = "1.10.1"
|
coreKtx = "1.10.1"
|
||||||
hiltAndroid = "2.60.1"
|
hiltAndroid = "2.60.1"
|
||||||
junit = "4.13.2"
|
junit = "4.13.2"
|
||||||
junitVersion = "1.1.5"
|
junitVersion = "1.1.5"
|
||||||
espressoCore = "3.5.1"
|
espressoCore = "3.5.1"
|
||||||
|
kotlinxCoroutinesAndroid = "1.11.0"
|
||||||
|
kotlinxCoroutinesCore = "1.11.0"
|
||||||
lifecycleRuntimeKtx = "2.6.1"
|
lifecycleRuntimeKtx = "2.6.1"
|
||||||
activityCompose = "1.8.0"
|
activityCompose = "1.8.0"
|
||||||
kotlin = "2.2.10"
|
kotlin = "2.2.10"
|
||||||
composeBom = "2026.02.01"
|
composeBom = "2026.02.01"
|
||||||
ksp = "2.3.11"
|
ksp = "2.3.11"
|
||||||
dagger-hilt = "2.60.1"
|
dagger-hilt = "2.60.1"
|
||||||
|
navigationCompose = "2.9.8"
|
||||||
|
retrofit = "3.0.0"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
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 = { module = "com.google.dagger:hilt-android", version.ref = "hiltAndroid" }
|
||||||
hilt-android-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hiltAndroid" }
|
hilt-android-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hiltAndroid" }
|
||||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
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-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
|
||||||
androidx-compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
androidx-compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
||||||
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
|
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]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
|
|||||||
Reference in New Issue
Block a user