Integration: residence invite accept/decline APIs + wire notification actions
Adds acceptResidenceInvite / declineResidenceInvite to ResidenceApi
(POST /api/residences/{id}/invite/{accept|decline}) and exposes them via
APILayer. On accept success, myResidences is force-refreshed so the
newly-joined residence appears without a manual pull.
Wires NotificationActionReceiver's ACCEPT_INVITE / DECLINE_INVITE
handlers to the new APILayer calls, replacing the log-only TODOs left
behind by P4 Stream O. Notifications are now cleared only on API
success so a failed accept stays actionable.
Tests:
- ResidenceApiInviteTest covers correct HTTP method/path + error surfacing.
- NotificationActionReceiverTest invite cases updated to assert the new
APILayer calls (were previously asserting the log-only path).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -415,6 +415,32 @@ object APILayer {
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Accept a residence invite (push-notification action button).
|
||||
* Refreshes myResidences on success so the new residence appears in the
|
||||
* cache without requiring a manual pull-to-refresh.
|
||||
*/
|
||||
suspend fun acceptResidenceInvite(residenceId: Int): ApiResult<Unit> {
|
||||
val token = getToken() ?: return ApiResult.Error("Not authenticated", 401)
|
||||
val result = residenceApi.acceptResidenceInvite(token, residenceId)
|
||||
if (result is ApiResult.Success) {
|
||||
// Residence list may have changed — force a refresh so any
|
||||
// newly-joined residence shows up in the home screen.
|
||||
getMyResidences(forceRefresh = true)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Decline a residence invite (push-notification action button).
|
||||
* Does not require cache refresh — pending-invites are not cached
|
||||
* client-side; the next app-open will re-query the server anyway.
|
||||
*/
|
||||
suspend fun declineResidenceInvite(residenceId: Int): ApiResult<Unit> {
|
||||
val token = getToken() ?: return ApiResult.Error("Not authenticated", 401)
|
||||
return residenceApi.declineResidenceInvite(token, residenceId)
|
||||
}
|
||||
|
||||
suspend fun getResidence(id: Int, forceRefresh: Boolean = false): ApiResult<ResidenceResponse> {
|
||||
// Check DataManager caches first - return cached if valid and not forcing refresh
|
||||
if (!forceRefresh) {
|
||||
|
||||
@@ -177,6 +177,50 @@ class ResidenceApi(private val client: HttpClient = ApiClient.httpClient) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Accept a residence invite (push-notification action button).
|
||||
*
|
||||
* Backend: `POST /api/residences/{id}/invite/accept`. No body.
|
||||
* Parity: `NotificationCategories.swift` ACCEPT_INVITE on iOS.
|
||||
*/
|
||||
suspend fun acceptResidenceInvite(token: String, residenceId: Int): ApiResult<Unit> {
|
||||
return try {
|
||||
val response = client.post("$baseUrl/residences/$residenceId/invite/accept/") {
|
||||
header("Authorization", "Token $token")
|
||||
}
|
||||
|
||||
if (response.status.isSuccess()) {
|
||||
ApiResult.Success(Unit)
|
||||
} else {
|
||||
val errorMessage = ErrorParser.parseError(response)
|
||||
ApiResult.Error(errorMessage, response.status.value)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
ApiResult.Error(e.message ?: "Unknown error occurred")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decline a residence invite. See [acceptResidenceInvite] for endpoint
|
||||
* pattern. Backend: `POST /api/residences/{id}/invite/decline`.
|
||||
*/
|
||||
suspend fun declineResidenceInvite(token: String, residenceId: Int): ApiResult<Unit> {
|
||||
return try {
|
||||
val response = client.post("$baseUrl/residences/$residenceId/invite/decline/") {
|
||||
header("Authorization", "Token $token")
|
||||
}
|
||||
|
||||
if (response.status.isSuccess()) {
|
||||
ApiResult.Success(Unit)
|
||||
} else {
|
||||
val errorMessage = ErrorParser.parseError(response)
|
||||
ApiResult.Error(errorMessage, response.status.value)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
ApiResult.Error(e.message ?: "Unknown error occurred")
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun joinWithCode(token: String, code: String): ApiResult<JoinResidenceResponse> {
|
||||
return try {
|
||||
val response = client.post("$baseUrl/residences/join-with-code/") {
|
||||
|
||||
Reference in New Issue
Block a user