chore: remove obsolete Android app configuration files
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:
Torsten Schulz (local)
2026-04-21 15:15:21 +02:00
parent c8dedb10cc
commit 41bbf81958
4144 changed files with 4975 additions and 61401 deletions

View 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()
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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()) }
}

View File

@@ -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
)

View File

@@ -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)
}
}

View File

@@ -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()
}
}

View File

@@ -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
}
}