Add nick for student (#1119)

This commit is contained in:
Rafał Borcz
2021-02-01 23:58:44 +01:00
committed by GitHub
parent 39534aeda4
commit 3e3a080b70
28 changed files with 2857 additions and 313 deletions

View File

@ -9,6 +9,7 @@ import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.databinding.ItemAccountBinding
import io.github.wulkanowy.utils.getThemeAttrColor
import io.github.wulkanowy.utils.nickOrName
import javax.inject.Inject
class WidgetConfigureAdapter @Inject constructor() : RecyclerView.Adapter<WidgetConfigureAdapter.ItemViewHolder>() {
@ -28,7 +29,7 @@ class WidgetConfigureAdapter @Inject constructor() : RecyclerView.Adapter<Widget
val (student, isCurrent) = items[position]
with(holder.binding) {
accountItemName.text = "${student.studentName} ${student.className}"
accountItemName.text = "${student.nickOrName} ${student.className}"
accountItemSchool.text = student.schoolName
with(accountItemImage) {

View File

@ -12,6 +12,7 @@ import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.databinding.HeaderAccountBinding
import io.github.wulkanowy.databinding.ItemAccountBinding
import io.github.wulkanowy.utils.getThemeAttrColor
import io.github.wulkanowy.utils.nickOrName
import javax.inject.Inject
class AccountAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
@ -84,7 +85,7 @@ class AccountAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView.V
}.size > 1 && isAccountQuickDialogMode
with(binding) {
accountItemName.text = "${student.studentName} ${diary?.diaryName.orEmpty()}"
accountItemName.text = "${student.nickOrName} ${diary?.diaryName.orEmpty()}"
accountItemSchool.text = studentWithSemesters.student.schoolName
accountItemAccountType.setText(if (student.isParent) R.string.account_type_parent else R.string.account_type_student)
accountItemAccountType.visibility = if (isDuplicatedStudent) VISIBLE else GONE

View File

@ -62,10 +62,16 @@ class AccountPresenter @Inject constructor(
}
private fun loadData() {
flowWithResource { studentRepository.getSavedStudents() }
flowWithResource { studentRepository.getSavedStudents(false) }
.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Loading account data started")
Status.LOADING -> {
Timber.i("Loading account data started")
view?.run {
showProgress(true)
showContent(false)
}
}
Status.SUCCESS -> {
Timber.i("Loading account result: Success")
view?.updateData(createAccountItems(it.data!!))

View File

@ -9,13 +9,16 @@ import androidx.appcompat.app.AlertDialog
import androidx.core.view.get
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.databinding.FragmentAccountDetailsBinding
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.account.accountedit.AccountEditDialog
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoFragment
import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoView
import io.github.wulkanowy.utils.nickOrName
import javax.inject.Inject
@AndroidEntryPoint
@ -43,22 +46,19 @@ class AccountDetailsFragment :
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
arguments?.let {
presenter.studentWithSemesters =
it.getSerializable(ARGUMENT_KEY) as StudentWithSemesters
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = FragmentAccountDetailsBinding.bind(view)
presenter.onAttachView(this)
presenter.onAttachView(this, requireArguments()[ARGUMENT_KEY] as StudentWithSemesters)
}
override fun initView() {
binding.accountDetailsErrorRetry.setOnClickListener { presenter.onRetry() }
binding.accountDetailsErrorDetails.setOnClickListener { presenter.onDetailsClick() }
binding.accountDetailsLogout.setOnClickListener { presenter.onRemoveSelected() }
binding.accountDetailsSelect.setOnClickListener { presenter.onStudentSelect() }
binding.accountDetailsSelect.isEnabled = !presenter.studentWithSemesters.student.isCurrent
binding.accountDetailsPersonalData.setOnClickListener {
presenter.onStudentInfoSelected(StudentInfoView.Type.PERSONAL)
@ -76,24 +76,31 @@ class AccountDetailsFragment :
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
menu[0].isVisible = false
inflater.inflate(R.menu.action_menu_account_details, menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return if (item.itemId == R.id.accountDetailsMenuEdit) {
showAccountEditDetailsDialog()
return true
presenter.onAccountEditSelected()
true
} else false
}
override fun showAccountData(studentWithSemesters: StudentWithSemesters) {
override fun showAccountData(student: Student) {
with(binding) {
accountDetailsName.text = studentWithSemesters.student.studentName
accountDetailsSchool.text = studentWithSemesters.student.schoolName
accountDetailsName.text = student.nickOrName
accountDetailsSchool.text = student.schoolName
}
}
override fun showAccountEditDetailsDialog() {
(requireActivity() as MainActivity).showDialogFragment(AccountEditDetailsDialog.newInstance())
override fun enableSelectStudentButton(enable: Boolean) {
binding.accountDetailsSelect.isEnabled = enable
}
override fun showAccountEditDetailsDialog(student: Student) {
(requireActivity() as MainActivity).showDialogFragment(
AccountEditDialog.newInstance(student)
)
}
override fun showLogoutConfirmDialog() {
@ -127,6 +134,22 @@ class AccountDetailsFragment :
)
}
override fun showErrorView(show: Boolean) {
binding.accountDetailsError.visibility = if (show) View.VISIBLE else View.GONE
}
override fun setErrorDetails(message: String) {
binding.accountDetailsErrorMessage.text = message
}
override fun showProgress(show: Boolean) {
binding.accountDetailsProgress.visibility = if (show) View.VISIBLE else View.GONE
}
override fun showContent(show: Boolean) {
binding.accountDetailsContent.visibility = if (show) View.VISIBLE else View.GONE
}
override fun onDestroyView() {
presenter.onDetachView()
super.onDestroyView()

View File

@ -1,5 +1,6 @@
package io.github.wulkanowy.ui.modules.account.accountdetails
import io.github.wulkanowy.data.Resource
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.data.repositories.StudentRepository
@ -9,6 +10,7 @@ import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoView
import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResource
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@ -19,14 +21,74 @@ class AccountDetailsPresenter @Inject constructor(
private val syncManager: SyncManager
) : BasePresenter<AccountDetailsView>(errorHandler, studentRepository) {
lateinit var studentWithSemesters: StudentWithSemesters
private lateinit var studentWithSemesters: StudentWithSemesters
override fun onAttachView(view: AccountDetailsView) {
private lateinit var lastError: Throwable
private var studentId: Long? = null
fun onAttachView(view: AccountDetailsView, studentWithSemesters: StudentWithSemesters) {
super.onAttachView(view)
view.initView()
Timber.i("Account details view was initialized")
studentId = studentWithSemesters.student.id
view.showAccountData(studentWithSemesters)
view.initView()
errorHandler.showErrorMessage = ::showErrorViewOnError
Timber.i("Account details view was initialized")
loadData()
}
fun onRetry() {
view?.run {
showErrorView(false)
showProgress(true)
}
loadData()
}
fun onDetailsClick() {
view?.showErrorDetailsDialog(lastError)
}
private fun loadData() {
flowWithResource { studentRepository.getSavedStudents() }
.map { studentWithSemesters ->
Resource(
data = studentWithSemesters.data?.single { it.student.id == studentId },
status = studentWithSemesters.status,
error = studentWithSemesters.error
)
}
.onEach {
when (it.status) {
Status.LOADING -> {
view?.run {
showProgress(true)
showContent(false)
}
Timber.i("Loading account details view started")
}
Status.SUCCESS -> {
Timber.i("Loading account details view result: Success")
studentWithSemesters = it.data!!
view?.run {
showAccountData(studentWithSemesters.student)
enableSelectStudentButton(!studentWithSemesters.student.isCurrent)
showContent(true)
showErrorView(false)
}
}
Status.ERROR -> {
Timber.i("Loading account details view result: An exception occurred")
errorHandler.dispatch(it.error!!)
}
}
}
.afterLoading { view?.showProgress(false) }
.launch()
}
fun onAccountEditSelected() {
view?.showAccountEditDetailsDialog(studentWithSemesters.student)
}
fun onStudentInfoSelected(infoType: StudentInfoView.Type) {
@ -97,4 +159,14 @@ class AccountDetailsPresenter @Inject constructor(
view?.popView()
}.launch("logout")
}
private fun showErrorViewOnError(message: String, error: Throwable) {
view?.run {
lastError = error
setErrorDetails(message)
showErrorView(true)
showContent(false)
showProgress(false)
}
}
}

View File

@ -1,5 +1,6 @@
package io.github.wulkanowy.ui.modules.account.accountdetails
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.ui.base.BaseView
import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoView
@ -8,9 +9,9 @@ interface AccountDetailsView : BaseView {
fun initView()
fun showAccountData(studentWithSemesters: StudentWithSemesters)
fun showAccountData(student: Student)
fun showAccountEditDetailsDialog()
fun showAccountEditDetailsDialog(student: Student)
fun showLogoutConfirmDialog()
@ -18,8 +19,18 @@ interface AccountDetailsView : BaseView {
fun recreateMainView()
fun enableSelectStudentButton(enable: Boolean)
fun openStudentInfoView(
infoType: StudentInfoView.Type,
studentWithSemesters: StudentWithSemesters
)
fun showErrorView(show: Boolean)
fun setErrorDetails(message: String)
fun showProgress(show: Boolean)
fun showContent(show: Boolean)
}

View File

@ -1,38 +0,0 @@
package io.github.wulkanowy.ui.modules.account.accountdetails
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.DialogFragment
import io.github.wulkanowy.databinding.DialogAccountEditDetailsBinding
import io.github.wulkanowy.utils.lifecycleAwareVariable
class AccountEditDetailsDialog : DialogFragment() {
private var binding: DialogAccountEditDetailsBinding by lifecycleAwareVariable()
companion object {
fun newInstance() = AccountEditDetailsDialog()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(STYLE_NO_TITLE, 0)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return DialogAccountEditDetailsBinding.inflate(inflater).apply { binding = this }.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.accountEditDetailsCancel.setOnClickListener { dismiss() }
}
}

View File

@ -0,0 +1,72 @@
package io.github.wulkanowy.ui.modules.account.accountedit
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.databinding.DialogAccountEditBinding
import io.github.wulkanowy.ui.base.BaseDialogFragment
import javax.inject.Inject
@AndroidEntryPoint
class AccountEditDialog : BaseDialogFragment<DialogAccountEditBinding>(), AccountEditView {
@Inject
lateinit var presenter: AccountEditPresenter
companion object {
private const val ARGUMENT_KEY = "student_with_semesters"
fun newInstance(student: Student) =
AccountEditDialog().apply {
arguments = Bundle().apply {
putSerializable(ARGUMENT_KEY, student)
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(STYLE_NO_TITLE, 0)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View = DialogAccountEditBinding.inflate(inflater).apply { binding = this }.root
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
presenter.onAttachView(this, requireArguments()[ARGUMENT_KEY] as Student)
}
override fun initView() {
with(binding) {
accountEditDetailsCancel.setOnClickListener { dismiss() }
accountEditDetailsSave.setOnClickListener {
presenter.changeStudentNick(binding.accountEditDetailsNickText.text.toString())
}
}
}
override fun showCurrentNick(nick: String) {
binding.accountEditDetailsNickText.setText(nick)
}
override fun popView() {
dismiss()
}
override fun recreateMainView() {
activity?.recreate()
}
override fun onDestroyView() {
super.onDestroyView()
presenter.onDetachView()
}
}

View File

@ -0,0 +1,54 @@
package io.github.wulkanowy.ui.modules.account.accountedit
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentNick
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResource
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
class AccountEditPresenter @Inject constructor(
errorHandler: ErrorHandler,
studentRepository: StudentRepository
) : BasePresenter<AccountEditView>(errorHandler, studentRepository) {
lateinit var student: Student
fun onAttachView(view: AccountEditView, student: Student) {
super.onAttachView(view)
this.student = student
with(view) {
initView()
showCurrentNick(student.nick.trim())
}
Timber.i("Account edit dialog view was initialized")
}
fun changeStudentNick(nick: String) {
flowWithResource {
val studentNick =
StudentNick(nick = nick.trim()).apply { id = student.id }
studentRepository.updateStudentNick(studentNick)
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Attempt to change a student nick")
Status.SUCCESS -> {
Timber.i("Change a student nick result: Success")
view?.recreateMainView()
}
Status.ERROR -> {
Timber.i("Change a student result: An exception occurred")
errorHandler.dispatch(it.error!!)
}
}
}
.afterLoading { view?.popView() }
.launch()
}
}

View File

@ -0,0 +1,14 @@
package io.github.wulkanowy.ui.modules.account.accountedit
import io.github.wulkanowy.ui.base.BaseView
interface AccountEditView : BaseView {
fun initView()
fun popView()
fun recreateMainView()
fun showCurrentNick(nick: String)
}

View File

@ -52,9 +52,9 @@ class AccountQuickPresenter @Inject constructor(
errorHandler.dispatch(it.error!!)
}
}
}.afterLoading {
view?.popView()
}.launch("switch")
}
.afterLoading { view?.popView() }
.launch("switch")
}
private fun loadData() {

View File

@ -109,6 +109,7 @@ class StudentInfoFragment :
listOf(
getString(R.string.student_info_first_name) to studentInfo.firstName,
getString(R.string.student_info_second_name) to studentInfo.secondName,
getString(R.string.student_info_last_name) to studentInfo.surname,
getString(R.string.student_info_gender) to getString(if (studentInfo.gender == Gender.MALE) R.string.student_info_male else R.string.student_info_female),
getString(R.string.student_info_polish_citizenship) to getString(if (studentInfo.hasPolishCitizenship) R.string.all_yes else R.string.all_no),
getString(R.string.student_info_family_name) to studentInfo.familyName,

View File

@ -27,6 +27,7 @@ import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.nextOrSameSchoolDay
import io.github.wulkanowy.utils.nextSchoolDay
import io.github.wulkanowy.utils.nickOrName
import io.github.wulkanowy.utils.previousSchoolDay
import io.github.wulkanowy.utils.toFormattedString
import kotlinx.coroutines.GlobalScope
@ -151,8 +152,14 @@ class TimetableWidgetProvider : HiltBroadcastReceiver() {
val remoteView = RemoteViews(context.packageName, layoutId).apply {
setEmptyView(R.id.timetableWidgetList, R.id.timetableWidgetEmpty)
setTextViewText(R.id.timetableWidgetDate, date.toFormattedString("EEEE, dd.MM").capitalize())
setTextViewText(R.id.timetableWidgetName, student?.studentName ?: context.getString(R.string.all_no_data))
setTextViewText(
R.id.timetableWidgetDate,
date.toFormattedString("EEEE, dd.MM").capitalize()
)
setTextViewText(
R.id.timetableWidgetName,
student?.nickOrName ?: context.getString(R.string.all_no_data)
)
setRemoteAdapter(R.id.timetableWidgetList, adapterIntent)
setOnClickPendingIntent(R.id.timetableWidgetNext, nextNavIntent)
setOnClickPendingIntent(R.id.timetableWidgetPrev, prevNavIntent)