Commit 32f8b3fd authored by Eugene Shapovalov's avatar Eugene Shapovalov 💬

Create user avatar view with built-in navigation.

parent 4214db40
package ru.terrakok.gitlabclient.entity.app
import ru.terrakok.gitlabclient.entity.Commit
import ru.terrakok.gitlabclient.entity.ShortUser
/**
* Created by Eugene Shapovalov (@CraggyHaggy) on 20.10.18.
*/
data class CommitWithAvatarUrl(val commit: Commit, val authorAvatarUrl: String?)
\ No newline at end of file
data class CommitWithShortUser(val commit: Commit, val shortUser: ShortUser?)
\ No newline at end of file
......@@ -7,7 +7,7 @@ import org.threeten.bp.ZonedDateTime
import ru.terrakok.gitlabclient.di.DefaultPageSize
import ru.terrakok.gitlabclient.di.PrimitiveWrapper
import ru.terrakok.gitlabclient.entity.*
import ru.terrakok.gitlabclient.entity.app.CommitWithAvatarUrl
import ru.terrakok.gitlabclient.entity.app.CommitWithShortUser
import ru.terrakok.gitlabclient.entity.app.target.*
import ru.terrakok.gitlabclient.entity.event.EventAction
import ru.terrakok.gitlabclient.entity.mergerequest.MergeRequest
......@@ -230,11 +230,11 @@ class MergeRequestRepository @Inject constructor(
.zip(
getAllMergeRequestParticipants(projectId, mergeRequestId),
api.getMergeRequestCommits(projectId, mergeRequestId, page, pageSize),
BiFunction<List<ShortUser>, List<Commit>, List<CommitWithAvatarUrl>> { participants, commits ->
BiFunction<List<ShortUser>, List<Commit>, List<CommitWithShortUser>> { participants, commits ->
commits.map { commit ->
CommitWithAvatarUrl(
CommitWithShortUser(
commit,
participants.find { it.name == commit.authorName || it.username == commit.authorName }?.avatarUrl
participants.find { it.name == commit.authorName || it.username == commit.authorName }
)
}
}
......
package ru.terrakok.gitlabclient.presentation.issue.info
import com.arellomobile.mvp.InjectViewState
import ru.terrakok.gitlabclient.Screens
import ru.terrakok.gitlabclient.di.IssueId
import ru.terrakok.gitlabclient.di.PrimitiveWrapper
import ru.terrakok.gitlabclient.di.ProjectId
import ru.terrakok.gitlabclient.entity.ShortUser
import ru.terrakok.gitlabclient.model.interactor.issue.IssueInteractor
import ru.terrakok.gitlabclient.model.system.flow.FlowRouter
import ru.terrakok.gitlabclient.presentation.global.BasePresenter
import ru.terrakok.gitlabclient.presentation.global.ErrorHandler
import javax.inject.Inject
......@@ -20,8 +17,7 @@ class IssueInfoPresenter @Inject constructor(
@ProjectId private val projectIdWrapper: PrimitiveWrapper<Long>,
@IssueId private val issueIdWrapper: PrimitiveWrapper<Long>,
private val issueInteractor: IssueInteractor,
private val errorHandler: ErrorHandler,
private val router: FlowRouter
private val errorHandler: ErrorHandler
) : BasePresenter<IssueInfoView>() {
private val projectId = projectIdWrapper.value
......@@ -40,6 +36,4 @@ class IssueInfoPresenter @Inject constructor(
)
.connect()
}
fun onAssigneeClicked(assignee: ShortUser) = router.startFlow(Screens.UserFlow(assignee.id))
}
\ No newline at end of file
......@@ -4,7 +4,7 @@ import com.arellomobile.mvp.InjectViewState
import ru.terrakok.gitlabclient.di.MergeRequestId
import ru.terrakok.gitlabclient.di.PrimitiveWrapper
import ru.terrakok.gitlabclient.di.ProjectId
import ru.terrakok.gitlabclient.entity.app.CommitWithAvatarUrl
import ru.terrakok.gitlabclient.entity.app.CommitWithShortUser
import ru.terrakok.gitlabclient.model.interactor.mergerequest.MergeRequestInteractor
import ru.terrakok.gitlabclient.presentation.global.BasePresenter
import ru.terrakok.gitlabclient.presentation.global.ErrorHandler
......@@ -35,7 +35,7 @@ class MergeRequestCommitsPresenter @Inject constructor(
private val paginator = Paginator(
{ page -> mrInteractor.getMergeRequestCommits(projectId, mrId, page) },
object : Paginator.ViewController<CommitWithAvatarUrl> {
object : Paginator.ViewController<CommitWithShortUser> {
override fun showEmptyProgress(show: Boolean) {
viewState.showEmptyProgress(show)
}
......@@ -56,7 +56,7 @@ class MergeRequestCommitsPresenter @Inject constructor(
viewState.showEmptyView(show)
}
override fun showData(show: Boolean, data: List<CommitWithAvatarUrl>) {
override fun showData(show: Boolean, data: List<CommitWithShortUser>) {
viewState.showCommits(show, data)
}
......
......@@ -4,7 +4,7 @@ import com.arellomobile.mvp.MvpView
import com.arellomobile.mvp.viewstate.strategy.AddToEndSingleStrategy
import com.arellomobile.mvp.viewstate.strategy.OneExecutionStateStrategy
import com.arellomobile.mvp.viewstate.strategy.StateStrategyType
import ru.terrakok.gitlabclient.entity.app.CommitWithAvatarUrl
import ru.terrakok.gitlabclient.entity.app.CommitWithShortUser
/**
* Created by Eugene Shapovalov (@CraggyHaggy) on 20.10.18.
......@@ -17,7 +17,7 @@ interface MergeRequestCommitsView : MvpView {
fun showPageProgress(show: Boolean)
fun showEmptyView(show: Boolean)
fun showEmptyError(show: Boolean, message: String?)
fun showCommits(show: Boolean, commits: List<CommitWithAvatarUrl>)
fun showCommits(show: Boolean, commits: List<CommitWithShortUser>)
@StateStrategyType(OneExecutionStateStrategy::class)
fun showMessage(message: String)
......
......@@ -40,6 +40,4 @@ class MergeRequestInfoPresenter @Inject constructor(
)
.connect()
}
fun onAssigneeClicked(assignee: ShortUser) = router.startFlow(Screens.UserFlow(assignee.id))
}
\ No newline at end of file
......@@ -71,7 +71,7 @@ class NavigationDrawerFragment : BaseFragment(), NavigationDrawerView, MessageDi
override fun setAccounts(accounts: List<UserAccount>, currentAccount: UserAccount) {
nickTV.text = currentAccount.userName
serverNameTV.text = currentAccount.serverPath
avatarImageView.loadRoundedImage(currentAccount.avatarUrl, context)
avatarImageView.setUserInfo(currentAccount.userId, currentAccount.avatarUrl)
accountsContainer.removeAllViews()
accounts.forEach { acc ->
......
......@@ -7,13 +7,11 @@ import ru.terrakok.gitlabclient.entity.ShortUser
/**
* Created by Eugene Shapovalov (@CraggyHaggy) on 26.05.19.
*/
class AssigneesAdapter(
clickListener: (ShortUser) -> Unit
) : ListDelegationAdapter<MutableList<ShortUser>>() {
class AssigneesAdapter : ListDelegationAdapter<MutableList<ShortUser>>() {
init {
items = mutableListOf()
delegatesManager.addDelegate(AssigneesAdapterDelegate(clickListener))
delegatesManager.addDelegate(AssigneesAdapterDelegate())
}
fun setData(assignees: List<ShortUser>) {
......
......@@ -5,19 +5,14 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.hannesdorfmann.adapterdelegates4.AdapterDelegate
import kotlinx.android.synthetic.main.item_assignee.view.*
import kotlinx.android.synthetic.main.item_target_header_public.view.avatarImageView
import kotlinx.android.synthetic.main.item_target_header_public.view.titleTextView
import ru.terrakok.gitlabclient.R
import ru.terrakok.gitlabclient.entity.ShortUser
import ru.terrakok.gitlabclient.extension.inflate
import ru.terrakok.gitlabclient.extension.loadRoundedImage
/**
* Created by Eugene Shapovalov (@CraggyHaggy) on 26.05.19.
*/
class AssigneesAdapterDelegate(
private val clickListener: (ShortUser) -> Unit
) : AdapterDelegate<MutableList<ShortUser>>() {
class AssigneesAdapterDelegate : AdapterDelegate<MutableList<ShortUser>>() {
override fun isForViewType(items: MutableList<ShortUser>, position: Int) = true
......@@ -35,16 +30,12 @@ class AssigneesAdapterDelegate(
private lateinit var item: ShortUser
init {
view.setOnClickListener { clickListener(item) }
}
fun bind(item: ShortUser) {
this.item = item
with(itemView) {
titleTextView.text = item.name
subtitleTextView.text = item.username
avatarImageView.loadRoundedImage(item.avatarUrl)
avatarImageView.setUserInfo(item.id, item.avatarUrl)
}
}
}
......
......@@ -6,10 +6,9 @@ import androidx.recyclerview.widget.RecyclerView
import com.hannesdorfmann.adapterdelegates4.AdapterDelegate
import kotlinx.android.synthetic.main.item_merge_request_commit.view.*
import ru.terrakok.gitlabclient.R
import ru.terrakok.gitlabclient.entity.app.CommitWithAvatarUrl
import ru.terrakok.gitlabclient.entity.app.CommitWithShortUser
import ru.terrakok.gitlabclient.extension.humanTime
import ru.terrakok.gitlabclient.extension.inflate
import ru.terrakok.gitlabclient.extension.loadRoundedImage
/**
* Created by Eugene Shapovalov (@CraggyHaggy) on 20.10.18.
......@@ -17,7 +16,7 @@ import ru.terrakok.gitlabclient.extension.loadRoundedImage
class CommitAdapterDelegate : AdapterDelegate<MutableList<Any>>() {
override fun isForViewType(items: MutableList<Any>, position: Int) =
items[position] is CommitWithAvatarUrl
items[position] is CommitWithShortUser
override fun onCreateViewHolder(parent: ViewGroup): RecyclerView.ViewHolder =
ViewHolder(parent.inflate(R.layout.item_merge_request_commit))
......@@ -27,20 +26,22 @@ class CommitAdapterDelegate : AdapterDelegate<MutableList<Any>>() {
position: Int,
viewHolder: RecyclerView.ViewHolder,
payloads: MutableList<Any>
) = (viewHolder as ViewHolder).bind(items[position] as CommitWithAvatarUrl)
) = (viewHolder as ViewHolder).bind(items[position] as CommitWithShortUser)
private inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
private lateinit var commitWithAvatarUrl: CommitWithAvatarUrl
private lateinit var commitWithShortUser: CommitWithShortUser
fun bind(commitWithAvatarUrl: CommitWithAvatarUrl) {
this.commitWithAvatarUrl = commitWithAvatarUrl
fun bind(commitWithShortUser: CommitWithShortUser) {
this.commitWithShortUser = commitWithShortUser
with(itemView) {
avatarImageView.loadRoundedImage(commitWithAvatarUrl.authorAvatarUrl)
titleTextView.text = commitWithAvatarUrl.commit.title
commitWithShortUser.shortUser?.run {
avatarImageView.setUserInfo(id, avatarUrl)
}
titleTextView.text = commitWithShortUser.commit.title
descriptionTextView.text = String.format(
context.getString(R.string.merge_request_commits_description),
commitWithAvatarUrl.commit.authorName,
commitWithAvatarUrl.commit.authoredDate.humanTime(resources)
commitWithShortUser.commit.authorName,
commitWithShortUser.commit.authoredDate.humanTime(resources)
)
}
}
......
......@@ -3,7 +3,7 @@ package ru.terrakok.gitlabclient.ui.global.list
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.hannesdorfmann.adapterdelegates4.ListDelegationAdapter
import ru.terrakok.gitlabclient.entity.app.CommitWithAvatarUrl
import ru.terrakok.gitlabclient.entity.app.CommitWithShortUser
/**
* Created by Eugene Shapovalov (@CraggyHaggy) on 20.10.18.
......@@ -18,7 +18,7 @@ class TargetCommitsAdapter(
delegatesManager.addDelegate(ProgressAdapterDelegate())
}
fun setData(data: List<CommitWithAvatarUrl>) {
fun setData(data: List<CommitWithShortUser>) {
val oldItems = items.toList()
items.clear()
......@@ -66,7 +66,7 @@ class TargetCommitsAdapter(
val oldItem = oldItems[oldItemPosition]
val newItem = newItems[newItemPosition]
return if (newItem is CommitWithAvatarUrl && oldItem is CommitWithAvatarUrl) {
return if (newItem is CommitWithShortUser && oldItem is CommitWithShortUser) {
newItem.commit.id == oldItem.commit.id
} else {
newItem is ProgressItem && oldItem is ProgressItem
......@@ -77,7 +77,7 @@ class TargetCommitsAdapter(
val oldItem = oldItems[oldItemPosition]
val newItem = newItems[newItemPosition]
return if (newItem is CommitWithAvatarUrl && oldItem is CommitWithAvatarUrl) {
return if (newItem is CommitWithShortUser && oldItem is CommitWithShortUser) {
newItem == oldItem
} else {
false
......
......@@ -70,7 +70,7 @@ class TargetHeaderPublicAdapterDelegate(
titleTextView.text = item.title.getHumanName(resources)
Markwon.setText(descriptionTextView, item.body)
descriptionTextView.movementMethod = null //disable internal link click
avatarImageView.loadRoundedImage(item.author.avatarUrl)
avatarImageView.setUserInfo(item.author.id, item.author.avatarUrl)
iconImageView.setImageResource(item.icon.getIcon())
dateTextView.text = item.date.humanTime(resources)
......
......@@ -10,7 +10,6 @@ import ru.terrakok.gitlabclient.R
import ru.terrakok.gitlabclient.entity.Note
import ru.terrakok.gitlabclient.extension.humanTime
import ru.terrakok.gitlabclient.extension.inflate
import ru.terrakok.gitlabclient.extension.loadRoundedImage
import ru.terrakok.gitlabclient.presentation.global.NoteWithFormattedBody
/**
......@@ -37,7 +36,7 @@ class UserNoteAdapterDelegate : AdapterDelegate<MutableList<Any>>() {
fun bind(data: NoteWithFormattedBody) {
this.note = data.note
with(itemView) {
avatarImageView.loadRoundedImage(note.author.avatarUrl)
avatarImageView.setUserInfo(note.author.id, note.author.avatarUrl)
titleTextView.text = note.author.name
subtitleTextView.text = note.createdAt.humanTime(context.resources)
Markwon.setText(descriptionTextView, data.body)
......
package ru.terrakok.gitlabclient.ui.global.view.custom
import android.content.Context
import android.util.AttributeSet
import android.widget.ImageView
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
import ru.terrakok.cicerone.Router
import ru.terrakok.gitlabclient.R
import ru.terrakok.gitlabclient.Screens
import ru.terrakok.gitlabclient.di.DI
import toothpick.Toothpick
class UserAvatarView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ImageView(context, attrs, defStyleAttr) {
private val router = Toothpick.openScope(DI.APP_SCOPE).getInstance(Router::class.java)
private var userId: Long? = null
init {
setOnClickListener {
userId?.let {
router.navigateTo(Screens.UserFlow(it))
}
}
}
fun setUserInfo(userId: Long, avatarUrl: String?) {
this.userId = userId
Glide.with(context)
.load(avatarUrl)
.apply(
RequestOptions().apply {
placeholder(R.drawable.default_img)
error(R.drawable.default_img)
}
)
.apply(RequestOptions.circleCropTransform())
.into(this)
}
}
\ No newline at end of file
......@@ -7,7 +7,10 @@ import ru.noties.markwon.Markwon
import ru.terrakok.gitlabclient.R
import ru.terrakok.gitlabclient.entity.issue.Issue
import ru.terrakok.gitlabclient.entity.issue.IssueState
import ru.terrakok.gitlabclient.extension.*
import ru.terrakok.gitlabclient.extension.color
import ru.terrakok.gitlabclient.extension.humanTime
import ru.terrakok.gitlabclient.extension.showSnackMessage
import ru.terrakok.gitlabclient.extension.visible
import ru.terrakok.gitlabclient.presentation.issue.details.IssueDetailsPresenter
import ru.terrakok.gitlabclient.presentation.issue.details.IssueDetailsView
import ru.terrakok.gitlabclient.ui.global.BaseFragment
......@@ -53,7 +56,7 @@ class IssueDetailsFragment : BaseFragment(), IssueDetailsView {
}
}
}
avatarImageView.loadRoundedImage(issue.author.avatarUrl, context)
avatarImageView.setUserInfo(issue.author.id, issue.author.avatarUrl)
Markwon.setText(descriptionTextView, mdDescription)
}
......
package ru.terrakok.gitlabclient.ui.issue
import android.os.Bundle
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.arellomobile.mvp.presenter.InjectPresenter
......@@ -32,6 +33,18 @@ class IssueInfoFragment : BaseFragment(), IssueInfoView {
fun providePresenter(): IssueInfoPresenter =
scope.getInstance(IssueInfoPresenter::class.java)
private val assigneesAdapter by lazy { AssigneesAdapter() }
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
with(assigneesList) {
isNestedScrollingEnabled = false
layoutManager = LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false)
setHasFixedSize(true)
adapter = assigneesAdapter
}
}
override fun showInfo(issue: Issue) {
with(issue) {
showAssignees(assignees)
......@@ -48,14 +61,7 @@ class IssueInfoFragment : BaseFragment(), IssueInfoView {
private fun showAssignees(assignees: List<ShortUser>) {
if (assignees.isNotEmpty()) {
assigneesNone.visible(false)
with(assigneesList) {
isNestedScrollingEnabled = false
layoutManager = LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false)
setHasFixedSize(true)
adapter = AssigneesAdapter { presenter.onAssigneeClicked(it) }.apply {
setData(assignees)
}
}
assigneesAdapter.setData(assignees)
} else {
assigneesList.visible(false)
}
......
......@@ -6,7 +6,7 @@ import com.arellomobile.mvp.presenter.InjectPresenter
import com.arellomobile.mvp.presenter.ProvidePresenter
import kotlinx.android.synthetic.main.layout_base_list.*
import ru.terrakok.gitlabclient.R
import ru.terrakok.gitlabclient.entity.app.CommitWithAvatarUrl
import ru.terrakok.gitlabclient.entity.app.CommitWithShortUser
import ru.terrakok.gitlabclient.extension.showSnackMessage
import ru.terrakok.gitlabclient.extension.visible
import ru.terrakok.gitlabclient.presentation.mergerequest.commits.MergeRequestCommitsPresenter
......@@ -69,7 +69,7 @@ class MergeRequestCommitsFragment : BaseFragment(), MergeRequestCommitsView {
emptyView.apply { if (show) showEmptyError(message) else hide() }
}
override fun showCommits(show: Boolean, commits: List<CommitWithAvatarUrl>) {
override fun showCommits(show: Boolean, commits: List<CommitWithShortUser>) {
recyclerView.visible(show)
postViewAction { adapter.setData(commits) }
}
......
......@@ -7,7 +7,10 @@ import ru.noties.markwon.Markwon
import ru.terrakok.gitlabclient.R
import ru.terrakok.gitlabclient.entity.mergerequest.MergeRequest
import ru.terrakok.gitlabclient.entity.mergerequest.MergeRequestState
import ru.terrakok.gitlabclient.extension.*
import ru.terrakok.gitlabclient.extension.humanTime
import ru.terrakok.gitlabclient.extension.showSnackMessage
import ru.terrakok.gitlabclient.extension.tint
import ru.terrakok.gitlabclient.extension.visible
import ru.terrakok.gitlabclient.presentation.mergerequest.details.MergeRequestDetailsPresenter
import ru.terrakok.gitlabclient.presentation.mergerequest.details.MergeRequestDetailsView
import ru.terrakok.gitlabclient.ui.global.BaseFragment
......@@ -67,7 +70,7 @@ class MergeRequestDetailsFragment : BaseFragment(), MergeRequestDetailsView {
}
}
}
avatarImageView.loadRoundedImage(mr.author.avatarUrl, context)
avatarImageView.setUserInfo(mr.author.id, mr.author.avatarUrl)
Markwon.setText(descriptionTextView, mdDescription)
}
......
package ru.terrakok.gitlabclient.ui.mergerequest
import android.os.Bundle
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.arellomobile.mvp.presenter.InjectPresenter
......@@ -33,6 +34,18 @@ class MergeRequestInfoFragment : BaseFragment(), MergeRequestInfoView {
fun providePresenter() =
scope.getInstance(MergeRequestInfoPresenter::class.java)
private val assigneesAdapter by lazy { AssigneesAdapter() }
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
with(assigneesList) {
isNestedScrollingEnabled = false
layoutManager = LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false)
setHasFixedSize(true)
adapter = assigneesAdapter
}
}
override fun showInfo(mr: MergeRequest) {
with(mr) {
showAssignees(assignees)
......@@ -47,14 +60,7 @@ class MergeRequestInfoFragment : BaseFragment(), MergeRequestInfoView {
private fun showAssignees(assignees: List<ShortUser>) {
if (assignees.isNotEmpty()) {
assigneesNone.visible(false)
with(assigneesList) {
isNestedScrollingEnabled = false
layoutManager = LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false)
setHasFixedSize(true)
adapter = AssigneesAdapter { presenter.onAssigneeClicked(it) }.apply {
setData(assignees)
}
}
assigneesAdapter.setData(assignees)
} else {
assigneesList.visible(false)
}
......
......@@ -14,7 +14,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
<ru.terrakok.gitlabclient.ui.global.view.custom.UserAvatarView
android:id="@+id/avatarImageView"
android:layout_width="48dp"
android:layout_height="48dp"
......
......@@ -14,7 +14,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
<ru.terrakok.gitlabclient.ui.global.view.custom.UserAvatarView
android:id="@+id/avatarImageView"
android:layout_width="48dp"
android:layout_height="48dp"
......
......@@ -22,7 +22,7 @@
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
<ru.terrakok.gitlabclient.ui.global.view.custom.UserAvatarView
android:id="@+id/avatarImageView"
android:layout_width="72dp"
android:layout_height="72dp"
......
......@@ -6,7 +6,7 @@
android:layout_height="wrap_content"
android:paddingTop="16dp">
<ImageView
<ru.terrakok.gitlabclient.ui.global.view.custom.UserAvatarView
android:id="@+id/avatarImageView"
android:layout_width="38dp"
android:layout_height="38dp"
......
......@@ -7,7 +7,7 @@
android:background="?attr/selectableItemBackground"
android:paddingBottom="16dp">
<ImageView
<ru.terrakok.gitlabclient.ui.global.view.custom.UserAvatarView
android:id="@+id/avatarImageView"
android:layout_width="48dp"
android:layout_height="48dp"
......
......@@ -6,7 +6,7 @@
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground">
<ImageView
<ru.terrakok.gitlabclient.ui.global.view.custom.UserAvatarView
android:id="@+id/avatarImageView"
android:layout_width="48dp"
android:layout_height="48dp"
......
......@@ -7,7 +7,7 @@
android:layout_height="wrap_content"
android:paddingBottom="16dp">
<ImageView
<ru.terrakok.gitlabclient.ui.global.view.custom.UserAvatarView