diff --git a/app/src/main/java/com/p_vacho/neat_calendar/activities/NotificationsActivity.kt b/app/src/main/java/com/p_vacho/neat_calendar/activities/NotificationsActivity.kt index 8f1e6e6..6baa624 100644 --- a/app/src/main/java/com/p_vacho/neat_calendar/activities/NotificationsActivity.kt +++ b/app/src/main/java/com/p_vacho/neat_calendar/activities/NotificationsActivity.kt @@ -25,6 +25,8 @@ import org.json.JSONException import org.json.JSONObject import retrofit2.HttpException +// TODO: Localize all strings + class NotificationsActivity : AppCompatActivity() { private lateinit var rvNotifications: RecyclerView private lateinit var btnBack: ImageButton @@ -90,7 +92,11 @@ class NotificationsActivity : AppCompatActivity() { } private fun getInvitationData(invitationId: String, rvPosition: Int): InvitationResponse? { - return invitations[invitationId] + val ret = invitations[invitationId] + if (ret == null) { + Log.w("NotificationsActivity", "NotificationAdapter requested unknown invitation: $invitationId") + } + return ret } private fun getUserData(userId: String, rvPosition: Int?): UserResponse? { @@ -139,25 +145,39 @@ class NotificationsActivity : AppCompatActivity() { Toast.makeText(this@NotificationsActivity, "Invitation accepted", Toast.LENGTH_SHORT).show() // Also mark the notification as read after the interaction - if (!notification.read) handleNotificationClick(notification, position, false) - } + if (!notification.read) { + handleNotificationClick(notification, position, false) + } else { + // If the notification was unread, handleNotificationClick will have triggered this + // but otherwise, we'll need to trigger an item update ourselves, to make sure that + // the accept/decline buttons are removed + rvNotifications.adapter!!.notifyItemChanged(position) + } } } } NotificationAdapter.Action.DECLINE -> { lifecycleScope.launch(Dispatchers.IO) { val invitationId = notification.data - RetrofitClient.invitationService.declineInvitation(invitationId) + invitations[invitationId] = RetrofitClient.invitationService.declineInvitation(invitationId) withContext(Dispatchers.Main) { Toast.makeText(this@NotificationsActivity, "Invitation declined", Toast.LENGTH_SHORT).show() // Also mark the notification as read after the interaction - if (!notification.read) handleNotificationClick(notification, position, false) + if (!notification.read) { + handleNotificationClick(notification, position, false) + } else { + // If the notification was unread, handleNotificationClick will have triggered this + // but otherwise, we'll need to trigger an item update ourselves, to make sure that + // the accept/decline buttons are removed + rvNotifications.adapter!!.notifyItemChanged(position) + } } } } NotificationAdapter.Action.VIEW_EVENT -> { // TODO: Handle viewing the event + if (!notification.read) handleNotificationClick(notification, position, false) } } } diff --git a/app/src/main/java/com/p_vacho/neat_calendar/adapters/NotificationAdapter.kt b/app/src/main/java/com/p_vacho/neat_calendar/adapters/NotificationAdapter.kt index b37046f..26ed47a 100644 --- a/app/src/main/java/com/p_vacho/neat_calendar/adapters/NotificationAdapter.kt +++ b/app/src/main/java/com/p_vacho/neat_calendar/adapters/NotificationAdapter.kt @@ -14,6 +14,8 @@ import java.time.Duration import java.time.OffsetDateTime import java.time.format.DateTimeFormatter +// TODO: Localize all strings + class NotificationAdapter( private val notifications: MutableList, private val onActionClick: (NotificationResponse, Action, Int) -> Unit, @@ -88,33 +90,65 @@ class NotificationAdapter( val user = getUserData(usernameId, position) val username = user?.username ?: "Unknown User" + val statusSuffix = when (invitation.status) { + "accepted" -> " [already accepted]" + "declined" -> " [already declined]" + "pending" -> "" + else -> throw IllegalStateException("Unexpected invitation status: ${invitation.status} for invitation ID: ${invitation.id}") + } + return when (notification.message) { - "new-invitation" -> "You have received an event invitation from @$username" - "invitation-accepted" -> "@$username has accepted your event invitation" - "invitation-declined" -> "@$username has declined your event invitation" + "new-invitation" -> "You have received an event invitation from @$username$statusSuffix" + "invitation-accepted" -> "@$username has accepted your event invitation$statusSuffix" + "invitation-declined" -> "@$username has declined your event invitation$statusSuffix" else -> throw IllegalArgumentException("Unexpected notification message: ${notification.message}") } } + private fun handleInvitationActions( holder: NotificationViewHolder, notification: NotificationResponse, position: Int ) { if (notification.event_type == "invitation") { + val invitation = getInvitationData(notification.data, position) + if (invitation == null) { + // No buttons for deleted invitations + holder.invitationActions.visibility = View.GONE + + return + } + when (notification.message) { "new-invitation" -> { - // Show Accept/Decline & View buttons - holder.invitationActions.visibility = View.VISIBLE - holder.acceptButton.visibility = View.VISIBLE - holder.declineButton.visibility = View.VISIBLE - holder.viewEventButton.visibility = View.VISIBLE + when (invitation.status) { + "pending" -> { + // Show Accept/Decline & View buttons + holder.invitationActions.visibility = View.VISIBLE + holder.acceptButton.visibility = View.VISIBLE + holder.declineButton.visibility = View.VISIBLE + holder.viewEventButton.visibility = View.VISIBLE - holder.acceptButton.setOnClickListener { - onActionClick(notification, Action.ACCEPT, position) - } - holder.declineButton.setOnClickListener { - onActionClick(notification, Action.DECLINE, position) + holder.acceptButton.setOnClickListener { + onActionClick(notification, Action.ACCEPT, position) + } + holder.declineButton.setOnClickListener { + onActionClick(notification, Action.DECLINE, position) + } + } + "accepted", "declined" -> { + // Show only View button + holder.invitationActions.visibility = View.VISIBLE + holder.acceptButton.visibility = View.GONE + holder.declineButton.visibility = View.GONE + holder.viewEventButton.visibility = View.VISIBLE + + holder.viewEventButton.setOnClickListener { + onActionClick(notification, Action.VIEW_EVENT, position) + } + } + else -> throw IllegalStateException("Unexpected invitation status: ${invitation.status} for invite ID: ${invitation.id}") } } "invitation-accepted", "invitation-declined" -> { diff --git a/app/src/main/java/com/p_vacho/neat_calendar/api/services/InvitationsService.kt b/app/src/main/java/com/p_vacho/neat_calendar/api/services/InvitationsService.kt index 03c6f5f..951657d 100644 --- a/app/src/main/java/com/p_vacho/neat_calendar/api/services/InvitationsService.kt +++ b/app/src/main/java/com/p_vacho/neat_calendar/api/services/InvitationsService.kt @@ -21,9 +21,9 @@ interface InvitationsService { suspend fun getIncomingInvitations(@Path("user_id") userId: String): List @POST("invitations/{invitation_id}/accept") - suspend fun acceptInvitation(@Path("invitation_id") invitationId: String): Unit + suspend fun acceptInvitation(@Path("invitation_id") invitationId: String): InvitationResponse @POST("invitations/{invitation_id}/decline") - suspend fun declineInvitation(@Path("invitation_id") invitationId: String): Unit + suspend fun declineInvitation(@Path("invitation_id") invitationId: String): InvitationResponse } \ No newline at end of file