Add comment support

This commit is contained in:
2023-04-09 22:07:42 +02:00
parent 8577dabf76
commit e089cb7e7b
18 changed files with 249 additions and 14 deletions

View File

@@ -3,6 +3,8 @@ plugins {
id 'org.jetbrains.kotlin.android'
}
apply plugin: 'kotlin-kapt'
android {
namespace 'fr.uca.iut.clfreville2.teaiswarm'
compileSdk 33
@@ -10,7 +12,7 @@ android {
defaultConfig {
applicationId "fr.uca.iut.clfreville2.teaiswarm"
minSdk 21
targetSdk 32
targetSdk 33
versionCode 1
versionName "1.0"
@@ -40,8 +42,9 @@ dependencies {
def paging_version = "3.1.1"
def fragment_version = "1.5.6"
def preference_version = "1.2.0"
def room_version = "2.5.1"
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.core:core-ktx:1.10.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.8.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
@@ -60,7 +63,10 @@ dependencies {
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation 'io.coil-kt:coil:2.3.0'
//implementation 'androidx.core:core-ktx:+'
implementation "androidx.room:room-ktx:$room_version"
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'

View File

@@ -14,6 +14,7 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.TeaIsWarm"
android:name=".TeaIsWarm"
tools:targetApi="31">
<activity
android:name=".MainActivity"

View File

@@ -1,10 +1,15 @@
package fr.uca.iut.clfreville2.teaiswarm
import android.app.Application
import fr.uca.iut.clfreville2.teaiswarm.db.TeaDatabase
import fr.uca.iut.clfreville2.teaiswarm.network.GiteaService
import fr.uca.iut.clfreville2.teaiswarm.network.RepositoryService
object TeaIsWarm {
class TeaIsWarm : Application() {
val database: TeaDatabase by lazy {
TeaDatabase.getInstance(this)
}
val service: RepositoryService by lazy {
GiteaService()
}

View File

@@ -0,0 +1,71 @@
package fr.uca.iut.clfreville2.teaiswarm.adapter
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import android.widget.TextView
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.asLiveData
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import fr.uca.iut.clfreville2.teaiswarm.R
import fr.uca.iut.clfreville2.teaiswarm.db.CommentDao
import fr.uca.iut.clfreville2.teaiswarm.db.CommentEntity
import fr.uca.iut.clfreville2.teaiswarm.model.RepositoryIdentifier
class CommentListAdapter : ListAdapter<CommentEntity, CommentListAdapter.CommentViewHolder>(CommentComparator) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CommentViewHolder {
val view: View = LayoutInflater.from(parent.context)
.inflate(R.layout.comment_view_item, parent, false)
return CommentViewHolder(view)
}
override fun onBindViewHolder(holder: CommentViewHolder, position: Int) {
val current = getItem(position)
holder.bind(current.comment)
}
class CommentViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val commentText: EditText = itemView.findViewById(R.id.comment_edit_text)
fun bind(text: String?) {
commentText.setText(text)
}
}
object CommentComparator : DiffUtil.ItemCallback<CommentEntity>() {
override fun areItemsTheSame(oldItem: CommentEntity, newItem: CommentEntity): Boolean {
return oldItem === newItem
}
override fun areContentsTheSame(oldItem: CommentEntity, newItem: CommentEntity): Boolean {
return oldItem.comment == newItem.comment
}
}
class CommentViewModel(
repository: RepositoryIdentifier,
commentDao: CommentDao
) : ViewModel() {
val allItems: LiveData<List<CommentEntity>> =
commentDao.getForRepository(repository.owner, repository.name).asLiveData()
}
class CommentViewModelFactory(
private val repository: RepositoryIdentifier,
private val commentDao: CommentDao
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(CommentViewModel::class.java)) {
@Suppress("UNCHECKED_CAST")
return CommentViewModel(repository, commentDao) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
}

View File

@@ -1,10 +1,11 @@
package fr.uca.iut.clfreville2.teaiswarm
package fr.uca.iut.clfreville2.teaiswarm.adapter
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import fr.uca.iut.clfreville2.teaiswarm.R
import fr.uca.iut.clfreville2.teaiswarm.model.VersionedFile
class FileListAdapter(private val dataSet: List<VersionedFile>, private val onClick: (VersionedFile) -> Unit) :

View File

@@ -0,0 +1,20 @@
package fr.uca.iut.clfreville2.teaiswarm.db
import androidx.room.*
import kotlinx.coroutines.flow.Flow
@Dao
interface CommentDao {
@Insert
fun insert(comment: CommentEntity)
@Update
fun update(comment: CommentEntity)
@Delete
fun delete(comment: CommentEntity)
@Query("SELECT * FROM comment WHERE owner = :owner AND repository = :repository")
fun getForRepository(owner: String, repository: String): Flow<List<CommentEntity>>
}

View File

@@ -0,0 +1,6 @@
package fr.uca.iut.clfreville2.teaiswarm.db
import androidx.room.Entity
@Entity(tableName = "comment", primaryKeys = ["id"])
data class CommentEntity(val id: Int? = null, val owner: String, val repository: String, val comment: String)

View File

@@ -0,0 +1,26 @@
package fr.uca.iut.clfreville2.teaiswarm.db
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
@Database(entities = [CommentEntity::class], version = 1, exportSchema = false)
abstract class TeaDatabase : RoomDatabase() {
abstract fun commentDao(): CommentDao
companion object {
@Volatile
private var instance: TeaDatabase? = null
fun getInstance(ctx: Context): TeaDatabase =
instance ?: synchronized(this) {
Room.databaseBuilder(
ctx.applicationContext,
TeaDatabase::class.java,
"tea.db"
).build().also { instance = it }
}
}
}

View File

@@ -31,7 +31,7 @@ import java.io.IOException
class ActivityListFragment : Fragment(R.layout.activity_list) {
private val service = TeaIsWarm.service
private lateinit var service: RepositoryService
var repository: RepositoryIdentifier? = null
var sha: String? = null
@@ -39,6 +39,7 @@ class ActivityListFragment : Fragment(R.layout.activity_list) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
service = (activity?.application as TeaIsWarm).service
if (view is SwipeRefreshLayout) {
view.setOnRefreshListener {
updateCommits()

View File

@@ -18,7 +18,7 @@ class CodeViewFragment : Fragment(R.layout.code_view_fragment) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val service = TeaIsWarm.service
val service = (activity?.application as TeaIsWarm).service
val bundle = requireArguments()
val repository = RepositoryIdentifier(
bundle.getString(REPOSITORY_OWNER)!!,

View File

@@ -0,0 +1,55 @@
package fr.uca.iut.clfreville2.teaiswarm.fragment
import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import fr.uca.iut.clfreville2.teaiswarm.R
import fr.uca.iut.clfreville2.teaiswarm.REPOSITORY_NAME
import fr.uca.iut.clfreville2.teaiswarm.REPOSITORY_OWNER
import fr.uca.iut.clfreville2.teaiswarm.TeaIsWarm
import fr.uca.iut.clfreville2.teaiswarm.adapter.CommentListAdapter
import fr.uca.iut.clfreville2.teaiswarm.db.CommentEntity
import fr.uca.iut.clfreville2.teaiswarm.model.RepositoryIdentifier
class CommentListFragment : Fragment() {
private lateinit var recyclerView: RecyclerView
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val owner = arguments?.getString(REPOSITORY_OWNER)!!
val repo = arguments?.getString(REPOSITORY_NAME)!!
val repository = RepositoryIdentifier(owner, repo)
val viewModel: CommentListAdapter.CommentViewModel by activityViewModels {
CommentListAdapter.CommentViewModelFactory(
repository,
(activity?.application as TeaIsWarm).database.commentDao()
)
}
(activity?.application as TeaIsWarm).database.commentDao().insert(
CommentEntity(
null,
owner,
repo,
"test"
)
)
recyclerView = view.findViewById(R.id.comment_list)
val adapter = CommentListAdapter()
recyclerView.adapter = adapter
viewModel.allItems.observe(this.viewLifecycleOwner) { items ->
items.let {
adapter.submitList(it)
}
}
recyclerView.layoutManager = LinearLayoutManager(context)
}
}

View File

@@ -6,14 +6,18 @@ import android.widget.Button
import android.widget.TextView
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
import androidx.fragment.app.add
import androidx.fragment.app.commit
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.tabs.TabLayout
import fr.uca.iut.clfreville2.teaiswarm.*
import fr.uca.iut.clfreville2.teaiswarm.adapter.FileListAdapter
import fr.uca.iut.clfreville2.teaiswarm.model.FileType
import fr.uca.iut.clfreville2.teaiswarm.model.RepositoryIdentifier
import fr.uca.iut.clfreville2.teaiswarm.model.VersionedFile
import fr.uca.iut.clfreville2.teaiswarm.network.RepositoryService
import kotlinx.coroutines.launch
import kotlin.io.path.Path
@@ -21,13 +25,13 @@ const val FILE_PATH = "file_path"
class RepositoryDetailsFragment : Fragment(R.layout.repository_details) {
private val service = TeaIsWarm.service
private lateinit var service: RepositoryService
private lateinit var repositoryOwner: TextView
private lateinit var repositoryName: TextView
private lateinit var repositoryDescription: TextView
private lateinit var repositoryStars: TextView
private lateinit var repositoryForks: TextView
private lateinit var activity: Button
private lateinit var activityButton: Button
private lateinit var versionedFiles: RecyclerView
private lateinit var currentRepositoryOwner: String
@@ -36,12 +40,13 @@ class RepositoryDetailsFragment : Fragment(R.layout.repository_details) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
service = (activity?.application as TeaIsWarm).service
repositoryOwner = view.findViewById(R.id.repository_detail_owner)
repositoryName = view.findViewById(R.id.repository_detail_name)
repositoryDescription = view.findViewById(R.id.repository_detail_description)
repositoryStars = view.findViewById(R.id.repo_stars)
repositoryForks = view.findViewById(R.id.repo_forks)
activity = view.findViewById(R.id.repository_detail_activity)
activityButton = view.findViewById(R.id.repository_detail_activity)
val bundle = requireArguments()
currentRepositoryOwner = bundle.getString(REPOSITORY_OWNER)!!
@@ -67,7 +72,7 @@ class RepositoryDetailsFragment : Fragment(R.layout.repository_details) {
}
}
activity.setOnClickListener {
activityButton.setOnClickListener {
findNavController().navigate(R.id.activity_list_fragment, bundle)
}
@@ -75,6 +80,16 @@ class RepositoryDetailsFragment : Fragment(R.layout.repository_details) {
resources.getStringArray(R.array.repository_tabs).forEach {
tabLayout.addTab(tabLayout.newTab().setText(it))
}
parentFragmentManager.commit {
setReorderingAllowed(true)
add<CommentListFragment>(
R.id.comment_fragment_container, args = bundleOf(
REPOSITORY_OWNER to currentRepositoryOwner,
REPOSITORY_NAME to currentRepositoryName
)
)
}
}
private fun adapterOnClick(file: VersionedFile) {

View File

@@ -35,7 +35,7 @@ class RepositoryListFragment(
private val onClick: (Repository) -> Unit
) : Fragment(R.layout.repository_list) {
private val service = TeaIsWarm.service
private lateinit var service: RepositoryService
private var lateInit = true
private var search: SearchSettings by Delegates.observable(initialSearch) { _, _, _ ->
if (!lateInit) {
@@ -54,6 +54,7 @@ class RepositoryListFragment(
view.isRefreshing = false
}
}
service = (activity?.application as TeaIsWarm).service
recyclerView = view.findViewById(R.id.repositories_view)
val spinner: Spinner = view.findViewById(R.id.sort_by_spinner)
ArrayAdapter.createFromResource(

View File

@@ -25,10 +25,11 @@ class SetupConfigFragment(private val preferences: SharedPreferences) : Fragment
super.onViewCreated(view, savedInstanceState)
usernameInput = view.findViewById(R.id.username_input)
confirmButton = view.findViewById(R.id.configure_button)
val service = (activity?.application as TeaIsWarm).service
confirmButton.setOnClickListener {
val username = usernameInput.text.toString()
lifecycleScope.launch {
val owner = TeaIsWarm.service.searchOwner(username)
val owner = service.searchOwner(username)
if (owner is Owner) {
preferences.edit {
putString(USERNAME, usernameInput.text.toString())

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/comment_list"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="@+id/comment_edit_text"
android:layout_width="match_parent"
android:layout_height="150dp"
android:inputType="text|textMultiLine" />
</LinearLayout>

View File

@@ -120,4 +120,9 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="LinearLayoutManager"/>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/comment_fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

View File

@@ -2,5 +2,5 @@
plugins {
id 'com.android.application' version '7.3.0' apply false
id 'com.android.library' version '7.3.0' apply false
id 'org.jetbrains.kotlin.android' version '1.8.20-RC' apply false
id 'org.jetbrains.kotlin.android' version '1.8.20' apply false
}