Sort by various criteria
This commit is contained in:
@@ -11,6 +11,7 @@ import androidx.navigation.fragment.NavHostFragment
|
|||||||
import fr.uca.iut.clfreville2.teaiswarm.fragment.RepositoryListFragment
|
import fr.uca.iut.clfreville2.teaiswarm.fragment.RepositoryListFragment
|
||||||
import fr.uca.iut.clfreville2.teaiswarm.fragment.SetupConfigFragment
|
import fr.uca.iut.clfreville2.teaiswarm.fragment.SetupConfigFragment
|
||||||
import fr.uca.iut.clfreville2.teaiswarm.model.Repository
|
import fr.uca.iut.clfreville2.teaiswarm.model.Repository
|
||||||
|
import fr.uca.iut.clfreville2.teaiswarm.model.search.SearchSettings
|
||||||
|
|
||||||
const val REPOSITORY_OWNER = "repository_owner"
|
const val REPOSITORY_OWNER = "repository_owner"
|
||||||
const val REPOSITORY_NAME = "repository_name"
|
const val REPOSITORY_NAME = "repository_name"
|
||||||
@@ -47,10 +48,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
override fun instantiate(classLoader: ClassLoader, className: String): Fragment =
|
override fun instantiate(classLoader: ClassLoader, className: String): Fragment =
|
||||||
when (className) {
|
when (className) {
|
||||||
RepositoryListFragment::class.java.name -> RepositoryListFragment(
|
RepositoryListFragment::class.java.name -> RepositoryListFragment(
|
||||||
preferences.getString(
|
SearchSettings(), onClick
|
||||||
USERNAME,
|
|
||||||
null
|
|
||||||
)!!, onClick
|
|
||||||
)
|
)
|
||||||
SetupConfigFragment::class.java.name -> SetupConfigFragment(preferences)
|
SetupConfigFragment::class.java.name -> SetupConfigFragment(preferences)
|
||||||
else -> super.instantiate(classLoader, className)
|
else -> super.instantiate(classLoader, className)
|
||||||
|
@@ -15,13 +15,17 @@ import fr.uca.iut.clfreville2.teaiswarm.R
|
|||||||
import fr.uca.iut.clfreville2.teaiswarm.TeaIsWarm
|
import fr.uca.iut.clfreville2.teaiswarm.TeaIsWarm
|
||||||
import fr.uca.iut.clfreville2.teaiswarm.adapter.RepositoryListAdapter
|
import fr.uca.iut.clfreville2.teaiswarm.adapter.RepositoryListAdapter
|
||||||
import fr.uca.iut.clfreville2.teaiswarm.model.Repository
|
import fr.uca.iut.clfreville2.teaiswarm.model.Repository
|
||||||
|
import fr.uca.iut.clfreville2.teaiswarm.model.search.SearchSettings
|
||||||
import fr.uca.iut.clfreville2.teaiswarm.network.RepositoryService
|
import fr.uca.iut.clfreville2.teaiswarm.network.RepositoryService
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
class RepositoryListFragment(private val username: String, private val onClick: (Repository) -> Unit) : Fragment(R.layout.repository_list) {
|
class RepositoryListFragment(
|
||||||
|
private val search: SearchSettings,
|
||||||
|
private val onClick: (Repository) -> Unit
|
||||||
|
) : Fragment(R.layout.repository_list) {
|
||||||
|
|
||||||
private val service = TeaIsWarm.service
|
private val service = TeaIsWarm.service
|
||||||
|
|
||||||
@@ -35,7 +39,7 @@ class RepositoryListFragment(private val username: String, private val onClick:
|
|||||||
factoryProducer = {
|
factoryProducer = {
|
||||||
RepositoryViewModelFactory(
|
RepositoryViewModelFactory(
|
||||||
service,
|
service,
|
||||||
username
|
search
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -53,13 +57,13 @@ class RepositoryListFragment(private val username: String, private val onClick:
|
|||||||
|
|
||||||
class RepositorySource(
|
class RepositorySource(
|
||||||
private val service: RepositoryService,
|
private val service: RepositoryService,
|
||||||
private val username: String
|
private val search: SearchSettings
|
||||||
) : PagingSource<Int, Repository>() {
|
) : PagingSource<Int, Repository>() {
|
||||||
|
|
||||||
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Repository> =
|
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Repository> =
|
||||||
try {
|
try {
|
||||||
val nextPageNumber = params.key ?: 1
|
val nextPageNumber = params.key ?: 1
|
||||||
val response = service.listActiveRepositories(username, nextPageNumber)
|
val response = service.searchRepositories(search.copy(page = nextPageNumber))
|
||||||
LoadResult.Page(
|
LoadResult.Page(
|
||||||
data = response,
|
data = response,
|
||||||
prevKey = nextPageNumber - 1,
|
prevKey = nextPageNumber - 1,
|
||||||
@@ -80,23 +84,23 @@ class RepositoryListFragment(private val username: String, private val onClick:
|
|||||||
|
|
||||||
class RepositoryViewModel(
|
class RepositoryViewModel(
|
||||||
private val service: RepositoryService,
|
private val service: RepositoryService,
|
||||||
private val username: String
|
private val search: SearchSettings
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
val flow = Pager(
|
val flow = Pager(
|
||||||
PagingConfig(pageSize = 10, enablePlaceholders = true)
|
PagingConfig(pageSize = 10, enablePlaceholders = true)
|
||||||
) {
|
) {
|
||||||
RepositorySource(service, username)
|
RepositorySource(service, search)
|
||||||
}.flow.cachedIn(viewModelScope)
|
}.flow.cachedIn(viewModelScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
class RepositoryViewModelFactory(
|
class RepositoryViewModelFactory(
|
||||||
private val service: RepositoryService,
|
private val service: RepositoryService,
|
||||||
private val username: String
|
private val search: SearchSettings
|
||||||
) : ViewModelProvider.Factory {
|
) : ViewModelProvider.Factory {
|
||||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||||
if (modelClass.isAssignableFrom(RepositoryViewModel::class.java)) {
|
if (modelClass.isAssignableFrom(RepositoryViewModel::class.java)) {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
return RepositoryViewModel(service, username) as T
|
return RepositoryViewModel(service, search) as T
|
||||||
}
|
}
|
||||||
throw IllegalArgumentException("Unknown ViewModel class")
|
throw IllegalArgumentException("Unknown ViewModel class")
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,14 @@
|
|||||||
|
package fr.uca.iut.clfreville2.teaiswarm.model
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
|
||||||
|
enum class RepositoryMode {
|
||||||
|
@Json(name = "fork")
|
||||||
|
FORK,
|
||||||
|
@Json(name = "source")
|
||||||
|
SOURCE,
|
||||||
|
@Json(name = "mirror")
|
||||||
|
MIRROR,
|
||||||
|
@Json(name = "collaborative")
|
||||||
|
COLLABORATIVE,
|
||||||
|
}
|
@@ -0,0 +1,3 @@
|
|||||||
|
package fr.uca.iut.clfreville2.teaiswarm.model.search
|
||||||
|
|
||||||
|
data class SearchResults<T>(val data: List<T>)
|
@@ -0,0 +1,15 @@
|
|||||||
|
package fr.uca.iut.clfreville2.teaiswarm.model.search
|
||||||
|
|
||||||
|
import fr.uca.iut.clfreville2.teaiswarm.model.RepositoryMode
|
||||||
|
|
||||||
|
data class SearchSettings(
|
||||||
|
val query: String = "",
|
||||||
|
val userId: Int? = null,
|
||||||
|
val teamId: Int? = null,
|
||||||
|
val starredBy: Int? = null,
|
||||||
|
val mode: RepositoryMode? = null,
|
||||||
|
val sort: SortCriteria = SortCriteria.ALPHA,
|
||||||
|
val order: SortOrder = SortOrder.ASC,
|
||||||
|
val page: Int = 1,
|
||||||
|
val limit: Int = 10
|
||||||
|
)
|
@@ -0,0 +1,16 @@
|
|||||||
|
package fr.uca.iut.clfreville2.teaiswarm.model.search
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
|
||||||
|
enum class SortCriteria {
|
||||||
|
@Json(name = "alpha")
|
||||||
|
ALPHA,
|
||||||
|
@Json(name = "created")
|
||||||
|
CREATED,
|
||||||
|
@Json(name = "updated")
|
||||||
|
UPDATED,
|
||||||
|
@Json(name = "size")
|
||||||
|
SIZE,
|
||||||
|
@Json(name = "id")
|
||||||
|
ID
|
||||||
|
}
|
@@ -0,0 +1,10 @@
|
|||||||
|
package fr.uca.iut.clfreville2.teaiswarm.model.search
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
|
||||||
|
enum class SortOrder {
|
||||||
|
@Json(name = "asc")
|
||||||
|
ASC,
|
||||||
|
@Json(name = "desc")
|
||||||
|
DESC
|
||||||
|
}
|
@@ -4,6 +4,8 @@ import com.squareup.moshi.Moshi
|
|||||||
import com.squareup.moshi.adapters.Rfc3339DateJsonAdapter
|
import com.squareup.moshi.adapters.Rfc3339DateJsonAdapter
|
||||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||||
import fr.uca.iut.clfreville2.teaiswarm.model.*
|
import fr.uca.iut.clfreville2.teaiswarm.model.*
|
||||||
|
import fr.uca.iut.clfreville2.teaiswarm.model.search.SearchResults
|
||||||
|
import fr.uca.iut.clfreville2.teaiswarm.model.search.SearchSettings
|
||||||
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@@ -16,7 +18,7 @@ import retrofit2.http.Path
|
|||||||
import retrofit2.http.Query
|
import retrofit2.http.Query
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
const val HTTP_NOT_FOUND = 404;
|
const val HTTP_NOT_FOUND = 404
|
||||||
|
|
||||||
interface GiteaApiService {
|
interface GiteaApiService {
|
||||||
|
|
||||||
@@ -34,6 +36,19 @@ interface GiteaApiService {
|
|||||||
|
|
||||||
@GET("users/{owner}")
|
@GET("users/{owner}")
|
||||||
suspend fun searchOwner(@Path("owner") owner: String): Owner?
|
suspend fun searchOwner(@Path("owner") owner: String): Owner?
|
||||||
|
|
||||||
|
@GET("repos/search")
|
||||||
|
suspend fun searchRepositories(
|
||||||
|
@Query("q") query: String,
|
||||||
|
@Query("uid") uid: Int?,
|
||||||
|
@Query("team_id") teamId: Int?,
|
||||||
|
@Query("starred_by") starredBy: Int?,
|
||||||
|
@Query("mode") mode: String?,
|
||||||
|
@Query("sort") sort: String,
|
||||||
|
@Query("order") order: String,
|
||||||
|
@Query("page") page: Int,
|
||||||
|
@Query("limit") limit: Int
|
||||||
|
): SearchResults<Repository>
|
||||||
}
|
}
|
||||||
|
|
||||||
class GiteaService(private val handle: GiteaApiService) : RepositoryService {
|
class GiteaService(private val handle: GiteaApiService) : RepositoryService {
|
||||||
@@ -80,6 +95,19 @@ class GiteaService(private val handle: GiteaApiService) : RepositoryService {
|
|||||||
throw ex
|
throw ex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun searchRepositories(settings: SearchSettings): List<Repository> =
|
||||||
|
handle.searchRepositories(
|
||||||
|
settings.query,
|
||||||
|
settings.userId,
|
||||||
|
settings.teamId,
|
||||||
|
settings.starredBy,
|
||||||
|
settings.mode?.toString()?.lowercase(),
|
||||||
|
settings.sort.toString().lowercase(),
|
||||||
|
settings.order.toString().lowercase(),
|
||||||
|
settings.page,
|
||||||
|
settings.limit
|
||||||
|
).data
|
||||||
}
|
}
|
||||||
|
|
||||||
private const val CODEFIRST_API_BASE = "https://codefirst.iut.uca.fr/git/api/v1/"
|
private const val CODEFIRST_API_BASE = "https://codefirst.iut.uca.fr/git/api/v1/"
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package fr.uca.iut.clfreville2.teaiswarm.network
|
package fr.uca.iut.clfreville2.teaiswarm.network
|
||||||
|
|
||||||
import fr.uca.iut.clfreville2.teaiswarm.model.*
|
import fr.uca.iut.clfreville2.teaiswarm.model.*
|
||||||
|
import fr.uca.iut.clfreville2.teaiswarm.model.search.SearchSettings
|
||||||
|
|
||||||
interface RepositoryService {
|
interface RepositoryService {
|
||||||
|
|
||||||
@@ -13,4 +14,6 @@ interface RepositoryService {
|
|||||||
suspend fun retrieveFileContents(repository: RepositoryIdentifiable, filePath: String): FileContent
|
suspend fun retrieveFileContents(repository: RepositoryIdentifiable, filePath: String): FileContent
|
||||||
|
|
||||||
suspend fun searchOwner(owner: String): Owner?
|
suspend fun searchOwner(owner: String): Owner?
|
||||||
|
|
||||||
|
suspend fun searchRepositories(settings: SearchSettings): List<Repository>
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package fr.uca.iut.clfreville2.teaiswarm.network
|
package fr.uca.iut.clfreville2.teaiswarm.network
|
||||||
|
|
||||||
import fr.uca.iut.clfreville2.teaiswarm.model.*
|
import fr.uca.iut.clfreville2.teaiswarm.model.*
|
||||||
|
import fr.uca.iut.clfreville2.teaiswarm.model.search.SearchSettings
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
@@ -69,6 +70,9 @@ class StubRepositoryService : RepositoryService {
|
|||||||
|
|
||||||
override suspend fun searchOwner(owner: String): Owner =
|
override suspend fun searchOwner(owner: String): Owner =
|
||||||
Owner(1, owner)
|
Owner(1, owner)
|
||||||
|
|
||||||
|
override suspend fun searchRepositories(settings: SearchSettings): List<Repository> =
|
||||||
|
listActiveRepositories(settings.query, settings.page)
|
||||||
}
|
}
|
||||||
|
|
||||||
val CHAR_POOL = "abcdefghijklmnopqrstuvwxyz0123456789".toCharArray();
|
val CHAR_POOL = "abcdefghijklmnopqrstuvwxyz0123456789".toCharArray();
|
||||||
|
Reference in New Issue
Block a user