Commit 9809e778 authored by Konstantin Tskhovrebov's avatar Konstantin Tskhovrebov 🤖

Merge branch 'feature/user_activity_badges' into 'develop'

Feature/user activity badges

See merge request terrakok/gitlab-client!188
parents 63d42715 f216f7aa
......@@ -131,7 +131,7 @@ dependencies {
implementation("ru.noties:markwon:$markwonVersion")
implementation("ru.noties:markwon-image-loader:$markwonVersion")
//Bottom navigation bar
implementation("com.aurelhubert:ahbottomnavigation:2.1.0")
implementation("com.aurelhubert:ahbottomnavigation:2.3.4")
//Lottie
implementation("com.airbnb.android:lottie:2.5.1")
//Date
......
......@@ -19,7 +19,7 @@ import ru.terrakok.gitlabclient.model.interactor.issue.IssueInteractor
import ru.terrakok.gitlabclient.model.interactor.mergerequest.MergeRequestInteractor
import ru.terrakok.gitlabclient.model.interactor.milestone.MilestoneInteractor
import ru.terrakok.gitlabclient.model.interactor.project.ProjectInteractor
import ru.terrakok.gitlabclient.model.interactor.todo.TodoListInteractor
import ru.terrakok.gitlabclient.model.interactor.todo.TodoInteractor
import ru.terrakok.gitlabclient.model.interactor.user.UserInteractor
import ru.terrakok.gitlabclient.model.repository.event.EventRepository
import ru.terrakok.gitlabclient.model.repository.issue.IssueRepository
......@@ -99,6 +99,6 @@ class ServerModule(userAccount: UserAccount?) : Module() {
//Todos
bind(TodoRepository::class.java)
bind(TodoListInteractor::class.java)
bind(TodoInteractor::class.java)
}
}
\ No newline at end of file
......@@ -27,6 +27,7 @@ import androidx.fragment.app.Fragment
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
import com.google.android.material.snackbar.Snackbar
import retrofit2.adapter.rxjava2.Result
import ru.terrakok.cicerone.Navigator
import ru.terrakok.cicerone.android.support.SupportAppScreen
import ru.terrakok.cicerone.commands.BackTo
......@@ -233,4 +234,8 @@ fun Toolbar.setTitleEllipsize(ellipsize: TextUtils.TruncateAt) {
}
}
title = ""
}
fun Result<*>.getXTotalHeader(): Int {
return if (!isError) response().headers().get("X-Total")?.toInt() ?: 0 else 0
}
\ No newline at end of file
......@@ -3,6 +3,7 @@ package ru.terrakok.gitlabclient.model.data.server
import io.reactivex.Completable
import io.reactivex.Single
import org.threeten.bp.LocalDateTime
import retrofit2.adapter.rxjava2.Result
import retrofit2.http.*
import ru.terrakok.gitlabclient.entity.*
import ru.terrakok.gitlabclient.entity.event.Event
......@@ -373,4 +374,24 @@ interface GitlabApi {
@Path("project_id") projectId: Long,
@Path("label_id") labelId: Long
): Single<Label>
@HEAD("$API_PATH/merge_requests")
fun getMyAssignedMergeRequestHeaders(
@Query("scope") scope: MergeRequestScope = MergeRequestScope.ASSIGNED_TO_ME,
@Query("state") state: MergeRequestState = MergeRequestState.OPENED,
@Query("per_page") pageSize: Int = 1
): Single<Result<Void>>
@HEAD("$API_PATH/issues")
fun getMyAssignedIssueHeaders(
@Query("scope") scope: IssueScope = IssueScope.ASSIGNED_BY_ME,
@Query("state") state: IssueState = IssueState.OPENED,
@Query("per_page") pageSize: Int = 1
): Single<Result<Void>>
@HEAD("$API_PATH/todos")
fun getMyAssignedTodoHeaders(
@Query("state") state: TodoState = TodoState.PENDING,
@Query("per_page") pageSize: Int = 1
): Single<Result<Void>>
}
\ No newline at end of file
......@@ -59,4 +59,6 @@ class IssueInteractor @Inject constructor(
issueId: Long,
body: String
) = issueRepository.createIssueNote(projectId, issueId, body)
fun getMyAssignedIssueCount() = issueRepository.getMyAssignedIssueCount()
}
\ No newline at end of file
......@@ -66,4 +66,6 @@ class MergeRequestInteractor @Inject constructor(
projectId: Long,
mergeRequestId: Long
) = mergeRequestRepository.getMergeRequestChanges(projectId, mergeRequestId)
fun getMyAssignedMergeRequestCount() = mergeRequestRepository.getMyAssignedMergeRequestCount()
}
\ No newline at end of file
......@@ -8,7 +8,7 @@ import javax.inject.Inject
/**
* @author Eugene Shapovalov (CraggyHaggy). Date: 21.09.17
*/
class TodoListInteractor @Inject constructor(
class TodoInteractor @Inject constructor(
private val todoRepository: TodoRepository,
private val profileRepository: ProfileRepository
) {
......@@ -24,4 +24,6 @@ class TodoListInteractor @Inject constructor(
page = page
)
}
fun getMyAssignedTodoCount() = todoRepository.getMyAssignedTodoCount()
}
\ No newline at end of file
......@@ -14,6 +14,7 @@ import ru.terrakok.gitlabclient.entity.event.EventAction
import ru.terrakok.gitlabclient.entity.issue.Issue
import ru.terrakok.gitlabclient.entity.issue.IssueScope
import ru.terrakok.gitlabclient.entity.issue.IssueState
import ru.terrakok.gitlabclient.extension.getXTotalHeader
import ru.terrakok.gitlabclient.model.data.server.GitlabApi
import ru.terrakok.gitlabclient.model.data.server.MarkDownUrlResolver
import ru.terrakok.gitlabclient.model.system.SchedulersProvider
......@@ -207,4 +208,10 @@ class IssueRepository @Inject constructor(
.getMilestoneIssues(projectId, milestoneId, page, pageSize)
.subscribeOn(schedulers.io())
.observeOn(schedulers.ui())
fun getMyAssignedIssueCount(): Single<Int> =
api.getMyAssignedIssueHeaders()
.map { it.getXTotalHeader() }
.subscribeOn(schedulers.io())
.observeOn(schedulers.ui())
}
\ No newline at end of file
......@@ -14,6 +14,7 @@ import ru.terrakok.gitlabclient.entity.mergerequest.MergeRequest
import ru.terrakok.gitlabclient.entity.mergerequest.MergeRequestScope
import ru.terrakok.gitlabclient.entity.mergerequest.MergeRequestState
import ru.terrakok.gitlabclient.entity.mergerequest.MergeRequestViewType
import ru.terrakok.gitlabclient.extension.getXTotalHeader
import ru.terrakok.gitlabclient.model.data.server.GitlabApi
import ru.terrakok.gitlabclient.model.data.server.MarkDownUrlResolver
import ru.terrakok.gitlabclient.model.system.SchedulersProvider
......@@ -255,4 +256,10 @@ class MergeRequestRepository @Inject constructor(
.getMilestoneMergeRequests(projectId, milestoneId, page, pageSize)
.subscribeOn(schedulers.io())
.observeOn(schedulers.ui())
fun getMyAssignedMergeRequestCount(): Single<Int> =
api.getMyAssignedMergeRequestHeaders()
.map { it.getXTotalHeader() }
.subscribeOn(schedulers.io())
.observeOn(schedulers.ui())
}
\ No newline at end of file
package ru.terrakok.gitlabclient.model.repository.todo
import io.reactivex.Single
import ru.terrakok.gitlabclient.di.DefaultPageSize
import ru.terrakok.gitlabclient.di.PrimitiveWrapper
import ru.terrakok.gitlabclient.entity.Assignee
......@@ -10,6 +11,7 @@ import ru.terrakok.gitlabclient.entity.target.TargetType
import ru.terrakok.gitlabclient.entity.todo.Todo
import ru.terrakok.gitlabclient.entity.todo.TodoAction
import ru.terrakok.gitlabclient.entity.todo.TodoState
import ru.terrakok.gitlabclient.extension.getXTotalHeader
import ru.terrakok.gitlabclient.model.data.server.GitlabApi
import ru.terrakok.gitlabclient.model.system.SchedulersProvider
import javax.inject.Inject
......@@ -94,4 +96,10 @@ class TodoRepository @Inject constructor(
fun markPendingTodoAsDone(id: Int) = api.markPendingTodoAsDone(id)
fun markAllPendingTodosAsDone() = api.markAllPendingTodosAsDone()
fun getMyAssignedTodoCount(): Single<Int> =
api.getMyAssignedTodoHeaders()
.map { it.getXTotalHeader() }
.subscribeOn(schedulers.io())
.observeOn(schedulers.ui())
}
\ No newline at end of file
package ru.terrakok.gitlabclient.presentation.main
import com.arellomobile.mvp.InjectViewState
import io.reactivex.Single
import io.reactivex.functions.Function3
import ru.terrakok.gitlabclient.model.interactor.issue.IssueInteractor
import ru.terrakok.gitlabclient.model.interactor.mergerequest.MergeRequestInteractor
import ru.terrakok.gitlabclient.model.interactor.todo.TodoInteractor
import ru.terrakok.gitlabclient.presentation.global.BasePresenter
import javax.inject.Inject
/**
* Created by Eugene Shapovalov (@CraggyHaggy) on 20.05.19.
*/
@InjectViewState
class MainPresenter @Inject constructor(
private val issueInteractor: IssueInteractor,
private val mergeRequestInteractor: MergeRequestInteractor,
private val todoInteractor: TodoInteractor
) : BasePresenter<MainView>() {
override fun onFirstViewAttach() {
super.onFirstViewAttach()
Single
.zip(
issueInteractor.getMyAssignedIssueCount(),
mergeRequestInteractor.getMyAssignedMergeRequestCount(),
todoInteractor.getMyAssignedTodoCount(),
Function3<Int, Int, Int, Triple<Int, Int, Int>> { issueCount, mergeRequestCount, todoCount ->
Triple(issueCount, mergeRequestCount, todoCount)
}
)
.subscribe(
{ viewState.setAssignedNotifications(it.first, it.second, it.third) },
{
// TODO: user activity badges (Maybe we can retry this request, until it finishes correctly?).
}
)
.connect()
}
}
\ No newline at end of file
package ru.terrakok.gitlabclient.presentation.main
import com.arellomobile.mvp.MvpView
import com.arellomobile.mvp.viewstate.strategy.OneExecutionStateStrategy
import com.arellomobile.mvp.viewstate.strategy.StateStrategyType
/**
* Created by Eugene Shapovalov (@CraggyHaggy) on 20.05.19.
*/
@StateStrategyType(OneExecutionStateStrategy::class)
interface MainView : MvpView {
fun setAssignedNotifications(issueCount: Int, mergeRequestCount: Int, todoCount: Int)
}
\ No newline at end of file
......@@ -7,7 +7,7 @@ import ru.terrakok.gitlabclient.di.PrimitiveWrapper
import ru.terrakok.gitlabclient.di.TodoListPendingState
import ru.terrakok.gitlabclient.entity.app.target.TargetHeader
import ru.terrakok.gitlabclient.extension.openInfo
import ru.terrakok.gitlabclient.model.interactor.todo.TodoListInteractor
import ru.terrakok.gitlabclient.model.interactor.todo.TodoInteractor
import ru.terrakok.gitlabclient.model.system.flow.FlowRouter
import ru.terrakok.gitlabclient.presentation.global.BasePresenter
import ru.terrakok.gitlabclient.presentation.global.ErrorHandler
......@@ -21,7 +21,7 @@ import javax.inject.Inject
@InjectViewState
class MyTodosPresenter @Inject constructor(
@TodoListPendingState private val pendingStateWrapper: PrimitiveWrapper<Boolean>,
private val todoListInteractor: TodoListInteractor,
private val todoInteractor: TodoInteractor,
private val mdConverter: MarkDownConverter,
private val errorHandler: ErrorHandler,
private val router: FlowRouter
......@@ -37,7 +37,7 @@ class MyTodosPresenter @Inject constructor(
private val paginator = Paginator(
{
todoListInteractor.getMyTodos(isPending, it)
todoInteractor.getMyTodos(isPending, it)
.flattenAsObservable { it }
.concatMap { item ->
when (item) {
......
package ru.terrakok.gitlabclient.ui.main
import android.os.Bundle
import androidx.annotation.ColorRes
import androidx.core.content.ContextCompat
import com.arellomobile.mvp.presenter.InjectPresenter
import com.arellomobile.mvp.presenter.ProvidePresenter
import com.aurelhubert.ahbottomnavigation.AHBottomNavigationAdapter
import com.aurelhubert.ahbottomnavigation.notification.AHNotification
import kotlinx.android.synthetic.main.fragment_main.*
import ru.terrakok.cicerone.android.support.SupportAppScreen
import ru.terrakok.gitlabclient.R
import ru.terrakok.gitlabclient.Screens
import ru.terrakok.gitlabclient.extension.color
import ru.terrakok.gitlabclient.presentation.main.MainPresenter
import ru.terrakok.gitlabclient.presentation.main.MainView
import ru.terrakok.gitlabclient.ui.global.BaseFragment
/**
* @author Konstantin Tskhovrebov (aka terrakok). Date: 02.04.17
*/
class MainFragment : BaseFragment() {
class MainFragment : BaseFragment(), MainView {
override val layoutRes = R.layout.fragment_main
private val currentTabFragment: BaseFragment?
get() = childFragmentManager.fragments.firstOrNull { !it.isHidden } as? BaseFragment
@InjectPresenter
lateinit var presenter: MainPresenter
@ProvidePresenter
fun providePresenter(): MainPresenter = scope.getInstance(MainPresenter::class.java)
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
......@@ -39,6 +52,8 @@ class MainFragment : BaseFragment() {
)
true
}
val leftMargin = resources.getDimension(R.dimen.bottom_bar_notification_left_margin).toInt()
setNotificationMarginLeft(leftMargin, leftMargin)
}
selectTab(
......@@ -78,6 +93,20 @@ class MainFragment : BaseFragment() {
currentTabFragment?.onBackPressed()
}
override fun setAssignedNotifications(issueCount: Int, mergeRequestCount: Int, todoCount: Int) {
with(bottomBar) {
setNotification(buildBottomBarNotification(R.color.fruit_salad, issueCount), 1)
setNotification(buildBottomBarNotification(R.color.brandy_punch, mergeRequestCount), 2)
setNotification(buildBottomBarNotification(R.color.mariner, todoCount), 3)
}
}
private fun buildBottomBarNotification(@ColorRes backgroundColor: Int, count: Int) =
AHNotification.Builder()
.setBackgroundColor(ContextCompat.getColor(requireContext(), backgroundColor))
.setText(count.toString())
.build()
companion object {
private val eventsTab = Screens.MyEvents
private val issuesTab = Screens.MyIssuesContainer
......
......@@ -19,12 +19,15 @@
<color name="green">#5AC54F</color>
<color name="lightGreen">#EEF9ED</color>
<color name="fruit_salad">#50A164</color>
<color name="red">#F23117</color>
<color name="lightRed">#FDEAE7</color>
<color name="brandy_punch">#D88138</color>
<color name="blue">#17d1f2</color>
<color name="lightBlue">#e7fafd</color>
<color name="mariner">#397BCE</color>
<color name="beige">#fff8e1</color>
......
......@@ -3,4 +3,5 @@
<dimen name="navigation_drawer_width">300dp</dimen>
<dimen name="divider_size">1dp</dimen>
<dimen name="toolbar_elevation">8dp</dimen>
<dimen name="bottom_bar_notification_left_margin">12dp</dimen>
</resources>
\ No newline at end of file
......@@ -13,9 +13,9 @@ import ru.terrakok.gitlabclient.model.repository.todo.TodoRepository
/**
* @author Eugene Shapovalov (CraggyHaggy). Date: 21.09.17
*/
class TodoListInteractorTest {
class TodoInteractorTest {
private lateinit var interactor: TodoListInteractor
private lateinit var interactor: TodoInteractor
private lateinit var todoRepository: TodoRepository
private lateinit var profileRepository: ProfileRepository
......@@ -27,7 +27,7 @@ class TodoListInteractorTest {
fun setUp() {
todoRepository = mock()
profileRepository = mock()
interactor = TodoListInteractor(todoRepository, profileRepository)
interactor = TodoInteractor(todoRepository, profileRepository)
}
@Test
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment