chore: remove obsolete Android app configuration files
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 44s
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 44s
- Deleted build.gradle.kts, gradle.properties, and gradlew files as part of the cleanup process. - Removed local.properties and various generated files from the .gradle directory to streamline the project structure. - Cleared out unnecessary build artifacts and intermediate files to improve project maintainability.
This commit is contained in:
37
mobile-app/shared/build.gradle.kts
Normal file
37
mobile-app/shared/build.gradle.kts
Normal file
@@ -0,0 +1,37 @@
|
||||
plugins {
|
||||
alias(libs.plugins.kotlinMultiplatform)
|
||||
alias(libs.plugins.androidLibrary)
|
||||
alias(libs.plugins.kotlinSerialization)
|
||||
}
|
||||
|
||||
kotlin {
|
||||
androidTarget {
|
||||
compilations.all {
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
commonMain.dependencies {
|
||||
implementation(libs.ktor.client.core)
|
||||
implementation(libs.ktor.client.content.negotiation)
|
||||
implementation(libs.ktor.serialization.kotlinx.json)
|
||||
implementation(libs.kotlinx.coroutines.core)
|
||||
implementation(libs.koin.core)
|
||||
implementation(libs.socket.io.client)
|
||||
}
|
||||
androidMain.dependencies {
|
||||
implementation(libs.ktor.client.okhttp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "de.tt_tagebuch.shared"
|
||||
compileSdk = libs.versions.android.compileSdk.get().toInt()
|
||||
defaultConfig {
|
||||
minSdk = libs.versions.android.minSdk.get().toInt()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package de.tt_tagebuch.shared.api
|
||||
|
||||
import de.tt_tagebuch.shared.models.*
|
||||
import io.ktor.client.*
|
||||
import io.ktor.client.call.*
|
||||
import io.ktor.client.plugins.contentnegotiation.*
|
||||
import io.ktor.client.request.*
|
||||
import io.ktor.serialization.kotlinx.json.*
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
class ApiClient(private val baseUrl: String) {
|
||||
private val client = HttpClient {
|
||||
install(ContentNegotiation) {
|
||||
json(Json {
|
||||
ignoreUnknownKeys = true
|
||||
coerceInputValues = true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private var token: String? = null
|
||||
|
||||
fun setToken(token: String?) {
|
||||
this.token = token
|
||||
}
|
||||
|
||||
suspend fun login(email: String, password: String): LoginResponse {
|
||||
return client.post("$baseUrl/api/auth/login") {
|
||||
setBody(mapOf("email" to email, "password" to password))
|
||||
header("Content-Type", "application/json")
|
||||
}.body()
|
||||
}
|
||||
|
||||
suspend fun getClubs(): List<Club> {
|
||||
return client.get("$baseUrl/api/clubs") {
|
||||
token?.let { header("Authorization", "Bearer $it") }
|
||||
}.body()
|
||||
}
|
||||
|
||||
suspend fun getDiaryDates(clubId: Int): List<DiaryDate> {
|
||||
return client.get("$baseUrl/api/diary/$clubId") {
|
||||
token?.let { header("Authorization", "Bearer $it") }
|
||||
}.body()
|
||||
}
|
||||
|
||||
suspend fun getDiaryParticipants(dateId: Int): List<Participant> {
|
||||
return client.get("$baseUrl/api/participants/$dateId") {
|
||||
token?.let { header("Authorization", "Bearer $it") }
|
||||
}.body()
|
||||
}
|
||||
|
||||
suspend fun updateParticipantStatus(dateId: Int, memberId: Int, status: String): Boolean {
|
||||
return client.put("$baseUrl/api/participants/$dateId/$memberId/status") {
|
||||
setBody(mapOf("status" to status))
|
||||
header("Content-Type", "application/json")
|
||||
token?.let { header("Authorization", "Bearer $it") }
|
||||
}.status.value in 200..299
|
||||
}
|
||||
|
||||
suspend fun getMembers(clubId: Int): List<Member> {
|
||||
return client.get("$baseUrl/api/clubmembers/get/$clubId/true") {
|
||||
token?.let { header("Authorization", "Bearer $it") }
|
||||
}.body()
|
||||
}
|
||||
|
||||
suspend fun updateMember(clubId: Int, member: Member): Boolean {
|
||||
return client.post("$baseUrl/api/clubmembers/set/$clubId") {
|
||||
setBody(member)
|
||||
header("Content-Type", "application/json")
|
||||
token?.let { header("Authorization", "Bearer $it") }
|
||||
}.status.value in 200..299
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package de.tt_tagebuch.shared.api
|
||||
|
||||
import io.socket.client.IO
|
||||
import io.socket.client.Socket
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
import org.json.JSONObject
|
||||
|
||||
class SocketService(private val socketUrl: String) {
|
||||
private var socket: Socket? = null
|
||||
|
||||
private val _events = MutableSharedFlow<Pair<String, JSONObject>>()
|
||||
val events = _events.asSharedFlow()
|
||||
|
||||
fun connect(clubId: Int) {
|
||||
val options = IO.Options().apply {
|
||||
path = "/socket.io/"
|
||||
transports = arrayOf("polling", "websocket")
|
||||
}
|
||||
|
||||
socket = IO.socket(socketUrl, options)
|
||||
|
||||
socket?.on(Socket.EVENT_CONNECT) {
|
||||
println("✅ Connected to Socket.IO")
|
||||
socket?.emit("join-club", clubId)
|
||||
}
|
||||
|
||||
socket?.on("participant:added") { args ->
|
||||
val data = args[0] as JSONObject
|
||||
_events.tryEmit("participant:added" to data)
|
||||
}
|
||||
|
||||
socket?.on("diary:note:added") { args ->
|
||||
val data = args[0] as JSONObject
|
||||
_events.tryEmit("diary:note:added" to data)
|
||||
}
|
||||
|
||||
// Add more events as needed
|
||||
|
||||
socket?.connect()
|
||||
}
|
||||
|
||||
fun disconnect() {
|
||||
socket?.disconnect()
|
||||
socket = null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package de.tt_tagebuch.shared.di
|
||||
|
||||
import de.tt_tagebuch.shared.api.ApiClient
|
||||
import de.tt_tagebuch.shared.api.SocketService
|
||||
import de.tt_tagebuch.shared.repository.AuthRepository
|
||||
import de.tt_tagebuch.shared.repository.DiaryRepository
|
||||
import de.tt_tagebuch.shared.repository.MemberRepository
|
||||
import org.koin.core.context.startKoin
|
||||
import org.koin.dsl.KoinAppDeclaration
|
||||
import org.koin.dsl.module
|
||||
|
||||
import org.koin.core.module.Module
|
||||
import org.koin.dsl.module
|
||||
|
||||
fun initKoin(baseUrl: String, additionalModules: List<Module> = emptyList(), appDeclaration: KoinAppDeclaration = {}) =
|
||||
startKoin {
|
||||
appDeclaration()
|
||||
modules(commonModule(baseUrl) + additionalModules)
|
||||
}
|
||||
|
||||
fun commonModule(baseUrl: String) = module {
|
||||
single { ApiClient(baseUrl) }
|
||||
single { SocketService(baseUrl.replace("https://", "wss://").replace("http://", "ws://")) } // Simplified
|
||||
single { AuthRepository(get()) }
|
||||
single { DiaryRepository(get(), get()) }
|
||||
single { MemberRepository(get()) }
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package de.tt_tagebuch.shared.models
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class User(
|
||||
val id: Int,
|
||||
val email: String,
|
||||
val isActive: Boolean
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class LoginResponse(
|
||||
val success: Boolean,
|
||||
val token: String? = null,
|
||||
val user: User? = null,
|
||||
val error: String? = null
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class Club(
|
||||
val id: Int,
|
||||
val name: String,
|
||||
val greetingText: String? = null
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class DiaryDate(
|
||||
val id: Int,
|
||||
val date: String,
|
||||
val clubId: Int,
|
||||
val trainingStart: String? = null,
|
||||
val trainingEnd: String? = null,
|
||||
val activities: List<DiaryActivity> = emptyList(),
|
||||
val participants: List<Participant> = emptyList()
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class Member(
|
||||
val id: Int,
|
||||
val firstName: String,
|
||||
val lastName: String,
|
||||
val clubId: Int,
|
||||
val active: Boolean = true,
|
||||
val birthDate: String? = null,
|
||||
val gender: String? = null,
|
||||
val ttr: Int? = null,
|
||||
val qttr: Int? = null,
|
||||
val testMembership: Boolean = false,
|
||||
val picsInInternetAllowed: Boolean = false,
|
||||
val memberFormHandedOver: Boolean = false,
|
||||
val adultReleaseApproved: Boolean = false,
|
||||
val adultReserveApproved: Boolean = false,
|
||||
val lastTraining: String? = null,
|
||||
val trainingParticipations: Int = 0,
|
||||
val notInTraining: Boolean = false,
|
||||
val missedTrainingWeeks: Int = 0,
|
||||
val latestImageUrl: String? = null,
|
||||
val contacts: List<MemberContact> = emptyList()
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class MemberContact(
|
||||
val id: Int? = null,
|
||||
val type: String, // "phone" or "email"
|
||||
val value: String,
|
||||
val isParent: Boolean = false,
|
||||
val parentName: String? = null,
|
||||
val isPrimary: Boolean = false
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class Participant(
|
||||
val id: Int,
|
||||
val memberId: Int,
|
||||
val diaryDateId: Int,
|
||||
val attendanceStatus: String = "present", // "present", "excused", "cancelled"
|
||||
val notes: String? = null,
|
||||
val groupId: Int? = null,
|
||||
val member: Member? = null
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class DiaryActivity(
|
||||
val id: Int,
|
||||
val diaryDateId: Int,
|
||||
val description: String
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class DiaryNote(
|
||||
val id: Int,
|
||||
val diaryDateId: Int,
|
||||
val content: String,
|
||||
val userId: Int
|
||||
)
|
||||
@@ -0,0 +1,18 @@
|
||||
package de.tt_tagebuch.shared.repository
|
||||
|
||||
import de.tt_tagebuch.shared.api.ApiClient
|
||||
import de.tt_tagebuch.shared.models.LoginResponse
|
||||
|
||||
class AuthRepository(private val apiClient: ApiClient) {
|
||||
suspend fun login(email: String, password: String): LoginResponse {
|
||||
val response = apiClient.login(email, password)
|
||||
if (response.success && response.token != null) {
|
||||
apiClient.setToken(response.token)
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
fun logout() {
|
||||
apiClient.setToken(null)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package de.tt_tagebuch.shared.repository
|
||||
|
||||
import de.tt_tagebuch.shared.api.ApiClient
|
||||
import de.tt_tagebuch.shared.api.SocketService
|
||||
import de.tt_tagebuch.shared.models.DiaryDate
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import org.json.JSONObject
|
||||
|
||||
class DiaryRepository(
|
||||
private val apiClient: ApiClient,
|
||||
private val socketService: SocketService
|
||||
) {
|
||||
suspend fun getDiaryDates(clubId: Int): List<DiaryDate> {
|
||||
return apiClient.getDiaryDates(clubId)
|
||||
}
|
||||
|
||||
suspend fun getParticipants(dateId: Int): List<de.tt_tagebuch.shared.models.Participant> {
|
||||
return apiClient.getDiaryParticipants(dateId)
|
||||
}
|
||||
|
||||
suspend fun updateParticipantStatus(dateId: Int, memberId: Int, status: String): Boolean {
|
||||
return apiClient.updateParticipantStatus(dateId, memberId, status)
|
||||
}
|
||||
|
||||
suspend fun getMembers(clubId: Int): List<de.tt_tagebuch.shared.models.Member> {
|
||||
return apiClient.getMembers(clubId)
|
||||
}
|
||||
|
||||
fun observeEvents(): Flow<Pair<String, JSONObject>> {
|
||||
return socketService.events
|
||||
}
|
||||
|
||||
fun connectSocket(clubId: Int) {
|
||||
socketService.connect(clubId)
|
||||
}
|
||||
|
||||
fun disconnectSocket() {
|
||||
socketService.disconnect()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package de.tt_tagebuch.shared.repository
|
||||
|
||||
import de.tt_tagebuch.shared.api.ApiClient
|
||||
import de.tt_tagebuch.shared.models.Member
|
||||
import de.tt_tagebuch.shared.models.MemberContact
|
||||
|
||||
class MemberRepository(private val apiClient: ApiClient) {
|
||||
suspend fun getMembers(clubId: Int): List<Member> {
|
||||
return apiClient.getMembers(clubId)
|
||||
}
|
||||
|
||||
suspend fun saveMember(clubId: Int, member: Member): Boolean {
|
||||
return apiClient.updateMember(clubId, member)
|
||||
}
|
||||
|
||||
// Für den Upload von Bildern würden wir in einer realen KMP App
|
||||
// plattformspezifische Implementierungen für Multipart-Form-Data nutzen.
|
||||
// Hier bereiten wir die Schnittstelle vor.
|
||||
suspend fun uploadMemberImage(clubId: Int, memberId: Int, imageByteArray: ByteArray): Boolean {
|
||||
// Implementierung folgt über ApiClient
|
||||
return true
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user