Skip to content

Commit c4030f9

Browse files
authored
Merge pull request #63 from Resolum/feature/household-screens
Feature/household screens
2 parents e5abd89 + cd4928c commit c4030f9

29 files changed

Lines changed: 483 additions & 104 deletions

app/src/main/java/com/resolum/intiva/core/data/local/datastore/TokenDataStore.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ class TokenDataStore @Inject constructor(
5656
dataStore.edit {
5757
it.remove(AUTH_TOKEN)
5858
it.remove(USER_ID)
59-
it.remove(GROUP_ID)
6059
}
6160
}
6261
}

app/src/main/java/com/resolum/intiva/core/navigation/graph/MainShell.kt

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.WindowInsets
44
import androidx.compose.foundation.layout.padding
55
import androidx.compose.material3.Scaffold
66
import androidx.compose.runtime.Composable
7+
import androidx.compose.runtime.collectAsState
78
import androidx.compose.runtime.getValue
89
import androidx.compose.ui.Modifier
910
import androidx.compose.ui.graphics.Color
@@ -119,7 +120,11 @@ fun MainShell(
119120
composable(NavRoutes.FAMILY) {
120121
FamilyScreen(
121122
onInviteClick = { shellNavController.navigate(NavRoutes.INVITE_MEMBER) },
122-
onViewAllActivity = { shellNavController.navigate(NavRoutes.FAMILY_ROLES) }
123+
onViewAllActivity = { shellNavController.navigate(NavRoutes.FAMILY_ROLES) },
124+
onContributeClick = {
125+
shellNavController.currentBackStackEntry?.savedStateHandle?.set("selectFamilyTab", true)
126+
shellNavController.navigate(NavRoutes.SAVINGS_GOALS)
127+
}
123128
)
124129
}
125130

@@ -221,6 +226,15 @@ fun MainShell(
221226
* Savings Goals feature navigation.
222227
*/
223228
composable(NavRoutes.SAVINGS_GOALS) {
229+
val selectFamilyTab = shellNavController
230+
.previousBackStackEntry
231+
?.savedStateHandle
232+
?.getStateFlow("selectFamilyTab", false)
233+
?.collectAsState()
234+
?.value == true
235+
if (selectFamilyTab) {
236+
shellNavController.previousBackStackEntry?.savedStateHandle?.set("selectFamilyTab", false)
237+
}
224238
SavingsGoalsScreen(
225239
onNavigateBack = { shellNavController.popBackStack() },
226240
onNavigateToCreate = { accountId ->
@@ -231,7 +245,8 @@ fun MainShell(
231245
},
232246
onNavigateToEdit = { accountId, goalId ->
233247
shellNavController.navigate("savings_goal_edit/$goalId")
234-
}
248+
},
249+
selectFamilyTab = selectFamilyTab
235250
)
236251
}
237252

app/src/main/java/com/resolum/intiva/core/network/interceptor/LoggingInterceptor.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,17 @@ class LoggingInterceptor(
4242
Log.d("HttpLogger", "URL: ${request.url}")
4343
Log.d("HttpLogger", "METHOD: ${request.method}")
4444

45+
request.headers.let { headers ->
46+
Log.d("HttpLogger", "HEADERS:")
47+
headers.forEach { (name, value) ->
48+
if (name.equals("Authorization", ignoreCase = true)) {
49+
Log.d("HttpLogger", " $name: Bearer ${value.take(20)}...")
50+
} else {
51+
Log.d("HttpLogger", " $name: $value")
52+
}
53+
}
54+
}
55+
4556
request.body?.let { body ->
4657
val buffer = Buffer()
4758
body.writeTo(buffer)

app/src/main/java/com/resolum/intiva/features/household/data/remote/FamilyFacadeService.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,12 @@ import com.resolum.intiva.features.household.data.remote.models.FamilyMemberResp
88
import com.resolum.intiva.features.household.data.remote.models.FamilyResponseDto
99
import com.resolum.intiva.features.household.data.remote.models.InvitationRejectResponseDto
1010
import com.resolum.intiva.features.household.data.remote.models.InvitationResponseDto
11+
import com.resolum.intiva.features.household.data.remote.models.LinkInvitationResponseDto
12+
import com.resolum.intiva.features.household.data.remote.models.PersistDeferredInvitationRequestDto
13+
import com.resolum.intiva.features.household.data.remote.models.PublicInvitationResponseDto
1114
import com.resolum.intiva.features.household.data.remote.models.QrCodeResponseDto
1215
import com.resolum.intiva.features.household.data.remote.models.SendInvitationRequestDto
16+
import com.resolum.intiva.features.household.data.remote.models.SendLinkInvitationRequestDto
1317
import com.resolum.intiva.features.household.data.remote.services.FamilyMemberService
1418
import com.resolum.intiva.features.household.data.remote.services.FamilyService
1519
import com.resolum.intiva.features.household.data.remote.services.InvitationService
@@ -69,4 +73,13 @@ class FamilyFacadeService @Inject constructor(
6973

7074
suspend fun getInvitationQr(familyId: Long): QrCodeResponseDto =
7175
invitationService.getInvitationQr(familyId)
76+
77+
suspend fun sendLinkInvitation(userId: Long, request: SendLinkInvitationRequestDto): LinkInvitationResponseDto =
78+
invitationService.sendLinkInvitation(userId, request)
79+
80+
suspend fun persistDeferredInvitation(request: PersistDeferredInvitationRequestDto) =
81+
invitationService.persistDeferredInvitation(request)
82+
83+
suspend fun getPublicInvitation(token: String): PublicInvitationResponseDto =
84+
invitationService.getPublicInvitation(token)
7285
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.resolum.intiva.features.household.data.remote.models
2+
3+
import com.google.gson.annotations.SerializedName
4+
5+
data class LinkInvitationResponseDto(
6+
@SerializedName("token") val token: String,
7+
@SerializedName("inviteUrl") val inviteUrl: String,
8+
@SerializedName("expiresAt") val expiresAt: String
9+
)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.resolum.intiva.features.household.data.remote.models
2+
3+
import com.google.gson.annotations.SerializedName
4+
5+
data class PersistDeferredInvitationRequestDto(
6+
@SerializedName("installId") val installId: String,
7+
@SerializedName("token") val token: String
8+
)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.resolum.intiva.features.household.data.remote.models
2+
3+
import com.google.gson.annotations.SerializedName
4+
5+
data class PublicInvitationResponseDto(
6+
@SerializedName("invitationId") val id: Long? = null,
7+
@SerializedName("token") val token: String? = null,
8+
@SerializedName("status") val status: String? = null,
9+
@SerializedName("familyId") val familyId: Long? = null,
10+
@SerializedName("invitedByName") val invitedByName: String? = null,
11+
@SerializedName("groupName") val groupName: String? = null,
12+
@SerializedName("isExpired") val isExpired: Boolean? = null
13+
)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.resolum.intiva.features.household.data.remote.models
2+
3+
import com.google.gson.annotations.SerializedName
4+
5+
data class SendLinkInvitationRequestDto(
6+
@SerializedName("familyId") val familyId: Long,
7+
@SerializedName("inviteeEmail") val inviteeEmail: String
8+
)

app/src/main/java/com/resolum/intiva/features/household/data/remote/services/InvitationService.kt

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@ import com.resolum.intiva.features.household.data.remote.models.CreateQrInvitati
44
import com.resolum.intiva.features.household.data.remote.models.DeferredInvitationResponseDto
55
import com.resolum.intiva.features.household.data.remote.models.InvitationRejectResponseDto
66
import com.resolum.intiva.features.household.data.remote.models.InvitationResponseDto
7+
import com.resolum.intiva.features.household.data.remote.models.LinkInvitationResponseDto
8+
import com.resolum.intiva.features.household.data.remote.models.PersistDeferredInvitationRequestDto
9+
import com.resolum.intiva.features.household.data.remote.models.PublicInvitationResponseDto
710
import com.resolum.intiva.features.household.data.remote.models.QrCodeResponseDto
811
import com.resolum.intiva.features.household.data.remote.models.SendInvitationRequestDto
12+
import com.resolum.intiva.features.household.data.remote.models.SendLinkInvitationRequestDto
913
import retrofit2.http.Body
1014
import retrofit2.http.GET
1115
import retrofit2.http.PATCH
@@ -56,12 +60,12 @@ interface InvitationService {
5660
@Path("invitationId") invitationId: Long
5761
): InvitationResponseDto
5862

59-
@PATCH("api/v1/invitations/{token}/accept")
63+
@PATCH("invitations/{token}/accept")
6064
suspend fun acceptInvitationByToken(
6165
@Path("token") token: String
6266
): InvitationResponseDto
6367

64-
@PATCH("api/v1/invitations/{token}/reject")
68+
@PATCH("invitations/{token}/reject")
6569
suspend fun rejectInvitationByToken(
6670
@Path("token") token: String
6771
): InvitationRejectResponseDto
@@ -75,4 +79,20 @@ interface InvitationService {
7579
suspend fun getInvitationQr(
7680
@Path("familyId") familyId: Long
7781
): QrCodeResponseDto
82+
83+
@POST("users/{userId}/invitations/link")
84+
suspend fun sendLinkInvitation(
85+
@Path("userId") userId: Long,
86+
@Body body: SendLinkInvitationRequestDto
87+
): LinkInvitationResponseDto
88+
89+
@POST("invitations/deferred")
90+
suspend fun persistDeferredInvitation(
91+
@Body body: PersistDeferredInvitationRequestDto
92+
)
93+
94+
@GET("invitations/public/{token}")
95+
suspend fun getPublicInvitation(
96+
@Path("token") token: String
97+
): PublicInvitationResponseDto
7898
}

app/src/main/java/com/resolum/intiva/features/household/data/repositories/InvitationRepositoryImpl.kt

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@ import com.resolum.intiva.core.deeplink.DeepLinkData
55
import com.resolum.intiva.core.network.model.NetworkResult
66
import com.resolum.intiva.features.household.data.remote.FamilyFacadeService
77
import com.resolum.intiva.features.household.data.remote.mappers.toDomain
8+
import com.resolum.intiva.features.household.data.remote.models.PersistDeferredInvitationRequestDto
89
import com.resolum.intiva.features.household.data.remote.models.SendInvitationRequestDto
10+
import com.resolum.intiva.features.household.data.remote.models.SendLinkInvitationRequestDto
911
import com.resolum.intiva.features.household.domain.models.Invitation
12+
import com.resolum.intiva.features.household.domain.models.InvitationDetail
1013
import com.resolum.intiva.features.household.domain.models.QrCodeResult
1114
import com.resolum.intiva.features.household.domain.repositories.InvitationRepository
1215
import com.resolum.intiva.features.iam.domain.repositories.SessionRepository
@@ -52,12 +55,19 @@ class InvitationRepositoryImpl @Inject constructor(
5255
}
5356

5457
override suspend fun acceptInvitationByToken(token: String): NetworkResult<Unit> = safeCall {
55-
familyFacadeService.acceptInvitationByToken(token)
58+
val userId = sessionRepository.getUserId() ?: throw IllegalStateException("User ID not found in session")
59+
val dto = familyFacadeService.getPublicInvitation(token)
60+
val invitationId = dto.id ?: throw IllegalStateException("Invitation ID not found in public response")
61+
familyFacadeService.acceptInvitation(userId, invitationId)
5662
Unit
5763
}
5864

5965
override suspend fun rejectInvitationByToken(token: String): NetworkResult<String> = safeCall {
60-
familyFacadeService.rejectInvitationByToken(token).invitedByName
66+
val userId = sessionRepository.getUserId() ?: throw IllegalStateException("User ID not found in session")
67+
val dto = familyFacadeService.getPublicInvitation(token)
68+
val invitationId = dto.id ?: throw IllegalStateException("Invitation ID not found in public response")
69+
familyFacadeService.rejectInvitation(userId, invitationId)
70+
"Invitación rechazada"
6171
}
6272

6373
override suspend fun getInvitationQr(familyId: Long): NetworkResult<QrCodeResult> = safeCall {
@@ -69,4 +79,32 @@ class InvitationRepositoryImpl @Inject constructor(
6979
expiresAt = dto.expiresAt
7080
)
7181
}
82+
83+
override suspend fun sendInvitationLink(familyId: Long, inviteeEmail: String?): NetworkResult<String> = safeCall {
84+
val userId = sessionRepository.getUserId() ?: throw IllegalStateException("User ID not found in session")
85+
val email = inviteeEmail ?: "link-${familyId}-${System.currentTimeMillis()}@invite.intiva"
86+
val response = familyFacadeService.sendLinkInvitation(
87+
userId,
88+
SendLinkInvitationRequestDto(familyId = familyId, inviteeEmail = email)
89+
)
90+
response.inviteUrl
91+
}
92+
93+
override suspend fun persistDeferredInvitation(installId: String, token: String): NetworkResult<Unit> = safeCall {
94+
familyFacadeService.persistDeferredInvitation(
95+
PersistDeferredInvitationRequestDto(installId = installId, token = token)
96+
)
97+
}
98+
99+
override suspend fun getPublicInvitation(token: String): NetworkResult<InvitationDetail> = safeCall {
100+
val dto = familyFacadeService.getPublicInvitation(token)
101+
InvitationDetail(
102+
id = dto.id ?: 0L,
103+
token = dto.token ?: token,
104+
status = dto.status ?: "PENDING",
105+
invitedByName = dto.invitedByName ?: "Usuario",
106+
familyId = dto.familyId ?: 0L,
107+
isExpired = dto.isExpired ?: false
108+
)
109+
}
72110
}

0 commit comments

Comments
 (0)