feat: Add edit button for existing categories

This commit is contained in:
Peter Vacho 2025-01-05 16:40:28 +01:00
parent c900c7093f
commit df18f13972
Signed by: school
GPG key ID: 8CFC3837052871B4
5 changed files with 91 additions and 27 deletions

View file

@ -18,7 +18,6 @@ import com.p_vacho.neat_calendar.R
import com.p_vacho.neat_calendar.adapters.CategoryAdapter import com.p_vacho.neat_calendar.adapters.CategoryAdapter
import com.p_vacho.neat_calendar.api.RetrofitClient import com.p_vacho.neat_calendar.api.RetrofitClient
import com.p_vacho.neat_calendar.api.models.CategoryResponse import com.p_vacho.neat_calendar.api.models.CategoryResponse
import com.p_vacho.neat_calendar.api.models.EventResponse
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -38,7 +37,12 @@ class CategoriesActivity : AppCompatActivity() {
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
val newCategory: CategoryResponse? = result.data?.getParcelableExtra("newCategory") val newCategory: CategoryResponse? = result.data?.getParcelableExtra("newCategory")
newCategory?.let { categoryCreateReply(it) } @Suppress("DEPRECATION")
val editedCategory: CategoryResponse? = result.data?.getParcelableExtra("editedCategory")
if (newCategory != null && editedCategory != null) throw IllegalStateException("Got both edit & new response")
if (newCategory != null) categoryCreateReply(newCategory, CategoryMode.CREATE)
if (editedCategory != null) categoryCreateReply(editedCategory, CategoryMode.EDIT)
} }
} }
@ -59,14 +63,14 @@ class CategoriesActivity : AppCompatActivity() {
tvEmptyState = findViewById(R.id.tvEmptyState) tvEmptyState = findViewById(R.id.tvEmptyState)
btnBack.setOnClickListener { finish() } btnBack.setOnClickListener { finish() }
btnAddCategory.setOnClickListener { navigateToCreateCategory() } btnAddCategory.setOnClickListener { handleCreateCategory() }
rvCategories.layoutManager = LinearLayoutManager(this) rvCategories.layoutManager = LinearLayoutManager(this)
lifecycleScope.launch { lifecycleScope.launch {
categories = fetchCategories().toMutableList() categories = fetchCategories().toMutableList()
val adapter = CategoryAdapter(categories, ::handleDeleteCategory) val adapter = CategoryAdapter(categories, ::handleDeleteCategory, ::handleEditCategory)
rvCategories.adapter = adapter rvCategories.adapter = adapter
updateEmptyState() updateEmptyState()
@ -105,20 +109,44 @@ class CategoriesActivity : AppCompatActivity() {
} }
/** /**
* Navigates to the activity for adding a new category. * Handle create category button getting pressed.
*
* Navigates to the create category activity in create mode.
*/ */
private fun navigateToCreateCategory() { private fun handleCreateCategory() {
val intent = Intent(this, CreateCategoryActivity::class.java) val intent = Intent(this, CreateCategoryActivity::class.java).apply {
startActivity(intent) putExtra("mode", CategoryMode.CREATE.name)
}
createActivityLauncher.launch(intent)
}
/**
* Handle edit category button being pressed.
*
* Navigates to the create category activity in edit mode.
*/
private fun handleEditCategory(category: CategoryResponse, position: Int) {
val intent = Intent(this, CreateCategoryActivity::class.java).apply {
putExtra("mode", CategoryMode.EDIT.name)
putExtra("category", category)
}
createActivityLauncher.launch(intent)
} }
/** /**
* Used as a callback, triggered when the CreateCategory Activity returns a result. * Used as a callback, triggered when the CreateCategory Activity returns a result.
* *
* The returned value (the new category data) is passed over as a parameter. * The returned value (the new / edited category data) is passed over as a parameter.
*/ */
private fun categoryCreateReply(category: CategoryResponse) { private fun categoryCreateReply(category: CategoryResponse, mode: CategoryMode) {
(rvCategories.adapter as CategoryAdapter).addCategory(category) val adapter = (rvCategories.adapter as CategoryAdapter)
when (mode) {
CategoryMode.CREATE -> {
adapter.addCategory(category)
updateEmptyState()
}
CategoryMode.EDIT -> { adapter.editCategory(category) }
}
} }
/** /**

View file

@ -7,6 +7,10 @@ import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import com.p_vacho.neat_calendar.R import com.p_vacho.neat_calendar.R
enum class CategoryMode {
CREATE, EDIT
}
class CreateCategoryActivity : AppCompatActivity() { class CreateCategoryActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)

View file

@ -11,12 +11,14 @@ import com.p_vacho.neat_calendar.api.models.CategoryResponse
class CategoryAdapter( class CategoryAdapter(
private val categories: MutableList<CategoryResponse>, private val categories: MutableList<CategoryResponse>,
private val onDeleteClick: (CategoryResponse, Int) -> Unit private val onDeleteClick: (CategoryResponse, Int) -> Unit,
private val onEditClick: (CategoryResponse, Int) -> Unit
) : RecyclerView.Adapter<CategoryAdapter.CategoryViewHolder>() { ) : RecyclerView.Adapter<CategoryAdapter.CategoryViewHolder>() {
inner class CategoryViewHolder(view: View) : RecyclerView.ViewHolder(view) { inner class CategoryViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val colorIndicator: View = view.findViewById(R.id.colorIndicator) val colorIndicator: View = view.findViewById(R.id.colorIndicator)
val categoryName: TextView = view.findViewById(R.id.tvCategoryName) val categoryName: TextView = view.findViewById(R.id.tvCategoryName)
val editButton: ImageButton = view.findViewById(R.id.btnEditCategory)
val deleteButton: ImageButton = view.findViewById(R.id.btnDeleteCategory) val deleteButton: ImageButton = view.findViewById(R.id.btnDeleteCategory)
} }
@ -33,10 +35,9 @@ class CategoryAdapter(
holder.colorIndicator.setBackgroundColor(category.color.toArgb()) holder.colorIndicator.setBackgroundColor(category.color.toArgb())
holder.categoryName.text = category.name holder.categoryName.text = category.name
// Set click listener for the delete button // Set click listeners for the buttons
holder.deleteButton.setOnClickListener { holder.deleteButton.setOnClickListener { onDeleteClick(category, position) }
onDeleteClick(category, position) holder.editButton.setOnClickListener { onEditClick(category, position) }
}
} }
override fun getItemCount(): Int = categories.size override fun getItemCount(): Int = categories.size
@ -64,4 +65,15 @@ class CategoryAdapter(
categories.add(category) categories.add(category)
notifyItemInserted(categories.size) notifyItemInserted(categories.size)
} }
/**
* Edit an existing category, updating it in the UI.
*
* Call this after the onEditClick callback edits the category from the backend API.
*/
fun editCategory(category: CategoryResponse) {
val position = categories.indexOfFirst { it.id == category.id }
categories[position] = category
notifyItemChanged(position)
}
} }

View file

@ -40,7 +40,28 @@
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/colorIndicator" app:layout_constraintStart_toEndOf="@id/colorIndicator"
app:layout_constraintEnd_toStartOf="@id/btnDeleteCategory" /> app:layout_constraintEnd_toStartOf="@id/buttonContainer" />
<!-- Button Container -->
<LinearLayout
android:id="@+id/buttonContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<!-- Edit Button -->
<ImageButton
android:id="@+id/btnEditCategory"
android:layout_width="40dp"
android:layout_height="40dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_pencil"
android:contentDescription="@string/edit_category"
app:tint="?android:attr/textColorSecondary" />
<!-- Delete Button --> <!-- Delete Button -->
<ImageButton <ImageButton
@ -50,10 +71,8 @@
android:background="?attr/selectableItemBackgroundBorderless" android:background="?attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_trashbin" android:src="@drawable/ic_trashbin"
android:contentDescription="@string/delete_category" android:contentDescription="@string/delete_category"
app:tint="?android:attr/textColorSecondary" app:tint="?android:attr/textColorSecondary" />
app:layout_constraintTop_toTopOf="parent" </LinearLayout>
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>

View file

@ -96,4 +96,5 @@
<string name="no_categories_placeholder">This event has no categories.</string> <string name="no_categories_placeholder">This event has no categories.</string>
<string name="event_owner_section">Event owner:</string> <string name="event_owner_section">Event owner:</string>
<string name="no_invited_categories_placeholder">You can\'t see categories for invited events</string> <string name="no_invited_categories_placeholder">You can\'t see categories for invited events</string>
<string name="edit_category">Edit category</string>
</resources> </resources>