Added announcement list screen

This commit is contained in:
Sebastian Seedorf
2021-11-20 14:00:23 +01:00
parent 46e431b277
commit 04f7e29b8a
11 changed files with 87 additions and 160 deletions

View File

@@ -8,6 +8,7 @@ import androidx.navigation.navArgument
import de.sebse.fuplanner2.ui.courses.CoursesScreen
import de.sebse.fuplanner2.ui.details.CourseDetailsScreen
import de.sebse.fuplanner2.ui.details_announcements.CourseAnnouncementScreen
import de.sebse.fuplanner2.ui.details_announcements.CourseAnnouncementsScreen
import de.sebse.fuplanner2.ui.details_description.CourseDescriptionScreen
sealed class MenuItem {
@@ -53,6 +54,15 @@ fun MainActivityComposable() {
val id = it.arguments!!.getLong("id")
CourseDescriptionScreen(tools, id)
}
composable(
arguments = listOf(
navArgument("id") { type = NavType.LongType }
),
route = "${MenuItem.Courses.route}/{id}/announcements"
) {
val id = it.arguments!!.getLong("id")
CourseAnnouncementsScreen(tools, id)
}
composable(
arguments = listOf(
navArgument("id") { type = NavType.LongType },

View File

@@ -1,5 +1,6 @@
package de.sebse.fuplanner2.ui.details
import android.content.res.Configuration
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable
@@ -91,7 +92,8 @@ fun CourseDetailsScreen(
}
}
@Preview
@Preview(showBackground = true)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, showBackground = true)
@Composable
fun CourseDetailsScreenPreview(@PreviewParameter(CoursePreviewProvider::class, 1) course: Course) {
AppTheme {

View File

@@ -14,11 +14,6 @@ import de.sebse.fuplanner2.ui.theme.AppTheme
import de.sebse.fuplanner2.ui.tools.previews.AnnouncementPreviewProvider
import de.sebse.fuplanner2.utils.toDateTimeString
@Composable
fun AnnouncementItem(announcement: Announcement) {
AnnouncementItem(announcement) { }
}
@Composable
fun AnnouncementItem(announcement: Announcement, click: () -> Unit) {
FuCardColumn(
@@ -40,6 +35,6 @@ fun AnnouncementItem(announcement: Announcement, click: () -> Unit) {
@Composable
fun AnnouncementItemPreview(@PreviewParameter(AnnouncementPreviewProvider::class, 5) announcement: Announcement) {
AppTheme {
AnnouncementItem(announcement)
AnnouncementItem(announcement) { }
}
}

View File

@@ -1,12 +1,13 @@
package de.sebse.fuplanner2.ui.details.components
import android.content.res.Configuration
import androidx.annotation.StringRes
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.GridCells
import androidx.compose.material.Card
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults.buttonColors
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
@@ -37,28 +38,28 @@ fun QuickLinks(courseId: Long, onClick: (String) -> Unit) {
cells = GridCells.Adaptive(150.dp)
) {
list.forEach {
Card(
backgroundColor = MaterialTheme.colors.secondary,
Button(
colors = buttonColors(
backgroundColor = MaterialTheme.colors.secondary,
contentColor = MaterialTheme.colors.onSecondary,
),
modifier = Modifier
.padding(dimensionResource(R.dimen.card_view_margin))
.fillMaxWidth()
.clickable { onClick(it.route) },
elevation = dimensionResource(R.dimen.card_view_elevation),
.fillMaxWidth(),
onClick = { onClick(it.route) }
) {
Text(
text = stringResource(it.name),
color = MaterialTheme.colors.onSecondary,
textAlign = TextAlign.Center,
style = MaterialTheme.typography.h6,
modifier = Modifier
.padding(dimensionResource(R.dimen.card_view_padding))
style = MaterialTheme.typography.h6
)
}
}
}
}
@Preview
@Preview(showBackground = true)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, showBackground = true)
@Composable
fun QuickLinksPreview() {
AppTheme {

View File

@@ -1,40 +0,0 @@
package de.sebse.fuplanner2.ui.details_announcements
import android.view.ViewGroup
import androidx.paging.PagedListAdapter
import androidx.recyclerview.widget.DiffUtil
import de.sebse.fuplanner2.database.Announcement
import de.sebse.fuplanner2.ui.ListItemHolder
import de.sebse.fuplanner2.utils.toDateTimeString
class AnnouncementsAdapter() : PagedListAdapter<Announcement, ListItemHolder>(EventDiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListItemHolder {
return ListItemHolder.invoke(parent)
}
override fun onBindViewHolder(holder: ListItemHolder, position: Int) {
val event = getItem(position)
val actCtx = holder.itemView.context
event?.let {
holder.title.text = it.title
holder.subLeft.text = it.createdOn.toDateTimeString(actCtx)
holder.subRight.text = it.createdBy
} ?: run {
holder.clear()
holder.itemView.setOnClickListener(null)
}
}
}
class EventDiffCallback : DiffUtil.ItemCallback<Announcement>() {
override fun areItemsTheSame(oldItem: Announcement, newItem: Announcement): Boolean {
return oldItem.uid == newItem.uid
}
override fun areContentsTheSame(oldItem: Announcement, newItem: Announcement): Boolean {
return oldItem.getHash() == newItem.getHash()
}
}

View File

@@ -1,64 +0,0 @@
package de.sebse.fuplanner2.ui.details_announcements
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer
import androidx.navigation.NavController
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.work.workDataOf
import de.sebse.fuplanner2.R
import de.sebse.fuplanner2.auth.AppAccounts
import de.sebse.fuplanner2.databinding.FragmentRefreshRecyclerBinding
import de.sebse.fuplanner2.utils.console
import de.sebse.fuplanner2.utils.enqueueOneTimeWork
import de.sebse.fuplanner2.worker.AbstractAccountWorker.Companion.KEY_ACCOUNT_NAME
import de.sebse.fuplanner2.worker.AbstractAccountWorker.Companion.KEY_COURSE_ID
import de.sebse.fuplanner2.worker.AnnouncementWorker
class AnnouncementsFragment : Fragment() {
private var title: String = ""
private val args: AnnouncementsFragmentArgs by navArgs()
private val announcementsViewModel: AnnouncementsViewModel by viewModels { AnnouncementsViewModelFactory(args.courseId) }
private lateinit var navController: NavController
private lateinit var binding: FragmentRefreshRecyclerBinding
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val viewManager = LinearLayoutManager(context)
val viewAdapter = AnnouncementsAdapter()
return inflater.inflate(R.layout.fragment_refresh_recycler, container, false).apply {
console.log(findViewById(R.id.recycler_view), binding.recyclerView)
binding.recyclerView.apply {
setHasFixedSize(true)
layoutManager = viewManager
adapter = viewAdapter
}
binding.swipeRefreshLayout.setOnRefreshListener {
enqueueOneTimeWork<AnnouncementWorker>(context.applicationContext) {
it.setInputData(workDataOf(
KEY_ACCOUNT_NAME to AppAccounts.getInstance().selectedAccount?.name,
KEY_COURSE_ID to args.courseId
))
}.observe(viewLifecycleOwner, Observer {
if (it.state.isFinished)
binding.swipeRefreshLayout.isRefreshing = false
})
}
announcementsViewModel.events.observe(viewLifecycleOwner, Observer {
viewAdapter.submitList(it)
this@AnnouncementsFragment.title = args.title
})
navController = findNavController()
}
}
}

View File

@@ -1,19 +0,0 @@
package de.sebse.fuplanner2.ui.details_announcements
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.paging.LivePagedListBuilder
import androidx.paging.PagedList
import de.sebse.fuplanner2.database.Announcement
import de.sebse.fuplanner2.database.AppDatabase
class AnnouncementsViewModelFactory(private val courseId: Long): ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel> create(modelClass: Class<T>): T = AnnouncementsViewModel(courseId) as T
}
class AnnouncementsViewModel(private val courseId: Long) : ViewModel() {
private val factory = AppDatabase.getInstance().announcementDao().getAll1(courseId)
val events: LiveData<PagedList<Announcement>> = LivePagedListBuilder(factory, 50).build()
}

View File

@@ -16,11 +16,10 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import de.sebse.fuplanner2.R
import de.sebse.fuplanner2.Tools
import de.sebse.fuplanner2.database.Announcement
import de.sebse.fuplanner2.database.Course
import de.sebse.fuplanner2.ui.shared.Heading
import de.sebse.fuplanner2.ui.shared.HtmlText
import de.sebse.fuplanner2.ui.theme.AppTheme
import de.sebse.fuplanner2.ui.tools.previews.CoursePreviewProvider
import de.sebse.fuplanner2.ui.tools.previews.AnnouncementPreviewProvider
import de.sebse.fuplanner2.ui.tools.viewmodels.AnnouncementViewModel
import de.sebse.fuplanner2.ui.tools.viewmodels.AnnouncementViewModelFactory
import de.sebse.fuplanner2.ui.tools.viewmodels.DetailsViewModel
@@ -57,10 +56,8 @@ fun CourseAnnouncementScreen(announcement: Announcement) {
@Preview
@Composable
fun CourseAnnouncementScreenPreview(@PreviewParameter(CoursePreviewProvider::class, 1) course: Course) {
fun CourseAnnouncementScreenPreview(@PreviewParameter(AnnouncementPreviewProvider::class, 2) announcement: Announcement) {
AppTheme {
/*CourseAnnouncementScreen(
"scrip<b>fsdfsfg</b><br><a href='example.com'>ti</a>on"
)*/
CourseAnnouncementScreen(announcement)
}
}

View File

@@ -0,0 +1,56 @@
package de.sebse.fuplanner2.ui.details_announcements
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.viewmodel.compose.viewModel
import de.sebse.fuplanner2.MenuItem
import de.sebse.fuplanner2.R
import de.sebse.fuplanner2.Tools
import de.sebse.fuplanner2.database.Announcement
import de.sebse.fuplanner2.ui.details.components.AnnouncementItem
import de.sebse.fuplanner2.ui.theme.AppTheme
import de.sebse.fuplanner2.ui.tools.previews.AnnouncementPreviewProvider
import de.sebse.fuplanner2.ui.tools.viewmodels.DetailsViewModel
import de.sebse.fuplanner2.ui.tools.viewmodels.DetailsViewModelFactory
@Composable
fun CourseAnnouncementsScreen(tools: Tools, id: Long) {
val coursesViewModel: DetailsViewModel =
viewModel(factory = DetailsViewModelFactory(id))
val course by coursesViewModel.course.observeAsState()
val announcements by coursesViewModel.announcements.observeAsState()
val title = course?.title ?: stringResource(id = R.string.description)
LaunchedEffect(title) {
tools.setTitle(title)
}
announcements?.let { CourseAnnouncementsScreen(it) { uid ->
tools.navTo("${MenuItem.Courses.route}/$id/announcements/${uid}")
} }
}
@Composable
fun CourseAnnouncementsScreen(announcements: List<Announcement>, onClick: (Long) -> Unit) {
LazyColumn {
items(announcements) {
AnnouncementItem(it) {
it.uid?.let { uid -> onClick(uid) }
}
}
}
}
@Preview
@Composable
fun CourseAnnouncementsScreenPreview() {
AppTheme {
CourseAnnouncementsScreen(
AnnouncementPreviewProvider().values.take(10).toList()
) { }
}
}

View File

@@ -23,6 +23,7 @@ fun Heading(text: String, onClick: (() -> Unit)? = null) {
Text(
text = text,
style = MaterialTheme.typography.h5,
color = MaterialTheme.colors.onSurface,
modifier = Modifier
.padding(top = dimensionResource(id = R.dimen.header_padding))
)

View File

@@ -27,18 +27,6 @@
android:name="title"
app:argType="string" />
</fragment>
<fragment
android:id="@+id/course_announcements"
android:name="de.sebse.fuplanner2.ui.details_announcements.AnnouncementsFragment"
android:label="{title}"
tools:layout="@layout/fragment_refresh_recycler">
<argument
android:name="courseId"
app:argType="long" />
<argument
android:name="title"
app:argType="string" />
</fragment>
<fragment
android:id="@+id/nav_notifications"
android:name="de.sebse.fuplanner2.ui.notification.NotificationFragment"