rewrite threads with Kotlin & coroutines (#2617)

* initial class setup

* handle events and filters

* handle status state changes

* code formatting

* fix status filtering

* cleanup code a bit

* implement removeAllByAccountId

* move toolbar into fragment, implement menu

* error and load state handling

* fix pull to refresh

* implement reveal button

* use requireContext() instead of context!!

* jump to detailed status

* add ViewThreadViewModelTest

* fix ktlint

* small code improvements (thx charlag)

* add testcase for toggleRevealButton

* add more state change testcases to ViewThreadViewModel
This commit is contained in:
Konrad Pozniak 2022-08-15 11:00:18 +02:00 committed by GitHub
commit 741461acde
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 1446 additions and 999 deletions

View file

@ -21,12 +21,12 @@ import com.keylesspalace.tusky.viewdata.StatusViewData;
import java.text.DateFormat;
import java.util.Date;
class StatusDetailedViewHolder extends StatusBaseViewHolder {
private TextView reblogs;
private TextView favourites;
private View infoDivider;
public class StatusDetailedViewHolder extends StatusBaseViewHolder {
private final TextView reblogs;
private final TextView favourites;
private final View infoDivider;
StatusDetailedViewHolder(View view) {
public StatusDetailedViewHolder(View view) {
super(view);
reblogs = view.findViewById(R.id.status_reblogs);
favourites = view.findViewById(R.id.status_favourites);

View file

@ -1,129 +0,0 @@
/* Copyright 2021 Tusky Contributors
*
* This file is a part of Tusky.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Tusky; if not,
* see <http://www.gnu.org/licenses>. */
package com.keylesspalace.tusky.adapter
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.interfaces.StatusActionListener
import com.keylesspalace.tusky.util.StatusDisplayOptions
import com.keylesspalace.tusky.viewdata.StatusViewData
class ThreadAdapter(
private val statusDisplayOptions: StatusDisplayOptions,
private val statusActionListener: StatusActionListener
) : RecyclerView.Adapter<StatusBaseViewHolder>() {
private val statuses = mutableListOf<StatusViewData.Concrete>()
var detailedStatusPosition: Int = RecyclerView.NO_POSITION
private set
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StatusBaseViewHolder {
return when (viewType) {
VIEW_TYPE_STATUS -> {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_status, parent, false)
StatusViewHolder(view)
}
VIEW_TYPE_STATUS_DETAILED -> {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_status_detailed, parent, false)
StatusDetailedViewHolder(view)
}
else -> error("Unknown item type: $viewType")
}
}
override fun onBindViewHolder(viewHolder: StatusBaseViewHolder, position: Int) {
val status = statuses[position]
viewHolder.setupWithStatus(status, statusActionListener, statusDisplayOptions)
}
override fun getItemViewType(position: Int): Int {
return if (position == detailedStatusPosition) {
VIEW_TYPE_STATUS_DETAILED
} else {
VIEW_TYPE_STATUS
}
}
override fun getItemCount(): Int = statuses.size
fun setStatuses(statuses: List<StatusViewData.Concrete>?) {
this.statuses.clear()
this.statuses.addAll(statuses!!)
notifyDataSetChanged()
}
fun addItem(position: Int, statusViewData: StatusViewData.Concrete) {
statuses.add(position, statusViewData)
notifyItemInserted(position)
}
fun clearItems() {
val oldSize = statuses.size
statuses.clear()
detailedStatusPosition = RecyclerView.NO_POSITION
notifyItemRangeRemoved(0, oldSize)
}
fun addAll(position: Int, statuses: List<StatusViewData.Concrete>) {
this.statuses.addAll(position, statuses)
notifyItemRangeInserted(position, statuses.size)
}
fun addAll(statuses: List<StatusViewData.Concrete>) {
val end = statuses.size
this.statuses.addAll(statuses)
notifyItemRangeInserted(end, statuses.size)
}
fun removeItem(position: Int) {
statuses.removeAt(position)
notifyItemRemoved(position)
}
fun clear() {
statuses.clear()
detailedStatusPosition = RecyclerView.NO_POSITION
notifyDataSetChanged()
}
fun setItem(position: Int, status: StatusViewData.Concrete, notifyAdapter: Boolean) {
statuses[position] = status
if (notifyAdapter) {
notifyItemChanged(position)
}
}
fun getItem(position: Int): StatusViewData.Concrete? = statuses.getOrNull(position)
fun setDetailedStatusPosition(position: Int) {
if (position != detailedStatusPosition &&
detailedStatusPosition != RecyclerView.NO_POSITION
) {
val prior = detailedStatusPosition
detailedStatusPosition = position
notifyItemChanged(prior)
} else {
detailedStatusPosition = position
}
}
companion object {
private const val VIEW_TYPE_STATUS = 0
private const val VIEW_TYPE_STATUS_DETAILED = 1
}
}