Compare commits
4 commits
d302927de0
...
01f1b003c9
Author | SHA1 | Date | |
---|---|---|---|
Peter Vacho | 01f1b003c9 | ||
Peter Vacho | 307afd1f0c | ||
Peter Vacho | 9aa6b5217d | ||
Peter Vacho | d741655292 |
|
@ -2,13 +2,16 @@ package com.p_vacho.neat_calendar.activities
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import android.view.View
|
||||||
import android.widget.ImageButton
|
import android.widget.ImageButton
|
||||||
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.recyclerview.widget.ItemTouchHelper
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.p_vacho.neat_calendar.MyApplication
|
import com.p_vacho.neat_calendar.MyApplication
|
||||||
|
@ -32,6 +35,8 @@ import retrofit2.HttpException
|
||||||
class NotificationsActivity : AppCompatActivity() {
|
class NotificationsActivity : AppCompatActivity() {
|
||||||
private lateinit var rvNotifications: RecyclerView
|
private lateinit var rvNotifications: RecyclerView
|
||||||
private lateinit var btnBack: ImageButton
|
private lateinit var btnBack: ImageButton
|
||||||
|
private lateinit var tvEmptyState: TextView
|
||||||
|
private lateinit var tvSwipeHint: TextView
|
||||||
|
|
||||||
private lateinit var notifications: MutableList<NotificationResponse>
|
private lateinit var notifications: MutableList<NotificationResponse>
|
||||||
private lateinit var invitations: MutableMap<String, InvitationResponse> // invitation id -> invitation
|
private lateinit var invitations: MutableMap<String, InvitationResponse> // invitation id -> invitation
|
||||||
|
@ -51,6 +56,8 @@ class NotificationsActivity : AppCompatActivity() {
|
||||||
|
|
||||||
rvNotifications = findViewById(R.id.rvNotifications)
|
rvNotifications = findViewById(R.id.rvNotifications)
|
||||||
btnBack = findViewById(R.id.btnBack)
|
btnBack = findViewById(R.id.btnBack)
|
||||||
|
tvEmptyState = findViewById(R.id.tvEmptyState)
|
||||||
|
tvSwipeHint = findViewById(R.id.tvSwipeHint)
|
||||||
|
|
||||||
btnBack.setOnClickListener { finish() }
|
btnBack.setOnClickListener { finish() }
|
||||||
|
|
||||||
|
@ -66,7 +73,7 @@ class NotificationsActivity : AppCompatActivity() {
|
||||||
invitations = invitationsDeferred.await().toMutableMap()
|
invitations = invitationsDeferred.await().toMutableMap()
|
||||||
events = eventsDeferred.await().toMutableMap()
|
events = eventsDeferred.await().toMutableMap()
|
||||||
|
|
||||||
rvNotifications.adapter = NotificationAdapter(
|
val adapter = NotificationAdapter(
|
||||||
notifications,
|
notifications,
|
||||||
::handleNotificationAction,
|
::handleNotificationAction,
|
||||||
::handleNotificationClick,
|
::handleNotificationClick,
|
||||||
|
@ -74,6 +81,10 @@ class NotificationsActivity : AppCompatActivity() {
|
||||||
::getUserData,
|
::getUserData,
|
||||||
::getEventData,
|
::getEventData,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
rvNotifications.adapter = adapter
|
||||||
|
setupSwipeToDelete(adapter)
|
||||||
|
updateEmptyState()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,7 +150,7 @@ class NotificationsActivity : AppCompatActivity() {
|
||||||
val ret = invitations[invitationId]
|
val ret = invitations[invitationId]
|
||||||
if (ret == null) {
|
if (ret == null) {
|
||||||
Log.w("NotificationsActivity", "NotificationAdapter requested unknown invitation: $invitationId")
|
Log.w("NotificationsActivity", "NotificationAdapter requested unknown invitation: $invitationId")
|
||||||
Log.w("NotificationsActivity", "Known invitations: $invitations")
|
Log.w("NotificationsActivity", "Known invitations (${invitations.size}): $invitations")
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
@ -148,7 +159,7 @@ class NotificationsActivity : AppCompatActivity() {
|
||||||
val ret = events[eventId]
|
val ret = events[eventId]
|
||||||
if (ret == null) {
|
if (ret == null) {
|
||||||
Log.w("NotificationsActivity", "NotificationAdapter requested unknown event: $eventId")
|
Log.w("NotificationsActivity", "NotificationAdapter requested unknown event: $eventId")
|
||||||
Log.w("NotificationsActivity", "Known events: $events")
|
Log.w("NotificationsActivity", "Known events (${events.size}): $events")
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
@ -250,4 +261,49 @@ class NotificationsActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun setupSwipeToDelete(adapter: NotificationAdapter) {
|
||||||
|
val itemTouchHelper = ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.RIGHT) {
|
||||||
|
override fun onMove(
|
||||||
|
recyclerView: RecyclerView,
|
||||||
|
viewHolder: RecyclerView.ViewHolder,
|
||||||
|
target: RecyclerView.ViewHolder
|
||||||
|
): Boolean {
|
||||||
|
return false // We don't support moving items
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
||||||
|
val position = viewHolder.adapterPosition
|
||||||
|
val notification = notifications[position]
|
||||||
|
|
||||||
|
// Call the deletion method
|
||||||
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
RetrofitClient.notificationsService.deleteNotification(notification.id)
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
// Remove the notification & notify the adapter about it
|
||||||
|
notifications.removeAt(position)
|
||||||
|
adapter.notifyItemRemoved(position)
|
||||||
|
|
||||||
|
// Annoyingly, we can't just use notifyItemRemoved for the single removed item,
|
||||||
|
// as all the items below it would now be using the wrong position that was
|
||||||
|
// already bounded to the callbacks from the click listeners, so we need to refresh
|
||||||
|
// all of the notifications below this one as well.
|
||||||
|
adapter.notifyItemRangeChanged(position, notifications.size - position)
|
||||||
|
|
||||||
|
Toast.makeText(this@NotificationsActivity, "Notification deleted", Toast.LENGTH_SHORT).show()
|
||||||
|
updateEmptyState()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Attach ItemTouchHelper to the RecyclerView
|
||||||
|
itemTouchHelper.attachToRecyclerView(rvNotifications)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateEmptyState() {
|
||||||
|
val isEmpty = notifications.isEmpty()
|
||||||
|
tvEmptyState.visibility = if (isEmpty) View.VISIBLE else View.GONE
|
||||||
|
tvSwipeHint.visibility = if (isEmpty) View.GONE else View.VISIBLE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ interface EventsService {
|
||||||
@Query("start_to") startTo: OffsetDateTime? = null,
|
@Query("start_to") startTo: OffsetDateTime? = null,
|
||||||
@Query("end_from") endFrom: OffsetDateTime? = null,
|
@Query("end_from") endFrom: OffsetDateTime? = null,
|
||||||
@Query("end_to") endTo: OffsetDateTime? = null,
|
@Query("end_to") endTo: OffsetDateTime? = null,
|
||||||
@Query("inviteStatus") inviteStatus: String? = null, // "pending" / "accepted" / null (both)
|
@Query("invite_status") inviteStatus: String? = null, // "pending" / "accepted" / null (both)
|
||||||
): List<EventResponse>
|
): List<EventResponse>
|
||||||
|
|
||||||
@DELETE("events/{event_id}")
|
@DELETE("events/{event_id}")
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package com.p_vacho.neat_calendar.api.services
|
package com.p_vacho.neat_calendar.api.services
|
||||||
|
|
||||||
import com.p_vacho.neat_calendar.api.models.NotificationResponse
|
import com.p_vacho.neat_calendar.api.models.NotificationResponse
|
||||||
|
import retrofit2.http.DELETE
|
||||||
import retrofit2.http.GET
|
import retrofit2.http.GET
|
||||||
import retrofit2.http.POST
|
import retrofit2.http.POST
|
||||||
import retrofit2.http.Path
|
import retrofit2.http.Path
|
||||||
|
@ -14,4 +15,10 @@ interface NotificationService {
|
||||||
|
|
||||||
@POST("notifications/{notification_id}/read")
|
@POST("notifications/{notification_id}/read")
|
||||||
suspend fun markNotificationRead(@Path("notification_id") notificationId: String): NotificationResponse
|
suspend fun markNotificationRead(@Path("notification_id") notificationId: String): NotificationResponse
|
||||||
|
|
||||||
|
@POST("notifications/{notification_id}/unread")
|
||||||
|
suspend fun markNotificationUnread(@Path("notification_id") notificationId: String): NotificationResponse
|
||||||
|
|
||||||
|
@DELETE("notifications/{notification_id}")
|
||||||
|
suspend fun deleteNotification(@Path("notification_id") notificationId: String): Unit
|
||||||
}
|
}
|
|
@ -62,4 +62,36 @@
|
||||||
android:layout_marginBottom="16dp"
|
android:layout_marginBottom="16dp"
|
||||||
android:layout_marginTop="8dp" />
|
android:layout_marginTop="8dp" />
|
||||||
|
|
||||||
|
<!-- Swipe Hint -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvSwipeHint"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/swipe_to_delete_hint"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
android:gravity="center"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/rvNotifications"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
|
<!-- Empty State -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvEmptyState"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/no_notifications"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
android:gravity="center"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/titleBar"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
|
@ -79,4 +79,6 @@
|
||||||
<string name="decline">Decline</string>
|
<string name="decline">Decline</string>
|
||||||
<string name="notifications">Notifications</string>
|
<string name="notifications">Notifications</string>
|
||||||
<string name="view_event">View Event</string>
|
<string name="view_event">View Event</string>
|
||||||
|
<string name="swipe_to_delete_hint">Swipe right on a notification to delete it.</string>
|
||||||
|
<string name="no_notifications">You\'re all caught up! No notifications right now.</string>
|
||||||
</resources>
|
</resources>
|
Loading…
Reference in a new issue