add mentions in comments

This commit is contained in:
k0shk0sh 2019-07-18 22:19:02 +02:00
parent 1085dc5937
commit f836961836
52 changed files with 911 additions and 453 deletions

View File

@ -3,7 +3,6 @@ package com.fastaccess.github.base
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.fastaccess.data.model.FastHubErrors
import com.fastaccess.extension.uiThread
import com.fastaccess.github.R
import com.google.gson.Gson
import io.reactivex.Completable
@ -66,14 +65,12 @@ abstract class BaseViewModel : ViewModel() {
}
protected fun <T> callApi(observable: Observable<T>): Observable<T> = observable
.uiThread()
.doOnSubscribe { showProgress() }
.doOnNext { hideProgress() }
.doOnError { handleError(it) }
.doOnComplete { hideProgress() }
protected fun callApi(completable: Completable): Completable = completable
.uiThread()
.doOnSubscribe { showProgress() }
.doOnComplete { hideProgress() }
.doOnError { handleError(it) }

View File

@ -4,6 +4,9 @@ import android.content.Context
import com.fastaccess.github.di.annotations.ForApplication
import com.fastaccess.github.di.scopes.PerFragment
import com.fastaccess.github.extensions.getDrawableCompat
import com.fastaccess.github.platform.mentions.MentionsPresenter
import com.fastaccess.github.ui.modules.issue.fragment.IssueFragment
import com.fastaccess.github.usecase.search.FilterSearchUsersUseCase
import com.fastaccess.markdown.R
import com.fastaccess.markdown.spans.*
import dagger.Module
@ -18,6 +21,8 @@ import net.nightwhistler.htmlspanner.style.Style
@Module
class FragmentModule {
@PerFragment @Provides fun provideContext(fragment: IssueFragment) = fragment.requireContext()
@PerFragment @Provides fun provideHtmlSpanner(@ForApplication context: Context): HtmlSpanner {
val mySpanner = HtmlSpanner()
mySpanner.isStripExtraWhiteSpace = true
@ -48,4 +53,9 @@ class FragmentModule {
mySpanner.registerHandler("h6", HeaderHandler(1.0f))
return mySpanner
}
@PerFragment @Provides fun provideMentionsPresenter(
context: Context,
searchUsersUseCase: FilterSearchUsersUseCase
) = MentionsPresenter(context, searchUsersUseCase)
}

View File

@ -25,6 +25,7 @@ import java.lang.reflect.Type
import java.net.URI
import java.text.SimpleDateFormat
import java.util.*
import javax.inject.Named
import javax.inject.Singleton
/**
@ -52,6 +53,14 @@ class NetworkModule {
.addInterceptor(HttpLoggingInterceptor())
.build()
@Named("apolloClient") @Singleton @Provides fun provideHttpClientForApollo(auth: AuthenticationInterceptor): OkHttpClient = OkHttpClient
.Builder()
.addInterceptor(auth)
.addInterceptor(Pandora.get().interceptor)
.addInterceptor(HttpLoggingInterceptor())
.build()
@Singleton @Provides fun provideRetrofit(
gson: Gson,
okHttpClient: OkHttpClient
@ -70,12 +79,13 @@ class NetworkModule {
.addConverterFactory(GithubResponseConverter(gson))
.client(okHttpClient)
@Singleton @Provides fun provideApollo(okHttpClient: OkHttpClient): ApolloClient = ApolloClient.builder()
@Singleton @Provides fun provideApollo(@Named("apolloClient") okHttpClient: OkHttpClient): ApolloClient = ApolloClient.builder()
.serverUrl(BuildConfig.GRAPHQL_REST_URL)
.okHttpClient(okHttpClient)
.addCustomTypeAdapter(CustomType.URI, UriApolloAdapter())
.addCustomTypeAdapter(CustomType.DATETIME, DateApolloAdapter())
.addCustomTypeAdapter(CustomType.HTML, ObjectApolloAdapter())
.addCustomTypeAdapter(CustomType.ID, ObjectApolloAdapter())
.build()
@Singleton @Provides fun provideLoginService(retrofit: Retrofit): LoginService = retrofit.create(LoginService::class.java)

View File

@ -35,8 +35,11 @@ class RepositoryModule {
return MyIssuesPullsRepositoryProvider(fastHubDatabase.getMainIssuesPullsDao())
}
@Singleton @Provides fun provideNotificationRepositoryProvider(fastHubDatabase: FastHubDatabase): NotificationRepositoryProvider {
return NotificationRepositoryProvider(fastHubDatabase.getNotifications())
@Singleton @Provides fun provideNotificationRepositoryProvider(
fastHubDatabase: FastHubDatabase,
schedulerProvider: SchedulerProvider
): NotificationRepositoryProvider {
return NotificationRepositoryProvider(fastHubDatabase.getNotifications(), schedulerProvider)
}
@Singleton @Provides fun provideFeedsRepositoryProvider(
@ -91,4 +94,6 @@ class RepositoryModule {
@Singleton @Provides fun provideSuggestionRepositoryProvider(fastHubDatabase: FastHubDatabase): SuggestionRepositoryProvider {
return SuggestionRepositoryProvider(fastHubDatabase.getSuggestionDao())
}
@Singleton @Provides fun provideAndroidSchedulerProvider(): SchedulerProvider = AndroidSchedulerProvider()
}

View File

@ -33,155 +33,190 @@ class UseCaseModule {
@PerFragment @Provides fun provideLoginWithAccessTokenUseCase(
loginRemoteRepository: LoginRepositoryProvider,
gson: Gson
gson: Gson,
schedulerProvider: SchedulerProvider
): LoginWithAccessTokenUseCase {
return LoginWithAccessTokenUseCase(loginRemoteRepository, gson)
return LoginWithAccessTokenUseCase(loginRemoteRepository, gson, schedulerProvider)
}
@PerFragment @Provides fun provideGetAccessTokenUseCase(loginRemoteRepository: LoginRepositoryProvider): GetAccessTokenUseCase {
return GetAccessTokenUseCase(loginRemoteRepository)
@PerFragment @Provides fun provideGetAccessTokenUseCase(
loginRemoteRepository: LoginRepositoryProvider,
schedulerProvider: SchedulerProvider
): GetAccessTokenUseCase {
return GetAccessTokenUseCase(loginRemoteRepository, schedulerProvider)
}
@PerFragment @Provides fun provideUserUseCase(userRepository: UserRepositoryProvider): UserUseCase = UserUseCase(userRepository)
@PerFragment @Provides fun provideUserUseCase(
userRepository: UserRepositoryProvider,
schedulerProvider: SchedulerProvider
): UserUseCase = UserUseCase(userRepository, schedulerProvider)
@PerFragment @Provides fun provideIssuesMainScreenUseCase(
loginRepository: LoginRepositoryProvider,
myIssuesPullsRepository: MyIssuesPullsRepositoryProvider,
apolloClient: ApolloClient
apolloClient: ApolloClient,
schedulerProvider: SchedulerProvider
): IssuesMainScreenUseCase {
return IssuesMainScreenUseCase(loginRepository, myIssuesPullsRepository, apolloClient)
return IssuesMainScreenUseCase(loginRepository, myIssuesPullsRepository, apolloClient, schedulerProvider)
}
@PerFragment @Provides fun providePullRequestsMainScreenUseCase(
loginRepository: LoginRepositoryProvider,
myIssues: MyIssuesPullsRepositoryProvider,
apolloClient: ApolloClient
apolloClient: ApolloClient,
schedulerProvider: SchedulerProvider
): PullRequestsMainScreenUseCase {
return PullRequestsMainScreenUseCase(loginRepository, myIssues, apolloClient)
return PullRequestsMainScreenUseCase(loginRepository, myIssues, apolloClient, schedulerProvider)
}
@PerFragment @Provides fun provideNotificationUseCase(
notificationRepositoryProvider: NotificationRepositoryProvider,
notificationService: NotificationService,
gson: Gson
gson: Gson,
schedulerProvider: SchedulerProvider
): NotificationUseCase {
return NotificationUseCase(notificationRepositoryProvider, notificationService, gson)
return NotificationUseCase(notificationRepositoryProvider, notificationService, gson, schedulerProvider)
}
@PerFragment @Provides fun provideFeedsUseCase(provider: FeedsRepositoryProvider): FeedsUseCase = FeedsUseCase(provider)
@PerFragment @Provides fun provideFeedsUseCase(
provider: FeedsRepositoryProvider,
schedulerProvider: SchedulerProvider
): FeedsUseCase = FeedsUseCase(provider, schedulerProvider)
@PerFragment @Provides fun provideBlockUnblockUserUseCase(userRepository: UserRepositoryProvider): BlockUnblockUserUseCase {
return BlockUnblockUserUseCase(userRepository)
@PerFragment @Provides fun provideBlockUnblockUserUseCase(
userRepository: UserRepositoryProvider,
schedulerProvider: SchedulerProvider
): BlockUnblockUserUseCase {
return BlockUnblockUserUseCase(userRepository, schedulerProvider)
}
@PerFragment @Provides fun provideIsUserBlockedUseCase(userRepository: UserRepositoryProvider): IsUserBlockedUseCase {
return IsUserBlockedUseCase(userRepository)
@PerFragment @Provides fun provideIsUserBlockedUseCase(
userRepository: UserRepositoryProvider,
schedulerProvider: SchedulerProvider
): IsUserBlockedUseCase {
return IsUserBlockedUseCase(userRepository, schedulerProvider)
}
@PerFragment @Provides fun provideFilterIssuesUseCase(
loginRepository: LoginRepositoryProvider,
apolloClient: ApolloClient
apolloClient: ApolloClient,
schedulerProvider: SchedulerProvider
): FilterIssuesUseCase {
return FilterIssuesUseCase(loginRepository, apolloClient)
return FilterIssuesUseCase(loginRepository, apolloClient, schedulerProvider)
}
@PerFragment @Provides fun provideFilterPullRequestsUseCase(
loginRepository: LoginRepositoryProvider,
apolloClient: ApolloClient
apolloClient: ApolloClient,
schedulerProvider: SchedulerProvider
): FilterPullRequestsUseCase {
return FilterPullRequestsUseCase(loginRepository, apolloClient)
return FilterPullRequestsUseCase(loginRepository, apolloClient, schedulerProvider)
}
@PerFragment @Provides fun provideFilterSearchReposUseCase(
apolloClient: ApolloClient
apolloClient: ApolloClient,
schedulerProvider: SchedulerProvider
): FilterSearchReposUseCase {
return FilterSearchReposUseCase(apolloClient)
return FilterSearchReposUseCase(apolloClient, schedulerProvider)
}
@PerFragment @Provides fun provideFilterSearchUsersUseCase(
apolloClient: ApolloClient
apolloClient: ApolloClient,
schedulerProvider: SchedulerProvider
): FilterSearchUsersUseCase {
return FilterSearchUsersUseCase(apolloClient)
return FilterSearchUsersUseCase(apolloClient, schedulerProvider)
}
@PerFragment @Provides fun provideGetIssueUseCase(
issueRepositoryProvider: IssueRepositoryProvider,
apolloClient: ApolloClient
apolloClient: ApolloClient,
schedulerProvider: SchedulerProvider
): GetIssueUseCase {
return GetIssueUseCase(issueRepositoryProvider, apolloClient)
return GetIssueUseCase(issueRepositoryProvider, apolloClient, schedulerProvider)
}
@PerFragment @Provides fun provideGetIssueTimelineUseCase(
issueRepositoryProvider: IssueRepositoryProvider,
apolloClient: ApolloClient
apolloClient: ApolloClient,
schedulerProvider: SchedulerProvider
): GetIssueTimelineUseCase {
return GetIssueTimelineUseCase(issueRepositoryProvider, apolloClient)
return GetIssueTimelineUseCase(issueRepositoryProvider, apolloClient, schedulerProvider)
}
@PerFragment @Provides fun provideEditIssuePrUseCase(
issueRepositoryProvider: IssueRepositoryProvider,
issuePrService: IssuePrService,
loginRepositoryProvider: LoginRepositoryProvider
loginRepositoryProvider: LoginRepositoryProvider,
schedulerProvider: SchedulerProvider
): CloseOpenIssuePrUseCase {
return CloseOpenIssuePrUseCase(issueRepositoryProvider, issuePrService, loginRepositoryProvider)
return CloseOpenIssuePrUseCase(issueRepositoryProvider, issuePrService, loginRepositoryProvider, schedulerProvider)
}
@PerFragment @Provides fun provideLockUnlockIssuePrUseCase(
issueRepositoryProvider: IssueRepositoryProvider,
apolloClient: ApolloClient,
loginRepositoryProvider: LoginRepositoryProvider
loginRepositoryProvider: LoginRepositoryProvider,
schedulerProvider: SchedulerProvider
): LockUnlockIssuePrUseCase {
return LockUnlockIssuePrUseCase(issueRepositoryProvider, apolloClient, loginRepositoryProvider)
return LockUnlockIssuePrUseCase(issueRepositoryProvider, apolloClient, loginRepositoryProvider, schedulerProvider)
}
@PerFragment @Provides fun provideGetLabelsUseCase(
apolloClient: ApolloClient
apolloClient: ApolloClient,
schedulerProvider: SchedulerProvider
): GetLabelsUseCase {
return GetLabelsUseCase(apolloClient)
return GetLabelsUseCase(apolloClient, schedulerProvider)
}
@PerFragment @Provides fun provideCreateLabelUseCase(
repoService: RepoService
repoService: RepoService,
schedulerProvider: SchedulerProvider
): CreateLabelUseCase {
return CreateLabelUseCase(repoService)
return CreateLabelUseCase(repoService, schedulerProvider)
}
@PerFragment @Provides fun providePutLabelsUseCase(
repoService: RepoService
repoService: RepoService,
schedulerProvider: SchedulerProvider
): PutLabelsUseCase {
return PutLabelsUseCase(repoService)
return PutLabelsUseCase(repoService, schedulerProvider)
}
@PerFragment @Provides fun provideGetAssigneesUseCase(
apolloClient: ApolloClient
apolloClient: ApolloClient,
schedulerProvider: SchedulerProvider
): GetAssigneesUseCase {
return GetAssigneesUseCase(apolloClient)
return GetAssigneesUseCase(apolloClient, schedulerProvider)
}
@PerFragment @Provides fun provideAddAssigneesUseCase(
repoService: RepoService
repoService: RepoService,
schedulerProvider: SchedulerProvider
): AddAssigneesUseCase {
return AddAssigneesUseCase(repoService)
return AddAssigneesUseCase(repoService, schedulerProvider)
}
@PerFragment @Provides fun provideGetMilestonesUseCase(
apolloClient: ApolloClient
apolloClient: ApolloClient,
schedulerProvider: SchedulerProvider
): GetMilestonesUseCase {
return GetMilestonesUseCase(apolloClient)
return GetMilestonesUseCase(apolloClient, schedulerProvider)
}
@PerFragment @Provides fun provideCreateMilestoneUseCase(
repoService: RepoService
repoService: RepoService,
schedulerProvider: SchedulerProvider
): CreateMilestoneUseCase {
return CreateMilestoneUseCase(repoService)
return CreateMilestoneUseCase(repoService, schedulerProvider)
}
@PerFragment @Provides fun provideMilestoneIssuePrUseCase(
issueRepositoryProvider: IssueRepositoryProvider,
issuePrService: IssuePrService,
loginRepositoryProvider: LoginRepositoryProvider
loginRepositoryProvider: LoginRepositoryProvider,
schedulerProvider: SchedulerProvider
): MilestoneIssuePrUseCase {
return MilestoneIssuePrUseCase(issueRepositoryProvider, issuePrService, loginRepositoryProvider)
return MilestoneIssuePrUseCase(issueRepositoryProvider, issuePrService, loginRepositoryProvider, schedulerProvider)
}
}

View File

@ -0,0 +1,93 @@
package com.fastaccess.github.platform.mentions
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.fastaccess.github.ui.adapter.base.BaseViewHolder
import com.fastaccess.github.usecase.search.FilterSearchUsersUseCase
import com.otaliastudios.autocomplete.RecyclerViewPresenter
import timber.log.Timber
import javax.inject.Inject
/**
* Created by Kosh on 2019-07-18.
*/
class MentionsPresenter @Inject constructor(
c: Context,
private val searchUsersUseCase: FilterSearchUsersUseCase
) : RecyclerViewPresenter<String>(c) {
private val localList = ArrayList<String>()
private val adapter by lazy { MentionsAdapter(this, arrayListOf()) }
override fun instantiateAdapter(): RecyclerView.Adapter<*> = adapter
override fun onQuery(query: CharSequence?) {
Timber.e("$query")
if (!query.isNullOrEmpty()) {
if (localList.firstOrNull { it.contains(query) } != null) {
Timber.e("current list has the query($query)")
// searchUsersUseCase.dispose()
return
}
searchUsersUseCase.keyword = query.toString()
searchUsersUseCase.executeSafely(searchUsersUseCase.buildObservable()
.map { result -> result.second.map { it.login ?: it.name ?: "" } }
.doOnNext {
Timber.e("result($it)")
localList.clear()
setUsers(it)
})
}
}
fun setUsers(newList: List<String>) {
localList.addAll(newList)
adapter.newList(localList.distinct())
}
fun onClick(item: String) = dispatchClick(item)
fun onDispose() = searchUsersUseCase.dispose()
}
class MentionsAdapter(
private val presenter: MentionsPresenter,
private val list: ArrayList<String>
) : RecyclerView.Adapter<MentionsAdapter.MentionsViewHolder>() {
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): MentionsViewHolder = MentionsViewHolder(
LayoutInflater.from(parent.context).inflate(
android.R.layout.simple_list_item_1,
parent, false
)
).apply {
itemView.setOnClickListener { presenter.onClick(list.getOrNull(adapterPosition) ?: "") }
}
override fun getItemCount(): Int = list.size
override fun onBindViewHolder(
holder: MentionsViewHolder,
position: Int
) = holder.bind(list.getOrNull(position) ?: "")
fun newList(newList: List<String>) {
list.clear()
list.addAll(newList)
notifyDataSetChanged()
}
class MentionsViewHolder(view: View) : BaseViewHolder<String>(view) {
override fun bind(item: String) {
itemView.findViewById<TextView>(android.R.id.text1).text = item
}
}
}

View File

@ -6,7 +6,7 @@ import android.view.View
import androidx.core.view.isVisible
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelProviders
import com.fastaccess.extension.uiThread
import com.fastaccess.data.repository.SchedulerProvider
import com.fastaccess.github.R
import com.fastaccess.github.base.BaseFragment
import com.fastaccess.github.base.BaseViewModel
@ -23,15 +23,19 @@ import javax.inject.Inject
*/
class LoginChooserFragment : BaseFragment() {
@Inject lateinit var viewModelFactory: ViewModelProvider.Factory
@Inject lateinit var schedulerProvider: SchedulerProvider
private val viewModel by lazy { ViewModelProviders.of(requireActivity(), viewModelFactory).get(LoginChooserViewModel::class.java) }
private var callback: LoginChooserCallback? = null
private val adapter by lazy {
LoggedInUsersAdapter { user ->
addDisposal(viewModel.reLogin(user)
.uiThread()
.subscribe({ (requireActivity() as? LoginChooserCallback)?.onUserLoggedIn(user) },
{ throwable -> view?.let { showSnackBar(it, message = throwable.message) } }))
addDisposal(
viewModel.reLogin(user)
.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.uiThread())
.subscribe({ (requireActivity() as? LoginChooserCallback)?.onUserLoggedIn(user) },
{ throwable -> view?.let { showSnackBar(it, message = throwable.message) } })
)
}
}
@ -47,8 +51,16 @@ class LoginChooserFragment : BaseFragment() {
override fun viewModel(): BaseViewModel? = null
override fun layoutRes() = R.layout.login_chooser_fragment_layout
override fun onFragmentCreatedWithUser(view: View, savedInstanceState: Bundle?) {}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
override fun onFragmentCreatedWithUser(
view: View,
savedInstanceState: Bundle?
) {
}
override fun onViewCreated(
view: View,
savedInstanceState: Bundle?
) {
super.onViewCreated(view, savedInstanceState)
basicAuth.setOnClickListener { callback?.navToBasicAuth(loginCard) }
accessToken.setOnClickListener { callback?.navToAccessToken(loginCard) }

View File

@ -2,11 +2,11 @@ package com.fastaccess.github.ui.modules.auth.login
import androidx.lifecycle.MutableLiveData
import com.crashlytics.android.Crashlytics
import com.fastaccess.data.persistence.db.FastHubDatabase
import com.fastaccess.data.model.FastHubErrors
import com.fastaccess.data.persistence.models.LoginModel
import com.fastaccess.data.model.ValidationError
import com.fastaccess.extension.uiThread
import com.fastaccess.data.persistence.db.FastHubDatabase
import com.fastaccess.data.persistence.models.LoginModel
import com.fastaccess.data.repository.SchedulerProvider
import com.fastaccess.github.R
import com.fastaccess.github.base.BaseViewModel
import com.fastaccess.github.di.modules.AuthenticationInterceptor
@ -25,17 +25,20 @@ class LoginViewModel @Inject constructor(
private val accessTokenUseCase: GetAccessTokenUseCase,
private val loginWithAccessTokenUseCase: LoginWithAccessTokenUseCase,
private val interceptor: AuthenticationInterceptor,
private val fasthubDatabase: FastHubDatabase
private val fasthubDatabase: FastHubDatabase,
private val schedulerProvider: SchedulerProvider
) : BaseViewModel() {
val validationLiveData = MutableLiveData<ValidationError>()
val loggedInUser = MutableLiveData<LoginModel>()
fun login(userName: String? = null,
password: String? = null,
twoFactorCode: String? = null,
endPoint: String? = null,
isAccessToken: Boolean = true) {
fun login(
userName: String? = null,
password: String? = null,
twoFactorCode: String? = null,
endPoint: String? = null,
isAccessToken: Boolean = true
) {
validationLiveData.value = ValidationError(ValidationError.FieldType.TWO_FACTOR, !twoFactorCode.isNullOrBlank())
validationLiveData.value = ValidationError(ValidationError.FieldType.URL, !endPoint.isNullOrBlank())
validationLiveData.value = ValidationError(ValidationError.FieldType.PASSWORD, !password.isNullOrBlank())
@ -59,10 +62,12 @@ class LoginViewModel @Inject constructor(
}
}
private fun loginWithAccessToken(password: String,
twoFactorCode: String? = null,
isEnterprise: Boolean? = false,
enterpriseUrl: String? = null) {
private fun loginWithAccessToken(
password: String,
twoFactorCode: String? = null,
isEnterprise: Boolean? = false,
enterpriseUrl: String? = null
) {
interceptor.token = password
loginWithAccessTokenUseCase.executeSafely(callApi(loginWithAccessTokenUseCase.buildObservable()
.flatMap { user ->
@ -77,9 +82,11 @@ class LoginViewModel @Inject constructor(
))
}
private fun loginBasic(twoFactorCode: String? = null,
isEnterprise: Boolean? = false,
enterpriseUrl: String? = null) {
private fun loginBasic(
twoFactorCode: String? = null,
isEnterprise: Boolean? = false,
enterpriseUrl: String? = null
) {
loginUserCase.setAuthBody(twoFactorCode)
loginUserCase.executeSafely(callApi(loginUserCase.buildObservable()
.flatMap({
@ -104,5 +111,7 @@ class LoginViewModel @Inject constructor(
super.onCleared()
}
fun clearDb() = Completable.fromCallable { fasthubDatabase.clearAll() }.uiThread()
fun clearDb() = Completable.fromCallable { fasthubDatabase.clearAll() }
.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.uiThread())
}

View File

@ -2,6 +2,7 @@ package com.fastaccess.github.ui.modules.issue.fragment
import android.graphics.Color
import android.os.Bundle
import android.text.Editable
import android.view.View
import androidx.core.os.bundleOf
import androidx.core.view.isVisible
@ -21,10 +22,8 @@ import com.fastaccess.github.R
import com.fastaccess.github.base.BaseFragment
import com.fastaccess.github.base.BaseViewModel
import com.fastaccess.github.base.engine.ThemeEngine
import com.fastaccess.github.extensions.isTrue
import com.fastaccess.github.extensions.observeNotNull
import com.fastaccess.github.extensions.shareUrl
import com.fastaccess.github.extensions.timeAgo
import com.fastaccess.github.extensions.*
import com.fastaccess.github.platform.mentions.MentionsPresenter
import com.fastaccess.github.ui.adapter.IssueTimelineAdapter
import com.fastaccess.github.ui.modules.issue.fragment.viewmodel.IssueTimelineViewModel
import com.fastaccess.github.ui.modules.issuesprs.edit.LockUnlockFragment
@ -43,6 +42,9 @@ import com.fastaccess.markdown.MarkdownProvider
import com.fastaccess.markdown.spans.LabelSpan
import com.fastaccess.markdown.widget.SpannableBuilder
import com.google.android.material.appbar.AppBarLayout
import com.otaliastudios.autocomplete.Autocomplete
import com.otaliastudios.autocomplete.AutocompleteCallback
import com.otaliastudios.autocomplete.CharPolicy
import github.type.LockReason
import kotlinx.android.synthetic.main.empty_state_layout.*
import kotlinx.android.synthetic.main.issue_header_row_item.*
@ -61,6 +63,7 @@ class IssueFragment : BaseFragment(), LockUnlockFragment.OnLockReasonSelected,
@Inject lateinit var viewModelFactory: ViewModelProvider.Factory
@Inject lateinit var htmlSpanner: HtmlSpanner
@Inject lateinit var preference: FastHubSharedPreference
@Inject lateinit var mentionsPresenter: MentionsPresenter
private val viewModel by lazy { ViewModelProviders.of(this, viewModelFactory).get(IssueTimelineViewModel::class.java) }
private val login by lazy { arguments?.getString(EXTRA) ?: "" }
@ -97,9 +100,15 @@ class IssueFragment : BaseFragment(), LockUnlockFragment.OnLockReasonSelected,
swipeRefresh.isRefreshing = false
}
}
setupEditText()
observeChanges()
}
override fun onDestroyView() {
mentionsPresenter.onDispose()
super.onDestroyView()
}
override fun onLockReasonSelected(lockReason: LockReason?) {
viewModel.lockUnlockIssue(login, repo, number, lockReason, true)
}
@ -120,6 +129,29 @@ class IssueFragment : BaseFragment(), LockUnlockFragment.OnLockReasonSelected,
initMilestone(milestone)
}
private fun setupEditText() {
Autocomplete.on<String>(commentText)
.with(CharPolicy('@'))
.with(mentionsPresenter)
.with(requireContext().getDrawableCompat(R.drawable.popup_window_background))
.with(object : AutocompleteCallback<String?> {
override fun onPopupItemClicked(
editable: Editable?,
item: String?
): Boolean {
val range = CharPolicy.getQueryRange(editable) ?: return false
val start = range[0]
val end = range[1]
Timber.e("$start $end $item")
editable?.replace(start, end, "$item ")
return true
}
override fun onPopupVisibilityChanged(shown: Boolean) {}
})
.build()
}
private fun menuClick(model: IssueModel) {
toolbar.setOnMenuItemClickListener { item ->
when (item.itemId) {
@ -160,8 +192,11 @@ class IssueFragment : BaseFragment(), LockUnlockFragment.OnLockReasonSelected,
viewModel.getIssue(login, repo, number).observeNotNull(this) {
initIssue(it.first, it.second)
}
viewModel.timeline.observeNotNull(this) {
adapter.submitList(it)
viewModel.timeline.observeNotNull(this) { timeline ->
adapter.submitList(timeline)
}
viewModel.userNamesLiveData.observeNotNull(this) {
mentionsPresenter.setUsers(it)
}
}

View File

@ -32,6 +32,7 @@ class IssueTimelineViewModel @Inject constructor(
private var pageInfo: PageInfoModel? = null
val timeline = MutableLiveData<ArrayList<TimelineModel>>()
private val list = arrayListOf<TimelineModel>()
val userNamesLiveData = MutableLiveData<ArrayList<String>>()
override fun onCleared() {
super.onCleared()
@ -41,11 +42,20 @@ class IssueTimelineViewModel @Inject constructor(
lockUnlockIssuePrUseCase.dispose()
}
fun getIssue(login: String, repo: String, number: Int) = issueRepositoryProvider.getIssueByNumber("$login/$repo", number)
fun getIssue(
login: String,
repo: String,
number: Int
) = issueRepositoryProvider.getIssueByNumber("$login/$repo", number)
.filterNull()
.map { Pair(it, loginRepositoryProvider.getLoginBlocking()) }
fun loadData(login: String, repo: String, number: Int, reload: Boolean = false) {
fun loadData(
login: String,
repo: String,
number: Int,
reload: Boolean = false
) {
if (reload) {
pageInfo = null
list.clear()
@ -58,13 +68,20 @@ class IssueTimelineViewModel @Inject constructor(
issueUseCase.repo = repo
issueUseCase.number = number
justSubscribe(issueUseCase.buildObservable()
.flatMap { loadTimeline(login, repo, number, cursor) })
.flatMap { loadTimeline(login, repo, number, cursor) }
.map { mapToUserNames(it.second) })
} else {
justSubscribe(loadTimeline(login, repo, number, cursor))
justSubscribe(loadTimeline(login, repo, number, cursor)
.map { mapToUserNames(it.second) })
}
}
private fun loadTimeline(login: String, repo: String, number: Int, cursor: String?): Observable<Pair<PageInfoModel, List<TimelineModel>>> {
private fun loadTimeline(
login: String,
repo: String,
number: Int,
cursor: String?
): Observable<Pair<PageInfoModel, List<TimelineModel>>> {
timelineUseCase.login = login
timelineUseCase.repo = repo
timelineUseCase.number = number
@ -77,7 +94,11 @@ class IssueTimelineViewModel @Inject constructor(
}
}
fun closeOpenIssue(login: String, repo: String, number: Int) {
fun closeOpenIssue(
login: String,
repo: String,
number: Int
) {
editIssuePrUseCase.repo = repo
editIssuePrUseCase.login = login
editIssuePrUseCase.number = number
@ -87,7 +108,13 @@ class IssueTimelineViewModel @Inject constructor(
})
}
fun lockUnlockIssue(login: String, repo: String, number: Int, lockReason: LockReason? = null, lock: Boolean = false) {
fun lockUnlockIssue(
login: String,
repo: String,
number: Int,
lockReason: LockReason? = null,
lock: Boolean = false
) {
lockUnlockIssuePrUseCase.repo = repo
lockUnlockIssuePrUseCase.login = login
lockUnlockIssuePrUseCase.number = number
@ -105,4 +132,10 @@ class IssueTimelineViewModel @Inject constructor(
}
fun hasNext() = pageInfo?.hasNextPage ?: false
private fun mapToUserNames(list: List<TimelineModel>) {
val _list = userNamesLiveData.value ?: arrayListOf()
_list.addAll(list.map { it.comment?.author?.login ?: it.comment?.author?.name ?: "" })
userNamesLiveData.postValue(_list)
}
}

View File

@ -145,7 +145,7 @@ class MainFragment : BaseFragment(), IconDialogFragment.IconDialogClickListener
swipeRefresh.isRefreshing = it == true
}
viewModel.list.observeNotNull(this) {
adapter.submitList(it.distinct())
adapter.submitList(it)
}
viewModel.logoutProcess.observeNotNull(this) {
if (it) {

View File

@ -6,11 +6,7 @@ import com.fastaccess.data.model.MainScreenModel
import com.fastaccess.data.model.MainScreenModelRowType
import com.fastaccess.data.persistence.db.FastHubDatabase
import com.fastaccess.data.persistence.models.FeedModel
import com.fastaccess.data.repository.FeedsRepositoryProvider
import com.fastaccess.data.repository.LoginRepositoryProvider
import com.fastaccess.data.repository.MyIssuesPullsRepositoryProvider
import com.fastaccess.data.repository.NotificationRepositoryProvider
import com.fastaccess.extension.uiThread
import com.fastaccess.data.repository.*
import com.fastaccess.github.base.BaseViewModel
import com.fastaccess.github.extensions.map
import com.fastaccess.github.extensions.switchMap
@ -34,7 +30,8 @@ class MainFragmentViewModel @Inject constructor(
private val myIssuesPullsRepo: MyIssuesPullsRepositoryProvider,
private val notificationRepositoryProvider: NotificationRepositoryProvider,
private val fasthubDatabase: FastHubDatabase,
private val loginProvider: LoginRepositoryProvider
private val loginProvider: LoginRepositoryProvider,
private val schedulerProvider: SchedulerProvider
) : BaseViewModel() {
val login = loginProvider.getLogin()
@ -55,7 +52,7 @@ class MainFragmentViewModel @Inject constructor(
.flatMap { notificationUseCase.buildObservable() }
.flatMap { issuesMainScreenUseCase.buildObservable() }
.flatMap { pullRequestsMainScreenUseCase.buildObservable() }
))
).subscribeOn(schedulerProvider.ioThread()).observeOn(schedulerProvider.uiThread()))
}
fun logout() {
@ -63,7 +60,8 @@ class MainFragmentViewModel @Inject constructor(
fasthubDatabase.clearAll()
loginProvider.logoutAll()
}
.uiThread()
.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.uiThread())
.subscribe({ logoutProcess.postValue(true) },
{ error.postValue(FastHubErrors(FastHubErrors.ErrorType.OTHER, it.message)) })
)

View File

@ -1,7 +1,6 @@
package com.fastaccess.github.ui.modules.profile.fragment.viewmodel
import androidx.lifecycle.MutableLiveData
import com.fastaccess.extension.uiThread
import com.fastaccess.github.base.BaseViewModel
import com.fastaccess.github.usecase.user.BlockUnblockUserUseCase
import com.fastaccess.github.usecase.user.FollowUnfollowUserUseCase
@ -26,30 +25,38 @@ class ProfileViewModel @Inject constructor(
fun getUserFromRemote(login: String) {
userUseCase.login = login
add(callApi(userUseCase.buildObservable())
.subscribe({}, { it.printStackTrace() }))
add(
callApi(userUseCase.buildObservable())
.subscribe({}, { it.printStackTrace() })
)
}
fun checkBlockingState(login: String) {
blockedUseCase.login = login
add(blockedUseCase.buildObservable()
.uiThread()
.subscribe({
isBlocked.postValue(it)
}, {
isBlocked.postValue(false)
it.printStackTrace()
}))
add(
blockedUseCase.buildObservable()
.subscribe({
isBlocked.postValue(it)
}, {
isBlocked.postValue(false)
it.printStackTrace()
})
)
}
fun blockUnblockUser(login: String) {
unblockUserUseCase.login = login
unblockUserUseCase.block = isBlocked.value == false
add(callApi(unblockUserUseCase.buildObservable())
.subscribe({ isBlocked.postValue(it) }, { it.printStackTrace() }))
add(
callApi(unblockUserUseCase.buildObservable())
.subscribe({ isBlocked.postValue(it) }, { it.printStackTrace() })
)
}
fun followUnfollowUser(login: String, viewerIsFollowing: Boolean?) {
fun followUnfollowUser(
login: String,
viewerIsFollowing: Boolean?
) {
followUnfollowUserUseCase.login = login
followUnfollowUserUseCase.follow = viewerIsFollowing == false
justSubscribe(followUnfollowUserUseCase.buildObservable())

View File

@ -6,7 +6,7 @@ import android.view.View
import androidx.core.os.bundleOf
import com.fastaccess.data.model.LanguageColorsModel
import com.fastaccess.data.model.parcelable.FilterTrendingModel
import com.fastaccess.extension.uiThread
import com.fastaccess.data.repository.SchedulerProvider
import com.fastaccess.github.R
import com.fastaccess.github.base.BaseFragment
import com.fastaccess.github.base.BaseViewModel
@ -24,6 +24,7 @@ import javax.inject.Inject
class FilterTrendingBottomSheet : BaseFragment() {
@Inject lateinit var gson: Gson
@Inject lateinit var schedulerProvider: SchedulerProvider
private var callback: FilterTrendingCallback? = null
private val adapter by lazy { LanguagesAdapter() }
@ -48,19 +49,26 @@ class FilterTrendingBottomSheet : BaseFragment() {
super.onDetach()
}
override fun onFragmentCreatedWithUser(view: View, savedInstanceState: Bundle?) {
override fun onFragmentCreatedWithUser(
view: View,
savedInstanceState: Bundle?
) {
setupToolbar(R.string.filter)
adapter.checkedLanguage = model.lang
languageRecyclerView.adapter = adapter
languageRecyclerView.addDivider()
sinceGroup.check(when (model.since) {
FilterTrendingModel.TrendingSince.DAILY -> R.id.daily
FilterTrendingModel.TrendingSince.WEEKLY -> R.id.weekly
FilterTrendingModel.TrendingSince.MONTHLY -> R.id.monthly
})
sinceGroup.check(
when (model.since) {
FilterTrendingModel.TrendingSince.DAILY -> R.id.daily
FilterTrendingModel.TrendingSince.WEEKLY -> R.id.weekly
FilterTrendingModel.TrendingSince.MONTHLY -> R.id.monthly
}
)
addDisposal(LanguageColorsModel.newInstance(gson, requireContext())
.uiThread()
.subscribe({ adapter.submitList(it) }, { showSnackBar(view, message = it.message) }))
.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.uiThread())
.subscribe({ adapter.submitList(it) }, { showSnackBar(view, message = it.message) })
)
submit.setOnClickListener {
model.lang = adapter.checkedLanguage

View File

@ -1,6 +1,7 @@
package com.fastaccess.github.usecase.auth
import com.fastaccess.data.repository.LoginRepositoryProvider
import com.fastaccess.data.repository.SchedulerProvider
import com.fastaccess.domain.response.AccessTokenResponse
import com.fastaccess.domain.usecase.base.BaseObservableUseCase
import com.fastaccess.github.BuildConfig
@ -11,14 +12,25 @@ import javax.inject.Inject
/**
* Created by Kosh on 12.05.18.
*/
class GetAccessTokenUseCase @Inject constructor(private val loginRemoteRepository: LoginRepositoryProvider) :
BaseObservableUseCase() {
class GetAccessTokenUseCase @Inject constructor(
private val loginRemoteRepository: LoginRepositoryProvider,
private val schedulerProvider: SchedulerProvider
) :
BaseObservableUseCase() {
var code: String? = null
override fun buildObservable(): Observable<AccessTokenResponse> = code?.let {
loginRemoteRepository.getAccessToken(it, BuildConfig.GITHUB_CLIENT_ID, BuildConfig.GITHUB_SECRET,
BuildConfig.APPLICATION_ID, REDIRECT_URL)
} ?: Observable.empty()
override fun buildObservable(): Observable<AccessTokenResponse> {
val observable = code?.let {
loginRemoteRepository.getAccessToken(
it, BuildConfig.GITHUB_CLIENT_ID, BuildConfig.GITHUB_SECRET,
BuildConfig.APPLICATION_ID, REDIRECT_URL
)
} ?: Observable.empty()
return observable
.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.uiThread())
}
}

View File

@ -2,6 +2,7 @@ package com.fastaccess.github.usecase.auth
import com.fastaccess.data.persistence.models.LoginModel
import com.fastaccess.data.repository.LoginRepositoryProvider
import com.fastaccess.data.repository.SchedulerProvider
import com.fastaccess.domain.usecase.base.BaseObservableUseCase
import com.google.gson.Gson
import io.reactivex.Observable
@ -10,10 +11,14 @@ import javax.inject.Inject
/**
* Created by Kosh on 12.05.18.
*/
class LoginWithAccessTokenUseCase @Inject constructor(private val loginRemoteRepository: LoginRepositoryProvider,
private val gson: Gson) : BaseObservableUseCase() {
class LoginWithAccessTokenUseCase @Inject constructor(
private val loginRemoteRepository: LoginRepositoryProvider,
private val gson: Gson,
private val schedulerProvider: SchedulerProvider
) : BaseObservableUseCase() {
override fun buildObservable(): Observable<LoginModel> = loginRemoteRepository.loginAccessToken()
.map { gson.fromJson(gson.toJson(it), LoginModel::class.java) }
.map { gson.fromJson(gson.toJson(it), LoginModel::class.java) }
fun insertUser(loginModel: LoginModel): Observable<LoginModel?> = Observable.fromCallable {
loginRemoteRepository.logoutAll()
@ -24,5 +29,6 @@ class LoginWithAccessTokenUseCase @Inject constructor(private val loginRemoteRep
} else {
null
}
}
}.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.uiThread())
}

View File

@ -1,6 +1,7 @@
package com.fastaccess.github.usecase.feed
import com.fastaccess.data.repository.FeedsRepositoryProvider
import com.fastaccess.data.repository.SchedulerProvider
import com.fastaccess.domain.response.FeedResponse
import com.fastaccess.domain.response.PageableResponse
import com.fastaccess.domain.usecase.base.BaseObservableUseCase
@ -10,7 +11,12 @@ import javax.inject.Inject
/**
* Created by Kosh on 26.06.18.
*/
class FeedsUseCase @Inject constructor(private val feedsRepositoryProvider: FeedsRepositoryProvider) : BaseObservableUseCase() {
class FeedsUseCase @Inject constructor(
private val feedsRepositoryProvider: FeedsRepositoryProvider,
private val schedulerProvider: SchedulerProvider
) : BaseObservableUseCase() {
var page: Int = 0
override fun buildObservable(): Observable<PageableResponse<FeedResponse>> = feedsRepositoryProvider.getReceivedEvents(page)
.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.uiThread())
}

View File

@ -1,5 +1,6 @@
package com.fastaccess.github.usecase.issuesprs
import com.fastaccess.data.repository.SchedulerProvider
import com.fastaccess.domain.repository.services.RepoService
import com.fastaccess.domain.response.body.AssigneesBodyModel
import com.fastaccess.domain.usecase.base.BaseObservableUseCase
@ -10,7 +11,8 @@ import javax.inject.Inject
* Created by Kosh on 16.02.19.
*/
class AddAssigneesUseCase @Inject constructor(
private val repoService: RepoService
private val repoService: RepoService,
private val schedulerProvider: SchedulerProvider
) : BaseObservableUseCase() {
var repo: String = ""
@ -20,8 +22,13 @@ class AddAssigneesUseCase @Inject constructor(
var toRemove: List<String>? = null
override fun buildObservable(): Observable<Boolean> = when (toRemove.isNullOrEmpty()) {
true -> repoService.addAssignees(login, repo, number, AssigneesBodyModel(assignees)).map { true }
true -> repoService.addAssignees(login, repo, number, AssigneesBodyModel(assignees))
.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.uiThread())
.map { true }
else -> repoService.removeAssignees(login, repo, number, AssigneesBodyModel(toRemove))
.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.uiThread())
.flatMap { repoService.addAssignees(login, repo, number, AssigneesBodyModel(assignees)) }
.map { true }
}

View File

@ -5,6 +5,7 @@ import com.fastaccess.data.model.TimelineModel
import com.fastaccess.data.persistence.models.MyIssuesPullsModel
import com.fastaccess.data.repository.IssueRepositoryProvider
import com.fastaccess.data.repository.LoginRepositoryProvider
import com.fastaccess.data.repository.SchedulerProvider
import com.fastaccess.domain.repository.services.IssuePrService
import com.fastaccess.domain.response.IssueRequestModel
import com.fastaccess.domain.usecase.base.BaseObservableUseCase
@ -19,7 +20,8 @@ import javax.inject.Inject
class CloseOpenIssuePrUseCase @Inject constructor(
private val issueRepositoryProvider: IssueRepositoryProvider,
private val issuePrService: IssuePrService,
private val loginRepositoryProvider: LoginRepositoryProvider
private val loginRepositoryProvider: LoginRepositoryProvider,
private val schedulerProvider: SchedulerProvider
) : BaseObservableUseCase() {
var repo: String = ""
@ -27,14 +29,22 @@ class CloseOpenIssuePrUseCase @Inject constructor(
var number: Int = -1
override fun buildObservable(): Observable<TimelineModel> = issueRepositoryProvider.getIssueByNumberMaybe("$login/$repo", number)
.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.uiThread())
.flatMapObservable { issue ->
issuePrService.editIssue(login, repo, number, IssueRequestModel(state = if ("closed".equals(issue.state, true)) "open" else "closed"))
.map {
issue.state = it.issueState
issueRepositoryProvider.upsert(issue)
val me = loginRepositoryProvider.getLoginBlocking()?.me()
return@map TimelineModel(closeOpenEventModel = CloseOpenEventModel(Date(), me, null,
MyIssuesPullsModel(issue.id, issue.databaseId, issue.number, issue.title, issue.repo?.nameWithOwner, 0, it.issueState, issue.url)))
return@map TimelineModel(
closeOpenEventModel = CloseOpenEventModel(
Date(), me, null,
MyIssuesPullsModel(
issue.id, issue.databaseId, issue.number, issue.title, issue.repo?.nameWithOwner, 0, it.issueState, issue.url
)
)
)
}
}
}

View File

@ -1,6 +1,7 @@
package com.fastaccess.github.usecase.issuesprs
import com.fastaccess.data.model.parcelable.LabelModel
import com.fastaccess.data.repository.SchedulerProvider
import com.fastaccess.domain.repository.services.RepoService
import com.fastaccess.domain.response.LabelResponse
import com.fastaccess.domain.usecase.base.BaseObservableUseCase
@ -11,7 +12,8 @@ import javax.inject.Inject
* Created by Kosh on 16.02.19.
*/
class CreateLabelUseCase @Inject constructor(
private val repoService: RepoService
private val repoService: RepoService,
private val schedulerProvider: SchedulerProvider
) : BaseObservableUseCase() {
var repo: String = ""
@ -20,6 +22,8 @@ class CreateLabelUseCase @Inject constructor(
var color: String = ""
override fun buildObservable(): Observable<LabelModel> = repoService.addLabel(login, repo, LabelResponse(color = color, name = name))
.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.uiThread())
.map {
LabelModel(it.name, it.color, it.url)
}

View File

@ -1,5 +1,6 @@
package com.fastaccess.github.usecase.issuesprs
import com.fastaccess.data.repository.SchedulerProvider
import com.fastaccess.domain.repository.services.RepoService
import com.fastaccess.domain.response.body.MilestoneBodyModel
import com.fastaccess.domain.usecase.base.BaseObservableUseCase
@ -10,7 +11,8 @@ import javax.inject.Inject
* Created by Kosh on 16.02.19.
*/
class CreateMilestoneUseCase @Inject constructor(
private val repoService: RepoService
private val repoService: RepoService,
private val schedulerProvider: SchedulerProvider
) : BaseObservableUseCase() {
var repo: String = ""
@ -20,5 +22,7 @@ class CreateMilestoneUseCase @Inject constructor(
var dueOn: String = ""
override fun buildObservable(): Observable<Boolean> = repoService.createMilestone(login, repo, MilestoneBodyModel(title, description, dueOn))
.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.uiThread())
.map { true }
}

View File

@ -7,6 +7,7 @@ import com.fastaccess.data.model.PageInfoModel
import com.fastaccess.data.model.parcelable.FilterIssuesPrsModel
import com.fastaccess.data.persistence.models.MyIssuesPullsModel
import com.fastaccess.data.repository.LoginRepositoryProvider
import com.fastaccess.data.repository.SchedulerProvider
import com.fastaccess.domain.usecase.base.BaseObservableUseCase
import github.SearchIssuesQuery
import io.reactivex.Observable
@ -17,7 +18,8 @@ import javax.inject.Inject
*/
class FilterIssuesUseCase @Inject constructor(
private val loginRepository: LoginRepositoryProvider,
private val apolloClient: ApolloClient
private val apolloClient: ApolloClient,
private val schedulerProvider: SchedulerProvider
) : BaseObservableUseCase() {
var cursor: Input<String?> = Input.absent()
@ -28,6 +30,8 @@ class FilterIssuesUseCase @Inject constructor(
val query = keyword
return if (query.isNullOrEmpty()) {
loginRepository.getLogin()
.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.uiThread())
.flatMapObservable { user ->
return@flatMapObservable user.login?.let { login ->
searchObservable(login = login)
@ -43,6 +47,8 @@ class FilterIssuesUseCase @Inject constructor(
query: String? = null
): Observable<Pair<PageInfoModel, List<MyIssuesPullsModel>>> {
return Rx2Apollo.from(apolloClient.query(SearchIssuesQuery(constructQuery(filterModel, login, query), cursor)))
.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.uiThread())
.map { it.data()?.search }
.map { search ->
val list = search.nodes?.asSequence()?.mapNotNull { it.fragments.shortIssueRowItem }

View File

@ -7,6 +7,7 @@ import com.fastaccess.data.model.PageInfoModel
import com.fastaccess.data.model.parcelable.FilterIssuesPrsModel
import com.fastaccess.data.persistence.models.MyIssuesPullsModel
import com.fastaccess.data.repository.LoginRepositoryProvider
import com.fastaccess.data.repository.SchedulerProvider
import com.fastaccess.domain.usecase.base.BaseObservableUseCase
import github.SearchPullRequestsQuery
import io.reactivex.Observable
@ -17,7 +18,8 @@ import javax.inject.Inject
*/
class FilterPullRequestsUseCase @Inject constructor(
private val loginRepository: LoginRepositoryProvider,
private val apolloClient: ApolloClient
private val apolloClient: ApolloClient,
private val schedulerProvider: SchedulerProvider
) : BaseObservableUseCase() {
var cursor: Input<String?> = Input.absent()
@ -28,6 +30,8 @@ class FilterPullRequestsUseCase @Inject constructor(
val query = keyword
return if (query.isNullOrEmpty()) {
loginRepository.getLogin()
.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.uiThread())
.flatMapObservable { user ->
return@flatMapObservable user.login?.let { login ->
searchObservable(login = login)
@ -38,18 +42,27 @@ class FilterPullRequestsUseCase @Inject constructor(
}
}
private fun searchObservable(login: String? = null, query: String? = null): Observable<Pair<PageInfoModel, List<MyIssuesPullsModel>>> {
private fun searchObservable(
login: String? = null,
query: String? = null
): Observable<Pair<PageInfoModel, List<MyIssuesPullsModel>>> {
return Rx2Apollo.from(apolloClient.query(SearchPullRequestsQuery(constructQuery(filterModel, login, query), cursor)))
.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.uiThread())
.map { it.data()?.search }
.map { search ->
val list = search.nodes?.asSequence()?.mapNotNull { it.fragments.shortPullRequestRowItem }
?.map {
MyIssuesPullsModel(it.id, it.databaseId, it.number, it.title,
it.repository.nameWithOwner, it.comments.totalCount, it.state.name, it.url.toString(), true)
MyIssuesPullsModel(
it.id, it.databaseId, it.number, it.title,
it.repository.nameWithOwner, it.comments.totalCount, it.state.name, it.url.toString(), true
)
}
?.toList() ?: arrayListOf()
val pageInfo = PageInfoModel(search.pageInfo.startCursor, search.pageInfo.endCursor,
search.pageInfo.isHasNextPage, search.pageInfo.isHasPreviousPage)
val pageInfo = PageInfoModel(
search.pageInfo.startCursor, search.pageInfo.endCursor,
search.pageInfo.isHasNextPage, search.pageInfo.isHasPreviousPage
)
return@map Pair(pageInfo, list)
}
}
@ -57,42 +70,54 @@ class FilterPullRequestsUseCase @Inject constructor(
/**
* Example: is:open is:pr author:k0shk0sh archived:false sort:created-desc
*/
private fun constructQuery(model: FilterIssuesPrsModel, login: String? = null, query: String? = null): String {
private fun constructQuery(
model: FilterIssuesPrsModel,
login: String? = null,
query: String? = null
): String {
return StringBuilder()
.append(if (query.isNullOrEmpty()) "" else "$query ")
.append("is:${when (model.searchType) {
FilterIssuesPrsModel.SearchType.OPEN -> "open"
FilterIssuesPrsModel.SearchType.CLOSED -> "closed"
}}")
.append(
"is:${when (model.searchType) {
FilterIssuesPrsModel.SearchType.OPEN -> "open"
FilterIssuesPrsModel.SearchType.CLOSED -> "closed"
}}"
)
.append(" ")
.append("is:pr")
.append(" ")
.append("archived:false")
.append(" ")
.append("sort:${when (model.searchSortBy) {
FilterIssuesPrsModel.SearchSortBy.NEWEST -> "created-desc"
FilterIssuesPrsModel.SearchSortBy.OLDEST -> "created-asc"
FilterIssuesPrsModel.SearchSortBy.MOST_COMMENTED -> "comments-desc"
FilterIssuesPrsModel.SearchSortBy.LEAST_COMMENTED -> "comments-asc"
FilterIssuesPrsModel.SearchSortBy.RECENTLY_UPDATED -> "updated-desc"
FilterIssuesPrsModel.SearchSortBy.LEAST_RECENTLY_UPDATED -> "updated-asc"
}}")
.append(
"sort:${when (model.searchSortBy) {
FilterIssuesPrsModel.SearchSortBy.NEWEST -> "created-desc"
FilterIssuesPrsModel.SearchSortBy.OLDEST -> "created-asc"
FilterIssuesPrsModel.SearchSortBy.MOST_COMMENTED -> "comments-desc"
FilterIssuesPrsModel.SearchSortBy.LEAST_COMMENTED -> "comments-asc"
FilterIssuesPrsModel.SearchSortBy.RECENTLY_UPDATED -> "updated-desc"
FilterIssuesPrsModel.SearchSortBy.LEAST_RECENTLY_UPDATED -> "updated-asc"
}}"
)
.append(" ")
.apply {
if (!login.isNullOrEmpty()) {
append("${when (model.searchBy) {
FilterIssuesPrsModel.SearchBy.ASSIGNED -> "assignee"
FilterIssuesPrsModel.SearchBy.MENTIONED -> "mentions"
FilterIssuesPrsModel.SearchBy.REVIEW_REQUESTS -> "review-requested"
else -> "author"
}}:$login").append(" ")
append(
"${when (model.searchBy) {
FilterIssuesPrsModel.SearchBy.ASSIGNED -> "assignee"
FilterIssuesPrsModel.SearchBy.MENTIONED -> "mentions"
FilterIssuesPrsModel.SearchBy.REVIEW_REQUESTS -> "review-requested"
else -> "author"
}}:$login"
).append(" ")
}
if (model.searchVisibility != FilterIssuesPrsModel.SearchVisibility.BOTH) {
append("is:${when (model.searchVisibility) {
FilterIssuesPrsModel.SearchVisibility.PUBLIC -> "public"
FilterIssuesPrsModel.SearchVisibility.PRIVATE -> "private"
else -> ""
}}")
append(
"is:${when (model.searchVisibility) {
FilterIssuesPrsModel.SearchVisibility.PUBLIC -> "public"
FilterIssuesPrsModel.SearchVisibility.PRIVATE -> "private"
else -> ""
}}"
)
}
}
.toString()

View File

@ -5,6 +5,7 @@ import com.apollographql.apollo.api.Input
import com.apollographql.apollo.rx2.Rx2Apollo
import com.fastaccess.data.model.PageInfoModel
import com.fastaccess.data.model.ShortUserModel
import com.fastaccess.data.repository.SchedulerProvider
import com.fastaccess.domain.usecase.base.BaseObservableUseCase
import com.fastaccess.extension.toUser
import github.GetAssigneesQuery
@ -15,7 +16,8 @@ import javax.inject.Inject
* Created by Kosh on 27.01.19.
*/
class GetAssigneesUseCase @Inject constructor(
private val apolloClient: ApolloClient
private val apolloClient: ApolloClient,
private val schedulerProvider: SchedulerProvider
) : BaseObservableUseCase() {
var login: String? = null
@ -30,10 +32,14 @@ class GetAssigneesUseCase @Inject constructor(
return Observable.error(Throwable("this should never happen ;)"))
}
return Rx2Apollo.from(apolloClient.query(GetAssigneesQuery(login, repo, page)))
.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.uiThread())
.map { it.data()?.repositoryOwner?.repository?.assignableUsers }
.map { data ->
val pageInfo = PageInfoModel(data.pageInfo.startCursor, data.pageInfo.endCursor,
data.pageInfo.isHasNextPage, data.pageInfo.isHasPreviousPage)
val pageInfo = PageInfoModel(
data.pageInfo.startCursor, data.pageInfo.endCursor,
data.pageInfo.isHasNextPage, data.pageInfo.isHasPreviousPage
)
return@map Pair(pageInfo, data.nodes?.map { it.fragments.shortUserRowItem.toUser() } ?: listOf())
}
}

View File

@ -6,6 +6,7 @@ import com.apollographql.apollo.rx2.Rx2Apollo
import com.fastaccess.data.model.*
import com.fastaccess.data.model.parcelable.LabelModel
import com.fastaccess.data.repository.IssueRepositoryProvider
import com.fastaccess.data.repository.SchedulerProvider
import com.fastaccess.domain.usecase.base.BaseObservableUseCase
import com.fastaccess.extension.*
import com.fastaccess.github.extensions.addIfNotNull
@ -19,7 +20,8 @@ import javax.inject.Inject
*/
class GetIssueTimelineUseCase @Inject constructor(
private val issueRepositoryProvider: IssueRepositoryProvider,
private val apolloClient: ApolloClient
private val apolloClient: ApolloClient,
private val schedulerProvider: SchedulerProvider
) : BaseObservableUseCase() {
var login: String? = null
@ -37,12 +39,16 @@ class GetIssueTimelineUseCase @Inject constructor(
}
return Rx2Apollo.from(apolloClient.query(GetIssueTimelineQuery(login, repo, number, page)))
.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.uiThread())
.map { it.data()?.repositoryOwner?.repository?.issue }
.map {
val list = arrayListOf<TimelineModel>()
val timeline = it.timeline
val pageInfo = PageInfoModel(timeline.pageInfo.startCursor, timeline.pageInfo.endCursor,
timeline.pageInfo.isHasNextPage, timeline.pageInfo.isHasPreviousPage)
val pageInfo = PageInfoModel(
timeline.pageInfo.startCursor, timeline.pageInfo.endCursor,
timeline.pageInfo.isHasNextPage, timeline.pageInfo.isHasPreviousPage
)
timeline.nodes?.forEach { node ->
when (node) {
is AsCommit -> list.add(getCommit(node))
@ -69,21 +75,29 @@ class GetIssueTimelineUseCase @Inject constructor(
}
}
private fun getTransferred(node: AsTransferredEvent): TimelineModel = TimelineModel(transferredEventModel = TransferredEventModel(
node.createdAt, node.actor?.fragments?.shortActor?.toUser(), node.fromRepository?.nameWithOwner
))
private fun getTransferred(node: AsTransferredEvent): TimelineModel = TimelineModel(
transferredEventModel = TransferredEventModel(
node.createdAt, node.actor?.fragments?.shortActor?.toUser(), node.fromRepository?.nameWithOwner
)
)
private fun getRenamed(node: AsRenamedTitleEvent): TimelineModel = TimelineModel(renamedEventModel = RenamedEventModel(
node.createdAt, node.actor?.fragments?.shortActor?.toUser(), node.currentTitle, node.previousTitle
))
private fun getRenamed(node: AsRenamedTitleEvent): TimelineModel = TimelineModel(
renamedEventModel = RenamedEventModel(
node.createdAt, node.actor?.fragments?.shortActor?.toUser(), node.currentTitle, node.previousTitle
)
)
private fun getDemilestoned(node: AsDemilestonedEvent): TimelineModel = TimelineModel(milestoneEventModel = MilestoneDemilestonedEventModel(
node.createdAt, node.actor?.fragments?.shortActor?.toUser(), node.milestoneTitle, false
))
private fun getDemilestoned(node: AsDemilestonedEvent): TimelineModel = TimelineModel(
milestoneEventModel = MilestoneDemilestonedEventModel(
node.createdAt, node.actor?.fragments?.shortActor?.toUser(), node.milestoneTitle, false
)
)
private fun getMilestone(node: AsMilestonedEvent): TimelineModel = TimelineModel(milestoneEventModel = MilestoneDemilestonedEventModel(
node.createdAt, node.actor?.fragments?.shortActor?.toUser(), node.milestoneTitle, true
))
private fun getMilestone(node: AsMilestonedEvent): TimelineModel = TimelineModel(
milestoneEventModel = MilestoneDemilestonedEventModel(
node.createdAt, node.actor?.fragments?.shortActor?.toUser(), node.milestoneTitle, true
)
)
private fun getUnassigned(
node: AsUnassignedEvent,
@ -97,10 +111,12 @@ class GetIssueTimelineUseCase @Inject constructor(
}
}
if (shouldAdd) {
return TimelineModel(assignedEventModel = AssignedUnAssignedEventModel(
node.createdAt, node.actor?.fragments?.shortActor?.toUser(), false,
arrayListOf(ShortUserModel(node.user?.login, node.user?.login, avatarUrl = node.user?.avatarUrl?.toString()))
))
return TimelineModel(
assignedEventModel = AssignedUnAssignedEventModel(
node.createdAt, node.actor?.fragments?.shortActor?.toUser(), false,
arrayListOf(ShortUserModel(node.user?.login, node.user?.login, avatarUrl = node.user?.avatarUrl?.toString()))
)
)
}
return null
}
@ -117,23 +133,29 @@ class GetIssueTimelineUseCase @Inject constructor(
}
}
if (shouldAdd) {
return TimelineModel(assignedEventModel = AssignedUnAssignedEventModel(
node.createdAt, node.actor?.fragments?.shortActor?.toUser(), true,
arrayListOf(ShortUserModel(node.user?.login, node.user?.login, avatarUrl = node.user?.avatarUrl?.toString()))
))
return TimelineModel(
assignedEventModel = AssignedUnAssignedEventModel(
node.createdAt, node.actor?.fragments?.shortActor?.toUser(), true,
arrayListOf(ShortUserModel(node.user?.login, node.user?.login, avatarUrl = node.user?.avatarUrl?.toString()))
)
)
}
return null
}
private fun getUnsubscribed(
node: AsUnsubscribedEvent
): TimelineModel = TimelineModel(subscribedUnsubscribedEvent = SubscribedUnsubscribedEventModel(
node.createdAt, node.actor?.fragments?.shortActor?.toUser(), false
))
): TimelineModel = TimelineModel(
subscribedUnsubscribedEvent = SubscribedUnsubscribedEventModel(
node.createdAt, node.actor?.fragments?.shortActor?.toUser(), false
)
)
private fun getSubscribed(node: AsSubscribedEvent): TimelineModel = TimelineModel(subscribedUnsubscribedEvent = SubscribedUnsubscribedEventModel(
node.createdAt, node.actor?.fragments?.shortActor?.toUser(), false
))
private fun getSubscribed(node: AsSubscribedEvent): TimelineModel = TimelineModel(
subscribedUnsubscribedEvent = SubscribedUnsubscribedEventModel(
node.createdAt, node.actor?.fragments?.shortActor?.toUser(), false
)
)
private fun getUnlabeled(
node: AsUnlabeledEvent,
@ -147,9 +169,11 @@ class GetIssueTimelineUseCase @Inject constructor(
}
}
if (shouldAdd) {
return TimelineModel(labelUnlabeledEvent = LabelUnlabeledEventModel(
node.createdAt, node.actor?.fragments?.shortActor?.toUser(), false, arrayListOf(constructLabel(node.label))
))
return TimelineModel(
labelUnlabeledEvent = LabelUnlabeledEventModel(
node.createdAt, node.actor?.fragments?.shortActor?.toUser(), false, arrayListOf(constructLabel(node.label))
)
)
}
return null
}
@ -166,9 +190,11 @@ class GetIssueTimelineUseCase @Inject constructor(
}
}
if (shouldAdd) {
return TimelineModel(labelUnlabeledEvent = LabelUnlabeledEventModel(
node.createdAt, node.actor?.fragments?.shortActor?.toUser(), true, arrayListOf(constructLabel(node.label))
))
return TimelineModel(
labelUnlabeledEvent = LabelUnlabeledEventModel(
node.createdAt, node.actor?.fragments?.shortActor?.toUser(), true, arrayListOf(constructLabel(node.label))
)
)
}
return null
}
@ -182,53 +208,73 @@ class GetIssueTimelineUseCase @Inject constructor(
}
private fun getUnlocked(node: AsUnlockedEvent): TimelineModel = TimelineModel(lockUnlockEventModel = LockUnlockEventModel(
node.createdAt, node.actor?.fragments?.shortActor?.toUser(), null, node.lockable.activeLockReason?.rawValue()
))
private fun getUnlocked(node: AsUnlockedEvent): TimelineModel = TimelineModel(
lockUnlockEventModel = LockUnlockEventModel(
node.createdAt, node.actor?.fragments?.shortActor?.toUser(), null, node.lockable.activeLockReason?.rawValue()
)
)
private fun getLock(node: AsLockedEvent): TimelineModel = TimelineModel(lockUnlockEventModel = LockUnlockEventModel(
node.createdAt, node.actor?.fragments?.shortActor?.toUser(), node.lockReason?.rawValue(), node.lockable.activeLockReason?.rawValue(), true
))
private fun getLock(node: AsLockedEvent): TimelineModel = TimelineModel(
lockUnlockEventModel = LockUnlockEventModel(
node.createdAt, node.actor?.fragments?.shortActor?.toUser(), node.lockReason?.rawValue(), node.lockable.activeLockReason?.rawValue(), true
)
)
private fun getReopened(node: AsReopenedEvent): TimelineModel {
return TimelineModel(closeOpenEventModel = CloseOpenEventModel(
node.createdAt, node.actor?.fragments?.shortActor?.toUser())
return TimelineModel(
closeOpenEventModel = CloseOpenEventModel(
node.createdAt, node.actor?.fragments?.shortActor?.toUser()
)
)
}
private fun getClosed(node: AsClosedEvent): TimelineModel {
val commit = node.closer?.fragments?.commitFragment?.toCommit()
val pr = node.closer?.fragments?.shortPullRequestRowItem?.toPullRequest()
return TimelineModel(closeOpenEventModel = CloseOpenEventModel(
node.createdAt, node.actor?.fragments?.shortActor?.toUser(), commit, pr, true)
return TimelineModel(
closeOpenEventModel = CloseOpenEventModel(
node.createdAt, node.actor?.fragments?.shortActor?.toUser(), commit, pr, true
)
)
}
private fun getReference(node: AsReferencedEvent): TimelineModel {
val issueModel = node.subject.fragments.shortIssueRowItem?.toIssue()
val pullRequest = node.subject.fragments.shortPullRequestRowItem?.toPullRequest()
return TimelineModel(referencedEventModel = ReferencedEventModel(
node.commitRepository.nameWithOwner, node.createdAt, ShortUserModel(
node.actor?.fragments?.shortActor?.login, node.actor?.fragments?.shortActor?.login, node.actor?.fragments?.shortActor?.url?.toString(),
avatarUrl = node.actor?.fragments?.shortActor?.avatarUrl?.toString()), node.isCrossRepository, node.isDirectReference,
node.commit?.fragments?.commitFragment?.toCommit(), issueModel, pullRequest))
return TimelineModel(
referencedEventModel = ReferencedEventModel(
node.commitRepository.nameWithOwner, node.createdAt, ShortUserModel(
node.actor?.fragments?.shortActor?.login, node.actor?.fragments?.shortActor?.login,
node.actor?.fragments?.shortActor?.url?.toString(),
avatarUrl = node.actor?.fragments?.shortActor?.avatarUrl?.toString()
), node.isCrossRepository, node.isDirectReference,
node.commit?.fragments?.commitFragment?.toCommit(), issueModel, pullRequest
)
)
}
private fun getCrossReference(node: AsCrossReferencedEvent): TimelineModel {
val actor = node.actor?.fragments?.shortActor?.toUser()
val issueModel = node.source.fragments.shortIssueRowItem?.toIssue()
val pullRequest = node.source.fragments.shortPullRequestRowItem?.toPullRequest()
return TimelineModel(crossReferencedEventModel = CrossReferencedEventModel(node.createdAt, node.referencedAt,
node.isCrossRepository, node.isWillCloseTarget, actor, issueModel, pullRequest))
return TimelineModel(
crossReferencedEventModel = CrossReferencedEventModel(
node.createdAt, node.referencedAt,
node.isCrossRepository, node.isWillCloseTarget, actor, issueModel, pullRequest
)
)
}
private fun getComment(node: AsIssueComment) = TimelineModel(comment = CommentModel(node.id,
ShortUserModel(node.author?.login, node.author?.login, avatarUrl = node.author?.avatarUrl.toString()),
node.bodyHTML.toString(), node.body, CommentAuthorAssociation.fromName(node.authorAssociation.rawValue()),
node.viewerCannotUpdateReasons.map { reason -> CommentCannotUpdateReason.fromName(reason.rawValue()) }.toList(),
node.reactionGroups?.map { it.fragments.reactions.toReactionGroup() }, node.createdAt, node.updatedAt,
node.isViewerCanReact, node.isViewerCanDelete, node.isViewerCanUpdate, node.isViewerDidAuthor, node.isViewerCanMinimize
))
private fun getComment(node: AsIssueComment) = TimelineModel(
comment = CommentModel(
node.id,
ShortUserModel(node.author?.login, node.author?.login, avatarUrl = node.author?.avatarUrl.toString()),
node.bodyHTML.toString(), node.body, CommentAuthorAssociation.fromName(node.authorAssociation.rawValue()),
node.viewerCannotUpdateReasons.map { reason -> CommentCannotUpdateReason.fromName(reason.rawValue()) }.toList(),
node.reactionGroups?.map { it.fragments.reactions.toReactionGroup() }, node.createdAt, node.updatedAt,
node.isViewerCanReact, node.isViewerCanDelete, node.isViewerCanUpdate, node.isViewerDidAuthor, node.isViewerCanMinimize
)
)
private fun getCommit(node: AsCommit) = TimelineModel(commit = node.fragments.commitFragment?.toCommit())
}

View File

@ -7,6 +7,7 @@ import com.fastaccess.data.model.EmbeddedRepoModel
import com.fastaccess.data.model.ShortUserModel
import com.fastaccess.data.persistence.models.IssueModel
import com.fastaccess.data.repository.IssueRepositoryProvider
import com.fastaccess.data.repository.SchedulerProvider
import com.fastaccess.domain.usecase.base.BaseObservableUseCase
import com.fastaccess.extension.toLabels
import com.fastaccess.extension.toMilestone
@ -21,7 +22,8 @@ import javax.inject.Inject
*/
class GetIssueUseCase @Inject constructor(
private val issueRepositoryProvider: IssueRepositoryProvider,
private val apolloClient: ApolloClient
private val apolloClient: ApolloClient,
private val schedulerProvider: SchedulerProvider
) : BaseObservableUseCase() {
var login: String? = null
@ -38,18 +40,23 @@ class GetIssueUseCase @Inject constructor(
}
return Rx2Apollo.from(apolloClient.query(GetIssueQuery(login, repo, number)))
.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.uiThread())
.map { it.data()?.repositoryOwner?.repository?.issue?.fragments?.fullIssue }
.map { issue ->
issueRepositoryProvider.upsert(IssueModel(issue.id, issue.databaseId, issue.number, issue.activeLockReason?.rawValue(),
issue.body, issue.bodyHTML.toString(), issue.closedAt, issue.createdAt, issue.updatedAt, issue.state.rawValue(),
issue.title, issue.viewerSubscription?.rawValue(), ShortUserModel(issue.author?.login, issue.author?.login,
issue.author?.url?.toString(), avatarUrl = issue.author?.avatarUrl?.toString()),
issue.title, issue.viewerSubscription?.rawValue(), ShortUserModel(
issue.author?.login, issue.author?.login,
issue.author?.url?.toString(), avatarUrl = issue.author?.avatarUrl?.toString()
),
EmbeddedRepoModel(issue.repository.nameWithOwner), CountModel(issue.userContentEdits?.totalCount),
issue.reactionGroups?.map { it.fragments.reactions.toReactionGroup() },
issue.viewerCannotUpdateReasons.map { it.rawValue() }, issue.isClosed, issue.isCreatedViaEmail, issue.isLocked,
issue.isViewerCanReact, issue.isViewerCanSubscribe, issue.isViewerCanUpdate, issue.isViewerDidAuthor,
issue.authorAssociation.rawValue(), issue.url.toString(), issue.labels?.nodes?.map { it.fragments.labels.toLabels() },
issue.milestone?.toMilestone(), issue.assignees.nodes?.map { it.fragments }?.map { it.shortUserRowItem.toUser() }))
issue.milestone?.toMilestone(), issue.assignees.nodes?.map { it.fragments }?.map { it.shortUserRowItem.toUser() })
)
}
}
}

View File

@ -5,6 +5,7 @@ import com.apollographql.apollo.api.Input
import com.apollographql.apollo.rx2.Rx2Apollo
import com.fastaccess.data.model.PageInfoModel
import com.fastaccess.data.model.parcelable.LabelModel
import com.fastaccess.data.repository.SchedulerProvider
import com.fastaccess.domain.usecase.base.BaseObservableUseCase
import com.fastaccess.extension.toLabels
import github.GetLabelsQuery
@ -15,7 +16,8 @@ import javax.inject.Inject
* Created by Kosh on 27.01.19.
*/
class GetLabelsUseCase @Inject constructor(
private val apolloClient: ApolloClient
private val apolloClient: ApolloClient,
private val schedulerProvider: SchedulerProvider
) : BaseObservableUseCase() {
var login: String? = null
@ -27,13 +29,19 @@ class GetLabelsUseCase @Inject constructor(
val repo = repo
if (login.isNullOrEmpty() || repo.isNullOrEmpty()) {
return Observable.error(Throwable("this should never happen ;)"))
return Observable.error<Pair<PageInfoModel, List<LabelModel>>>(Throwable("this should never happen ;)"))
.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.uiThread())
}
return Rx2Apollo.from(apolloClient.query(GetLabelsQuery(login, repo, page)))
.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.uiThread())
.map { it.data()?.repositoryOwner?.repository?.labels }
.map { data ->
val pageInfo = PageInfoModel(data.pageInfo.startCursor, data.pageInfo.endCursor,
data.pageInfo.isHasNextPage, data.pageInfo.isHasPreviousPage)
val pageInfo = PageInfoModel(
data.pageInfo.startCursor, data.pageInfo.endCursor,
data.pageInfo.isHasNextPage, data.pageInfo.isHasPreviousPage
)
return@map Pair(pageInfo, data.nodes?.map { it.fragments.labels.toLabels() } ?: listOf())
}
}

View File

@ -5,6 +5,7 @@ import com.apollographql.apollo.api.Input
import com.apollographql.apollo.rx2.Rx2Apollo
import com.fastaccess.data.model.PageInfoModel
import com.fastaccess.data.model.parcelable.MilestoneModel
import com.fastaccess.data.repository.SchedulerProvider
import com.fastaccess.domain.usecase.base.BaseObservableUseCase
import github.GetMilestonesQuery
import io.reactivex.Observable
@ -14,7 +15,8 @@ import javax.inject.Inject
* Created by Kosh on 27.01.19.
*/
class GetMilestonesUseCase @Inject constructor(
private val apolloClient: ApolloClient
private val apolloClient: ApolloClient,
private val schedulerProvider: SchedulerProvider
) : BaseObservableUseCase() {
var login: String? = null
@ -29,14 +31,20 @@ class GetMilestonesUseCase @Inject constructor(
return Observable.error(Throwable("this should never happen ;)"))
}
return Rx2Apollo.from(apolloClient.query(GetMilestonesQuery(login, repo, page)))
.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.uiThread())
.map { it.data()?.repositoryOwner?.repository?.milestones }
.map { data ->
val pageInfo = PageInfoModel(data.pageInfo.startCursor, data.pageInfo.endCursor,
data.pageInfo.isHasNextPage, data.pageInfo.isHasPreviousPage)
val pageInfo = PageInfoModel(
data.pageInfo.startCursor, data.pageInfo.endCursor,
data.pageInfo.isHasNextPage, data.pageInfo.isHasPreviousPage
)
return@map Pair(pageInfo, data.nodes?.map { it.fragments.milestoneFragment }
?.map {
MilestoneModel(it.id, it.title, it.description, it.state.toString(),
it.url.toString(), it.number, it.isClosed, it.dueOn)
MilestoneModel(
it.id, it.title, it.description, it.state.toString(),
it.url.toString(), it.number, it.isClosed, it.dueOn
)
} ?: listOf())
}
}

View File

@ -7,6 +7,7 @@ import com.fastaccess.data.model.LockUnlockEventModel
import com.fastaccess.data.model.TimelineModel
import com.fastaccess.data.repository.IssueRepositoryProvider
import com.fastaccess.data.repository.LoginRepositoryProvider
import com.fastaccess.data.repository.SchedulerProvider
import com.fastaccess.domain.usecase.base.BaseObservableUseCase
import com.fastaccess.extension.me
import github.LockMutation
@ -22,7 +23,8 @@ import javax.inject.Inject
class LockUnlockIssuePrUseCase @Inject constructor(
private val issueRepositoryProvider: IssueRepositoryProvider,
private val apolloClient: ApolloClient,
private val loginRepositoryProvider: LoginRepositoryProvider
private val loginRepositoryProvider: LoginRepositoryProvider,
private val schedulerProvider: SchedulerProvider
) : BaseObservableUseCase() {
var repo: String = ""
@ -32,12 +34,16 @@ class LockUnlockIssuePrUseCase @Inject constructor(
var lock: Boolean = false
override fun buildObservable(): Observable<TimelineModel> = issueRepositoryProvider.getIssueByNumberMaybe("$login/$repo", number)
.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.uiThread())
.flatMapObservable { issue ->
if (lockReason == null) {
Rx2Apollo.from(apolloClient.mutate(LockMutation(issue.id, Input.optional(lockReason))))
} else {
Rx2Apollo.from(apolloClient.mutate(UnlockMutation(issue.id)))
}
.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.uiThread())
.map {
issue.locked = lock == true
issue.activeLockReason = lockReason?.rawValue()

View File

@ -5,6 +5,7 @@ import com.fastaccess.data.model.TimelineModel
import com.fastaccess.data.model.parcelable.MilestoneModel
import com.fastaccess.data.repository.IssueRepositoryProvider
import com.fastaccess.data.repository.LoginRepositoryProvider
import com.fastaccess.data.repository.SchedulerProvider
import com.fastaccess.domain.repository.services.IssuePrService
import com.fastaccess.domain.response.IssueRequestModel
import com.fastaccess.domain.usecase.base.BaseObservableUseCase
@ -19,7 +20,8 @@ import javax.inject.Inject
class MilestoneIssuePrUseCase @Inject constructor(
private val issueRepositoryProvider: IssueRepositoryProvider,
private val issuePrService: IssuePrService,
private val loginRepositoryProvider: LoginRepositoryProvider
private val loginRepositoryProvider: LoginRepositoryProvider,
private val schedulerProvider: SchedulerProvider
) : BaseObservableUseCase() {
var repo: String = ""
@ -29,17 +31,27 @@ class MilestoneIssuePrUseCase @Inject constructor(
override fun buildObservable(): Observable<Pair<TimelineModel, MilestoneModel>> =
issueRepositoryProvider.getIssueByNumberMaybe("$login/$repo", number)
.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.uiThread())
.flatMapObservable { issue ->
issuePrService.editIssue(login, repo, number, IssueRequestModel(milestone = milestone))
.map {
val milestone = MilestoneModel(it.milestone?.id?.toString(),
val milestone = MilestoneModel(
it.milestone?.id?.toString(),
it.milestone?.title, it.milestone?.description,
it.milestone?.state, it.milestone?.url, it.milestone?.number, it.milestone?.closedAt != null, it.milestone?.dueOn)
it.milestone?.state, it.milestone?.url, it.milestone?.number, it.milestone?.closedAt != null, it.milestone?.dueOn
)
issue.milestone = milestone
issueRepositoryProvider.upsert(issue)
val me = loginRepositoryProvider.getLoginBlocking()?.me()
return@map Pair(TimelineModel(milestoneEventModel = MilestoneDemilestonedEventModel(Date(), me,
it.milestone?.title ?: it.milestone?.number?.toString(), true)), milestone)
return@map Pair(
TimelineModel(
milestoneEventModel = MilestoneDemilestonedEventModel(
Date(), me,
it.milestone?.title ?: it.milestone?.number?.toString(), true
)
), milestone
)
}
}
}

View File

@ -1,5 +1,6 @@
package com.fastaccess.github.usecase.issuesprs
import com.fastaccess.data.repository.SchedulerProvider
import com.fastaccess.domain.repository.services.RepoService
import com.fastaccess.domain.response.body.LabelsBodyModel
import com.fastaccess.domain.usecase.base.BaseObservableUseCase
@ -10,7 +11,8 @@ import javax.inject.Inject
* Created by Kosh on 16.02.19.
*/
class PutLabelsUseCase @Inject constructor(
private val repoService: RepoService
private val repoService: RepoService,
private val schedulerProvider: SchedulerProvider
) : BaseObservableUseCase() {
var repo: String = ""
@ -21,8 +23,12 @@ class PutLabelsUseCase @Inject constructor(
override fun buildObservable(): Observable<Boolean> = when (toRemove.isNullOrEmpty()) {
true -> repoService.addLabelsToIssue(login, repo, number, LabelsBodyModel(toAdd))
.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.uiThread())
.map { true }
else -> repoService.addLabelsToIssue(login, repo, number, LabelsBodyModel(toAdd))
.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.uiThread())
.flatMap { Observable.fromIterable(toRemove) }
.flatMap { repoService.removeLabelsToIssue(login, repo, number, it) }
.map { true }

View File

@ -5,6 +5,7 @@ import com.apollographql.apollo.rx2.Rx2Apollo
import com.fastaccess.data.persistence.models.MyIssuesPullsModel
import com.fastaccess.data.repository.LoginRepositoryProvider
import com.fastaccess.data.repository.MyIssuesPullsRepositoryProvider
import com.fastaccess.data.repository.SchedulerProvider
import com.fastaccess.domain.usecase.base.BaseObservableUseCase
import github.GetIssuesWithoutStateQuery
import github.type.IssueState
@ -17,7 +18,8 @@ import javax.inject.Inject
class IssuesMainScreenUseCase @Inject constructor(
private val loginRepository: LoginRepositoryProvider,
private val myIssues: MyIssuesPullsRepositoryProvider,
private val apolloClient: ApolloClient
private val apolloClient: ApolloClient,
private val schedulerProvider: SchedulerProvider
) : BaseObservableUseCase() {
var state: IssueState = IssueState.OPEN
@ -33,6 +35,8 @@ class IssuesMainScreenUseCase @Inject constructor(
.build()
)
)
.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.uiThread())
.map { it.data()?.user?.issues?.nodes }
.map { value ->
myIssues.deleteAllIssues()

View File

@ -5,6 +5,7 @@ import com.apollographql.apollo.rx2.Rx2Apollo
import com.fastaccess.data.persistence.models.MyIssuesPullsModel
import com.fastaccess.data.repository.LoginRepositoryProvider
import com.fastaccess.data.repository.MyIssuesPullsRepositoryProvider
import com.fastaccess.data.repository.SchedulerProvider
import com.fastaccess.domain.usecase.base.BaseObservableUseCase
import github.GetPullRequestsQuery
import github.type.IssueState
@ -17,7 +18,8 @@ import javax.inject.Inject
class PullRequestsMainScreenUseCase @Inject constructor(
private val loginRepository: LoginRepositoryProvider,
private val myIssues: MyIssuesPullsRepositoryProvider,
private val apolloClient: ApolloClient
private val apolloClient: ApolloClient,
private val schedulerProvider: SchedulerProvider
) : BaseObservableUseCase() {
var state: IssueState = IssueState.OPEN
@ -32,6 +34,8 @@ class PullRequestsMainScreenUseCase @Inject constructor(
.build()
)
)
.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.uiThread())
.map { it.data()?.user?.pullRequests?.nodes }
.map { value ->
myIssues.deleteAllPrs()

View File

@ -2,6 +2,7 @@ package com.fastaccess.github.usecase.notification
import com.fastaccess.data.persistence.models.NotificationModel
import com.fastaccess.data.repository.NotificationRepositoryProvider
import com.fastaccess.data.repository.SchedulerProvider
import com.fastaccess.domain.repository.services.NotificationService
import com.fastaccess.domain.response.NotificationResponse
import com.fastaccess.domain.response.PageableResponse
@ -18,7 +19,8 @@ import javax.inject.Inject
class NotificationUseCase @Inject constructor(
private val notificationRepositoryProvider: NotificationRepositoryProvider,
private val notificationService: NotificationService,
private val gson: Gson
private val gson: Gson,
private val schedulerProvider: SchedulerProvider
) : BaseObservableUseCase() {
var page: Int? = null
@ -36,18 +38,21 @@ class NotificationUseCase @Inject constructor(
notificationService.getNotifications(getLastWeekDate(), page)
}
}
return observable.map { it ->
it.items?.let { items ->
if (all == true) {
notificationRepositoryProvider.deleteAll(false)
val list = items.asSequence().filter { it.unread == false }.toList()
notificationRepositoryProvider.insert(NotificationModel.convert(gson, list))
return@map it
return observable
.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.uiThread())
.map { it ->
it.items?.let { items ->
if (all == true) {
notificationRepositoryProvider.deleteAll(false)
val list = items.asSequence().filter { it.unread == false }.toList()
notificationRepositoryProvider.insert(NotificationModel.convert(gson, list))
return@map it
}
if (page ?: 0 <= 1) notificationRepositoryProvider.deleteAll(true)
notificationRepositoryProvider.insert(NotificationModel.convert(gson, items))
}
if (page ?: 0 <= 1) notificationRepositoryProvider.deleteAll(true)
notificationRepositoryProvider.insert(NotificationModel.convert(gson, items))
return@map it
}
return@map it
}
}
}

View File

@ -7,6 +7,7 @@ import com.fastaccess.data.model.CountModel
import com.fastaccess.data.model.PageInfoModel
import com.fastaccess.data.model.ShortRepoModel
import com.fastaccess.data.model.parcelable.FilterByRepo
import com.fastaccess.data.repository.SchedulerProvider
import com.fastaccess.domain.usecase.base.BaseObservableUseCase
import github.SearchReposQuery
import io.reactivex.Observable
@ -16,7 +17,8 @@ import javax.inject.Inject
* Created by Kosh on 20.01.19.
*/
class FilterSearchReposUseCase @Inject constructor(
private val apolloClient: ApolloClient
private val apolloClient: ApolloClient,
private val schedulerProvider: SchedulerProvider
) : BaseObservableUseCase() {
var cursor: Input<String?> = Input.absent()
@ -25,17 +27,25 @@ class FilterSearchReposUseCase @Inject constructor(
override fun buildObservable(): Observable<Pair<PageInfoModel, List<ShortRepoModel>>> = Rx2Apollo
.from(apolloClient.query(SearchReposQuery(constructQuery(keyword), cursor)))
.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.uiThread())
.map { it.data()?.search }
.map { search ->
val list = search.nodes
?.mapNotNull { it.fragments.shortRepoRowItem }
?.map { repo ->
ShortRepoModel(repo.id, repo.databaseId, repo.name,
ShortRepoModel(
repo.id, repo.databaseId, repo.name,
CountModel(repo.stargazers.totalCount), CountModel(repo.issues.totalCount),
CountModel(repo.pullRequests.totalCount), repo.forkCount, repo.isFork, repo.isPrivate)
CountModel(repo.pullRequests.totalCount), repo.forkCount, repo.isFork, repo.isPrivate
)
} ?: arrayListOf()
return@map Pair(PageInfoModel(search.pageInfo.startCursor, search.pageInfo.endCursor,
search.pageInfo.isHasNextPage, search.pageInfo.isHasPreviousPage), list)
return@map Pair(
PageInfoModel(
search.pageInfo.startCursor, search.pageInfo.endCursor,
search.pageInfo.isHasNextPage, search.pageInfo.isHasPreviousPage
), list
)
}

View File

@ -5,6 +5,7 @@ import com.apollographql.apollo.api.Input
import com.apollographql.apollo.rx2.Rx2Apollo
import com.fastaccess.data.model.PageInfoModel
import com.fastaccess.data.model.ShortUserModel
import com.fastaccess.data.repository.SchedulerProvider
import com.fastaccess.domain.usecase.base.BaseObservableUseCase
import github.SearchUserQuery
import io.reactivex.Observable
@ -14,7 +15,8 @@ import javax.inject.Inject
* Created by Kosh on 20.01.19.
*/
class FilterSearchUsersUseCase @Inject constructor(
private val apolloClient: ApolloClient
private val apolloClient: ApolloClient,
private val schedulerProvider: SchedulerProvider
) : BaseObservableUseCase() {
var cursor: Input<String?> = Input.absent()
@ -22,16 +24,24 @@ class FilterSearchUsersUseCase @Inject constructor(
override fun buildObservable(): Observable<Pair<PageInfoModel, List<ShortUserModel>>> = Rx2Apollo
.from(apolloClient.query(SearchUserQuery(keyword, cursor)))
.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.uiThread())
.map { it.data()?.search }
.map { search ->
val list = search.nodes
?.mapNotNull { it.fragments.shortUserRowItem }
?.map { user ->
ShortUserModel(user.id, user.login, user.url.toString(), user.name, user.location,
user.bio, user.avatarUrl.toString(), user.isViewerCanFollow, user.isViewerIsFollowing)
ShortUserModel(
user.id, user.login, user.url.toString(), user.name, user.location,
user.bio, user.avatarUrl.toString(), user.isViewerCanFollow, user.isViewerIsFollowing
)
} ?: arrayListOf()
return@map Pair(PageInfoModel(search.pageInfo.startCursor, search.pageInfo.endCursor,
search.pageInfo.isHasNextPage, search.pageInfo.isHasPreviousPage), list)
return@map Pair(
PageInfoModel(
search.pageInfo.startCursor, search.pageInfo.endCursor,
search.pageInfo.isHasNextPage, search.pageInfo.isHasPreviousPage
), list
)
}

View File

@ -1,5 +1,6 @@
package com.fastaccess.github.usecase.user
import com.fastaccess.data.repository.SchedulerProvider
import com.fastaccess.data.repository.UserRepositoryProvider
import com.fastaccess.domain.usecase.base.BaseObservableUseCase
import io.reactivex.Observable
@ -8,12 +9,17 @@ import javax.inject.Inject
/**
* Created by Kosh on 10.06.18.
*/
class BlockUnblockUserUseCase @Inject constructor(private val userRepository: UserRepositoryProvider) : BaseObservableUseCase() {
class BlockUnblockUserUseCase @Inject constructor(
private val userRepository: UserRepositoryProvider,
private val schedulerProvider: SchedulerProvider
) : BaseObservableUseCase() {
var login: String? = null
var block: Boolean = false
override fun buildObservable(): Observable<Boolean> = login?.let { login ->
userRepository.blockUnblockUser(login, block)
.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.uiThread())
.map {
val isSuccess = it.isSuccessful && it.code() == 204
if (isSuccess) {

View File

@ -1,5 +1,6 @@
package com.fastaccess.github.usecase.user
import com.fastaccess.data.repository.SchedulerProvider
import com.fastaccess.data.repository.UserRepositoryProvider
import com.fastaccess.domain.usecase.base.BaseObservableUseCase
import io.reactivex.Observable
@ -8,11 +9,16 @@ import javax.inject.Inject
/**
* Created by Kosh on 10.06.18.
*/
class IsUserBlockedUseCase @Inject constructor(private val userRepository: UserRepositoryProvider) : BaseObservableUseCase() {
class IsUserBlockedUseCase @Inject constructor(
private val userRepository: UserRepositoryProvider,
private val schedulerProvider: SchedulerProvider
) : BaseObservableUseCase() {
var login: String? = null
override fun buildObservable(): Observable<Boolean> = login?.let { login ->
userRepository.isUserBlock(login)
userRepository.isUserBlocked(login)
.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.uiThread())
.map { it.isSuccessful && it.code() == 204 }
} ?: Observable.empty()
}

View File

@ -1,6 +1,7 @@
package com.fastaccess.github.usecase.user
import com.fastaccess.data.repository.FeedsRepositoryProvider
import com.fastaccess.data.repository.SchedulerProvider
import com.fastaccess.domain.response.FeedResponse
import com.fastaccess.domain.response.PageableResponse
import com.fastaccess.domain.usecase.base.BaseObservableUseCase
@ -10,12 +11,17 @@ import javax.inject.Inject
/**
* Created by Kosh on 20.10.18.
*/
class UserFeedsUseCase @Inject constructor(private val feedsRepositoryProvider: FeedsRepositoryProvider) : BaseObservableUseCase() {
class UserFeedsUseCase @Inject constructor(
private val feedsRepositoryProvider: FeedsRepositoryProvider,
private val schedulerProvider: SchedulerProvider
) : BaseObservableUseCase() {
var page: Int = 0
var login: String? = null
override fun buildObservable(): Observable<PageableResponse<FeedResponse>> = when {
login.isNullOrEmpty() -> Observable.empty()
else -> feedsRepositoryProvider.getFeeds(login ?: "", page)
.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.ioThread())
}
}

View File

@ -1,6 +1,7 @@
package com.fastaccess.github.usecase.user
import com.fastaccess.data.persistence.models.UserModel
import com.fastaccess.data.repository.SchedulerProvider
import com.fastaccess.data.repository.UserRepositoryProvider
import com.fastaccess.domain.usecase.base.BaseObservableUseCase
import io.reactivex.Observable
@ -9,10 +10,18 @@ import javax.inject.Inject
/**
* Created by Kosh on 10.06.18.
*/
class UserUseCase @Inject constructor(private val userRepository: UserRepositoryProvider) : BaseObservableUseCase() {
class UserUseCase @Inject constructor(
private val userRepository: UserRepositoryProvider,
val schedulerProvider: SchedulerProvider
) : BaseObservableUseCase() {
var login: String? = null
override fun buildObservable(): Observable<UserModel> = login?.let { userRepository.getUserFromRemote(it) } ?: Observable.empty()
override fun buildObservable(): Observable<UserModel> {
val observable = login?.let { userRepository.getUserFromRemote(it) } ?: Observable.empty()
return observable
.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.uiThread())
}
fun getUser(login: String) = userRepository.getUser(login)
}

View File

@ -119,7 +119,7 @@
android:layout_height="?actionBarSize"
android:orientation="horizontal">
<ImageButton
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/toggleButtons"
android:layout_width="wrap_content"
android:layout_height="match_parent"
@ -145,7 +145,7 @@
android:paddingEnd="@dimen/spacing_normal"
android:scrollbars="vertical" />
<ImageButton
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/sendComment"
android:layout_width="wrap_content"
android:layout_height="match_parent"

View File

@ -18,13 +18,14 @@
<FrameLayout
android:layout_width="38dp"
android:layout_height="38dp"
android:layout_marginEnd="@dimen/spacing_xs_large"
android:layout_gravity="center">
android:layout_gravity="center"
android:layout_marginEnd="@dimen/spacing_xs_large">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/typeIcon"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="center"
android:background="@drawable/circle_shape_cardview"
android:padding="@dimen/spacing_micro"
tools:src="@drawable/ic_issues" />

View File

@ -64,7 +64,8 @@ ext {
'com.github.zagum:Android-ExpandIcon:1.2.1',
"com.evernote:android-state:$androidState",
'org.jsoup:jsoup:1.12.1',
'com.github.k0shk0sh:RetainedDateTimePickers:1.0.2'
'com.github.k0shk0sh:RetainedDateTimePickers:1.0.2',
'com.otaliastudios:autocomplete:1.1.0'
]
networking = [

View File

@ -0,0 +1,20 @@
package com.fastaccess.data.repository
import io.reactivex.Scheduler
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
/**
* Created by Kosh on 2019-05-09.
*/
class AndroidSchedulerProvider : SchedulerProvider {
override fun uiThread(): Scheduler = AndroidSchedulers.mainThread()
override fun ioThread(): Scheduler = Schedulers.io()
override fun computationThread(): Scheduler = Schedulers.computation()
}
interface SchedulerProvider {
fun uiThread(): Scheduler
fun ioThread(): Scheduler
fun computationThread(): Scheduler
}

View File

@ -5,7 +5,6 @@ import androidx.paging.DataSource
import com.fastaccess.data.model.GroupedNotificationsModel
import com.fastaccess.data.persistence.dao.NotificationsDao
import com.fastaccess.data.persistence.models.NotificationModel
import com.fastaccess.extension.uiThread
import com.fastaccess.github.extensions.map
import io.reactivex.Completable
import io.reactivex.Maybe
@ -14,7 +13,10 @@ import javax.inject.Inject
/**
* Created by Kosh on 22.06.18.
*/
class NotificationRepositoryProvider @Inject constructor(private val dao: NotificationsDao) : NotificationRepository {
class NotificationRepositoryProvider @Inject constructor(
private val dao: NotificationsDao,
private val schedulerProvider: SchedulerProvider
) : NotificationRepository {
override fun getNotifications(unread: Boolean): DataSource.Factory<Int, NotificationModel> = dao.getNotifications(unread)
override fun getAllNotifications(): LiveData<List<GroupedNotificationsModel>> = dao.getAllNotifications(false).map(groupNotifications())
@ -24,10 +26,14 @@ class NotificationRepositoryProvider @Inject constructor(private val dao: Notifi
override fun update(model: NotificationModel): Int = dao.update(model)
override fun delete(model: NotificationModel) = dao.delete(model)
override fun deleteAll(unread: Boolean) = dao.deleteAll(unread)
override fun markAsRead(id: String): Completable = Completable.fromCallable { dao.markAsRead(id) }.uiThread()
override fun markAllAsRead(): Completable = Completable.fromCallable { dao.markAllAsRead() }.uiThread()
override fun markAsRead(id: String): Completable = Completable.fromCallable { dao.markAsRead(id) }
override fun markAllAsRead(): Completable = Completable.fromCallable { dao.markAllAsRead() }
.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.uiThread())
override fun getAllNotificationsAsMaybe(unread: Boolean): Maybe<List<NotificationModel>> = Maybe.just(dao.getAllNotificationsBlocking(unread))
.uiThread()
.subscribeOn(schedulerProvider.ioThread())
.observeOn(schedulerProvider.uiThread())
/**
* Fixes Cannot infer a type for this parameter. Please specify it explicitly. 🤷🤷🤷🤷🤷🤷

View File

@ -17,29 +17,36 @@ import javax.inject.Inject
* Created by Kosh on 10.06.18.
*/
class UserRepositoryProvider @Inject constructor(private val userDao: UserDao,
private val apolloClient: ApolloClient,
private val userService: UserService) : UserRepository {
class UserRepositoryProvider @Inject constructor(
private val userDao: UserDao,
private val apolloClient: ApolloClient,
private val userService: UserService
) : UserRepository {
override fun getUserFromRemote(login: String): Observable<UserModel> = Rx2Apollo.from(apolloClient.query(GetProfileQuery(login)))
.filter { !it.hasErrors() }
.map { it ->
return@map it.data()?.user?.let { queryUser ->
UserModel(queryUser.databaseId
?: 0, queryUser.login, queryUser.avatarUrl.toString(), queryUser.url.toString(), queryUser.name, queryUser.company,
UserModel(
queryUser.databaseId ?: 0, queryUser.login, queryUser.avatarUrl.toString(),
queryUser.url.toString(), queryUser.name, queryUser.company,
queryUser.websiteUrl.toString(), queryUser.location, queryUser.email, queryUser.bio, queryUser.createdAt,
queryUser.createdAt, queryUser.isViewerCanFollow, queryUser.isViewerIsFollowing, queryUser.isViewer,
queryUser.isDeveloperProgramMember, CountModel(queryUser.followers.totalCount),
CountModel(queryUser.following.totalCount),
queryUser.isDeveloperProgramMember, CountModel(queryUser.followers.totalCount), CountModel(queryUser.following.totalCount),
UserOrganizationModel(queryUser.organizations.totalCount, queryUser.organizations.nodes?.asSequence()?.map {
UserOrganizationNodesModel(it.avatarUrl.toString(), it.location, it.email, it.login, it.name)
}?.toList()), UserPinnedReposModel(queryUser.pinnedRepositories.totalCount,
queryUser.pinnedRepositories.nodes?.asSequence()?.map {
UserPinnedRepoNodesModel(it.name, it.nameWithOwner,
RepoLanguageModel(it.primaryLanguage?.name, it.primaryLanguage?.color),
CountModel(it.stargazers.totalCount), CountModel(it.issues.totalCount),
CountModel(it.pullRequests.totalCount), it.forkCount)
}?.toList()))
}?.toList()), UserPinnedReposModel(
queryUser.pinnedRepositories.totalCount,
queryUser.pinnedRepositories.nodes?.asSequence()?.map {
UserPinnedRepoNodesModel(
it.name, it.nameWithOwner,
RepoLanguageModel(it.primaryLanguage?.name, it.primaryLanguage?.color),
CountModel(it.stargazers.totalCount), CountModel(it.issues.totalCount),
CountModel(it.pullRequests.totalCount), it.forkCount
)
}?.toList()
)
)
}?.apply {
userDao.upsert(this)
}
@ -50,14 +57,20 @@ class UserRepositoryProvider @Inject constructor(private val userDao: UserDao,
override fun getUser(login: String): LiveData<UserModel> = userDao.getUser(login)
override fun deleteAll() = userDao.deleteAll()
override fun updateUser(userModel: UserModel) = userDao.update(userModel)
override fun isUserBlock(login: String): Observable<Response<Boolean>> = userService.isUserBlocked(login)
override fun isUserBlocked(login: String): Observable<Response<Boolean>> = userService.isUserBlocked(login)
override fun blockUnblockUser(login: String, block: Boolean): Observable<Response<Boolean>> = when (block) {
override fun blockUnblockUser(
login: String,
block: Boolean
): Observable<Response<Boolean>> = when (block) {
true -> userService.blockUser(login)
else -> userService.unBlockUser(login)
}
override fun followUnfollowUser(login: String, follow: Boolean): Observable<Response<Boolean>> = when (follow) {
override fun followUnfollowUser(
login: String,
follow: Boolean
): Observable<Response<Boolean>> = when (follow) {
true -> userService.followUser(login)
else -> userService.unfollowUser(login)
}
@ -69,8 +82,16 @@ interface UserRepository {
fun getUserBlocking(login: String): UserModel?
fun getUserFromRemote(login: String): Observable<UserModel>
fun deleteAll()
fun isUserBlock(login: String): Observable<Response<Boolean>>
fun blockUnblockUser(login: String, block: Boolean): Observable<Response<Boolean>>
fun followUnfollowUser(login: String, follow: Boolean): Observable<Response<Boolean>>
fun isUserBlocked(login: String): Observable<Response<Boolean>>
fun blockUnblockUser(
login: String,
block: Boolean
): Observable<Response<Boolean>>
fun followUnfollowUser(
login: String,
follow: Boolean
): Observable<Response<Boolean>>
fun updateUser(userModel: UserModel)
}

View File

@ -1,39 +0,0 @@
package com.fastaccess.extension
import io.reactivex.*
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
/**
* Created by Kosh on 16.08.18.
*/
fun <T> Maybe<T>.toObservableDistinct(): Observable<T> = this.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.toObservable()
.distinctUntilChanged()
fun <T> Flowable<T>.toObservableDistinct(): Observable<T> = this.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.toObservable()
.distinctUntilChanged()
fun <T> Maybe<T>.toObservable(): Observable<T> = this.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.toObservable()
fun <T> Observable<T>.uiThread() = this.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
fun <T> Observable<T>.uiThreadDistinct() = this.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.distinctUntilChanged()
fun Completable.uiThread() = this.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
fun <T> Single<T>.uiThread() = this.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
fun <T> Maybe<T>.uiThread() = this.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())

View File

@ -2,8 +2,6 @@ package com.fastaccess.domain.usecase.base
import io.reactivex.Completable
import io.reactivex.CompletableObserver
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
/**
* Created by Kosh on 12.05.18.
@ -13,25 +11,19 @@ abstract class BaseCompletableUseCase : BaseUseCase() {
fun disposeAndExecuteObservable(observer: CompletableObserver) {
disposeAndExecute(buildCompletable()
.doOnSubscribe { observer.onSubscribe(it) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe
(
{ observer.onComplete() },
{ observer.onError(it) }
))
.doOnSubscribe { observer.onSubscribe(it) }
.subscribe(
{ observer.onComplete() },
{ observer.onError(it) }
))
}
fun executeObservable(observer: CompletableObserver) {
execute(buildCompletable()
.doOnSubscribe { observer.onSubscribe(it) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe
(
{ observer.onComplete() },
{ observer.onError(it) }
))
.doOnSubscribe { observer.onSubscribe(it) }
.subscribe(
{ observer.onComplete() },
{ observer.onError(it) }
))
}
}

View File

@ -1,8 +1,6 @@
package com.fastaccess.domain.usecase.base
import io.reactivex.Flowable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import io.reactivex.subscribers.DisposableSubscriber
/**
@ -13,27 +11,21 @@ abstract class BaseFlowableUseCase : BaseUseCase() {
fun disposeAndExecuteObservable(observer: DisposableSubscriber<Any>) {
disposeAndExecute(buildFlowable()
.doOnSubscribe { observer.onSubscribe(it) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe
(
{ observer.onNext(it) },
{ observer.onError(it) },
{ observer.onComplete() }
))
.doOnSubscribe { observer.onSubscribe(it) }
.subscribe(
{ observer.onNext(it) },
{ observer.onError(it) },
{ observer.onComplete() }
))
}
fun executeObservable(observer: DisposableSubscriber<Any>) {
execute(buildFlowable()
.doOnSubscribe { observer.onSubscribe(it) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe
(
{ observer.onNext(it) },
{ observer.onError(it) },
{ observer.onComplete() }
))
.doOnSubscribe { observer.onSubscribe(it) }
.subscribe(
{ observer.onNext(it) },
{ observer.onError(it) },
{ observer.onComplete() }
))
}
}

View File

@ -2,8 +2,6 @@ package com.fastaccess.domain.usecase.base
import io.reactivex.Maybe
import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
/**
* Created by Kosh on 12.05.18.
@ -13,27 +11,21 @@ abstract class BaseMaybeUseCase : BaseUseCase() {
fun disposeAndExecuteObservable(observer: Observer<Any>) {
disposeAndExecute(buildMaybe()
.doOnSubscribe { observer.onSubscribe(it) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe
(
{ observer.onNext(it) },
{ observer.onError(it) },
{ observer.onComplete() }
))
.doOnSubscribe { observer.onSubscribe(it) }
.subscribe(
{ observer.onNext(it) },
{ observer.onError(it) },
{ observer.onComplete() }
))
}
fun executeObservable(observer: Observer<Any>) {
execute(buildMaybe()
.doOnSubscribe { observer.onSubscribe(it) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe
(
{ observer.onNext(it) },
{ observer.onError(it) },
{ observer.onComplete() }
))
.doOnSubscribe { observer.onSubscribe(it) }
.subscribe(
{ observer.onNext(it) },
{ observer.onError(it) },
{ observer.onComplete() }
))
}
}

View File

@ -2,8 +2,6 @@ package com.fastaccess.domain.usecase.base
import io.reactivex.Observable
import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
/**
* Created by Kosh on 12.05.18.
@ -13,34 +11,25 @@ abstract class BaseObservableUseCase : BaseUseCase() {
fun disposeAndExecuteObservable(observer: Observer<Any>) {
disposeAndExecute(buildObservable()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe { observer.onSubscribe(it) }
.subscribe
(
{ observer.onNext(it) },
{ observer.onError(it) },
{ observer.onComplete() }
))
.doOnSubscribe { observer.onSubscribe(it) }
.subscribe(
{ observer.onNext(it) },
{ observer.onError(it) },
{ observer.onComplete() }
))
}
fun executeObservable(observer: Observer<Any>) {
execute(buildObservable()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe { observer.onSubscribe(it) }
.subscribe
(
{ observer.onNext(it) },
{ observer.onError(it) },
{ observer.onComplete() }
))
.doOnSubscribe { observer.onSubscribe(it) }
.subscribe(
{ observer.onNext(it) },
{ observer.onError(it) },
{ observer.onComplete() }
))
}
fun <T> executeSafely(observable: Observable<T>) {
execute(observable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({}, { t -> t.printStackTrace() }))
execute(observable.subscribe({}, { t -> t.printStackTrace() }))
}
}

View File

@ -2,8 +2,6 @@ package com.fastaccess.domain.usecase.base
import io.reactivex.Single
import io.reactivex.SingleObserver
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
/**
* Created by Kosh on 12.05.18.
@ -13,25 +11,19 @@ abstract class BaseSingleUseCase : BaseUseCase() {
fun disposeAndExecuteObservable(observer: SingleObserver<Any>) {
disposeAndExecute(buildSingle()
.doOnSubscribe { observer.onSubscribe(it) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe
(
{ observer.onSuccess(it) },
{ observer.onError(it) }
))
.doOnSubscribe { observer.onSubscribe(it) }
.subscribe(
{ observer.onSuccess(it) },
{ observer.onError(it) }
))
}
fun executeObservable(observer: SingleObserver<Any>) {
execute(buildSingle()
.doOnSubscribe { observer.onSubscribe(it) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe
(
{ observer.onSuccess(it) },
{ observer.onError(it) }
))
.doOnSubscribe { observer.onSubscribe(it) }
.subscribe(
{ observer.onSuccess(it) },
{ observer.onError(it) }
))
}
}