Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor #1620: migrated notification screen to compose #1621

Merged
merged 6 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class FetchNotifications @Inject constructor(private val mFineractRepository: Fi

class RequestValues(val clientId: Long) : UseCase.RequestValues
class ResponseValue(
val notificationPayloadList: List<NotificationPayload?>
val notificationPayloadList: List<NotificationPayload>?
) : UseCase.ResponseValue

override fun executeUseCase(requestValues: RequestValues) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package org.mifospay.notification.presenter

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.mifospay.core.model.domain.NotificationPayload
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import org.mifospay.core.data.base.UseCase
import org.mifospay.core.data.base.UseCaseHandler
import org.mifospay.core.data.domain.usecase.notification.FetchNotifications
import org.mifospay.data.local.LocalRepository
import javax.inject.Inject

@HiltViewModel
class NotificationViewModel @Inject constructor(
private val mUseCaseHandler: UseCaseHandler,
private val mLocalRepository: LocalRepository,
private val fetchNotificationsUseCase: FetchNotifications
) : ViewModel() {

private val _notificationUiState: MutableStateFlow<NotificationUiState> =
MutableStateFlow(NotificationUiState.Loading)
val notificationUiState: StateFlow<NotificationUiState> = _notificationUiState

private val _isRefreshing = MutableStateFlow(false)
val isRefreshing: StateFlow<Boolean> get() = _isRefreshing.asStateFlow()

fun refresh() {
viewModelScope.launch {
_isRefreshing.emit(true)
fetchNotifications()
_isRefreshing.emit(false)
therajanmaurya marked this conversation as resolved.
Show resolved Hide resolved
}
}

fun fetchNotifications() {
mUseCaseHandler.execute(fetchNotificationsUseCase,
FetchNotifications.RequestValues(
mLocalRepository.clientDetails.clientId
),
object : UseCase.UseCaseCallback<FetchNotifications.ResponseValue> {
override fun onSuccess(response: FetchNotifications.ResponseValue) {
_notificationUiState.value =
NotificationUiState.Success(response.notificationPayloadList.orEmpty())
}

override fun onError(message: String) {
_notificationUiState.value = NotificationUiState.Error(message)
}
})
}
}

sealed interface NotificationUiState {
data object Loading : NotificationUiState
data class Success(val notificationList: List<NotificationPayload>) : NotificationUiState
data class Error(val message: String) : NotificationUiState
}
Original file line number Diff line number Diff line change
@@ -1,102 +1,25 @@
package org.mifospay.notification.ui

import android.os.Bundle
import android.view.View
import android.widget.TextView
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import butterknife.BindView
import butterknife.ButterKnife
import androidx.activity.compose.setContent
import dagger.hilt.android.AndroidEntryPoint
import com.mifospay.core.model.domain.NotificationPayload
import org.mifospay.R
import org.mifospay.base.BaseActivity
import org.mifospay.notification.NotificationContract
import org.mifospay.notification.NotificationContract.NotificationView
import org.mifospay.notification.presenter.NotificationPresenter
import org.mifospay.common.Constants
import org.mifospay.utils.DebugUtil
import org.mifospay.utils.Toaster
import javax.inject.Inject
import org.mifospay.theme.MifosTheme

/**
* This activity is to view notifications record.
* Notifications Datatable is populated automatically by server when an event happens.
* This feature is yet to be implemented on the server side.
*/
@AndroidEntryPoint
class NotificationActivity : BaseActivity(), NotificationView {
@JvmField
@Inject
var mPresenter: NotificationPresenter? = null
var mNotificationPresenter: NotificationContract.NotificationPresenter? = null
class NotificationActivity : BaseActivity() {

@JvmField
@BindView(R.id.rv_notification)
var mRvNotification: RecyclerView? = null

@JvmField
@BindView(R.id.tv_placeholder)
var tvplaceholder: TextView? = null

@JvmField
@Inject
var mNotificationAdapter: NotificationAdapter? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_notification)
ButterKnife.bind(this)
setToolbarTitle("Notifications")
showColoredBackButton(R.drawable.ic_arrow_back_black_24dp)
setupRecyclerView()
setupSwipeRefreshLayout()
mPresenter?.attachView(this)
showSwipeProgress()
mNotificationPresenter?.fetchNotifications()
}

private fun setupRecyclerView() {
mRvNotification?.layoutManager = LinearLayoutManager(this)
mRvNotification?.adapter = mNotificationAdapter
mRvNotification?.addItemDecoration(
DividerItemDecoration(
this,
DividerItemDecoration.VERTICAL
)
)
}

private fun setupSwipeRefreshLayout() {
setSwipeRefreshEnabled(true)
swipeRefreshLayout?.setOnRefreshListener { mNotificationPresenter?.fetchNotifications() }
}

override fun fetchNotificationsSuccess(notificationPayloadList: List<NotificationPayload?>?) {
hideSwipeProgress()
if (notificationPayloadList.isNullOrEmpty()) {
DebugUtil.log("null")
mRvNotification?.visibility = View.GONE
tvplaceholder?.visibility = View.VISIBLE
} else {
DebugUtil.log("yes")
mRvNotification?.visibility = View.VISIBLE
tvplaceholder?.visibility = View.GONE
mNotificationAdapter?.setNotificationPayloadList(notificationPayloadList as List<NotificationPayload>)
setContent {
MifosTheme {
NotificationScreen()
}
}
mNotificationAdapter?.setNotificationPayloadList(notificationPayloadList as List<NotificationPayload>)
}

override fun fetchNotificationsError(message: String?) {
hideSwipeProgress()
showToast(message)
}

override fun setPresenter(presenter: NotificationContract.NotificationPresenter?) {
mNotificationPresenter = presenter
}

fun showToast(message: String?) {
Toaster.showToast(this, message)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
package org.mifospay.notification.ui

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Info
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.Card
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.mifospay.core.model.domain.NotificationPayload
import org.mifospay.R
import org.mifospay.core.designsystem.component.MfLoadingWheel
import org.mifospay.core.designsystem.component.MifosTopAppBar
import org.mifospay.core.designsystem.theme.MifosTheme
import org.mifospay.core.designsystem.theme.historyItemTextStyle
import org.mifospay.core.designsystem.theme.styleMedium16sp
import org.mifospay.core.ui.EmptyContentScreen
import org.mifospay.notification.presenter.NotificationUiState
import org.mifospay.notification.presenter.NotificationViewModel

@Composable
fun NotificationScreen(viewmodel: NotificationViewModel = hiltViewModel()) {
val uiState by viewmodel.notificationUiState.collectAsStateWithLifecycle()
val isRefreshing by viewmodel.isRefreshing.collectAsStateWithLifecycle()
NotificationScreen(
uiState = uiState,
isRefreshing = isRefreshing,
onRefresh = {
viewmodel.refresh()
}
)
}

@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class)
@Composable
fun NotificationScreen(
uiState: NotificationUiState,
isRefreshing: Boolean,
onRefresh: () -> Unit
) {
val pullRefreshState = rememberPullRefreshState(isRefreshing, onRefresh)
Box(Modifier.pullRefresh(pullRefreshState)) {
Column(
modifier = Modifier.fillMaxSize()
) {
MifosTopAppBar(titleRes = R.string.notifications)
when (uiState) {
is NotificationUiState.Error -> {
EmptyContentScreen(
modifier = Modifier,
title = stringResource(id = R.string.error_oops),
subTitle = stringResource(id = R.string.unexpected_error_subtitle),
iconTint = Color.Black,
iconImageVector = Icons.Rounded.Info
)
}

NotificationUiState.Loading -> {
MfLoadingWheel(
contentDesc = stringResource(R.string.loading),
backgroundColor = Color.White
)
}

is NotificationUiState.Success -> {
if (uiState.notificationList.isEmpty()) {
EmptyContentScreen(
modifier = Modifier,
title = stringResource(R.string.nothing_to_notify),
subTitle = stringResource(R.string.there_is_nothing_to_show),
iconTint = Color.Black,
iconImageVector = Icons.Rounded.Info
)
} else {
LazyColumn {
items(uiState.notificationList) { notification ->
NotificationListItem(
title = notification.title.toString(),
body = notification.body.toString(),
timestamp = notification.timestamp.toString()
)
}
}
}
}
}
}
PullRefreshIndicator(
refreshing = isRefreshing,
state = pullRefreshState,
modifier = Modifier.align(Alignment.TopCenter)
)
}
}

@Composable
fun NotificationListItem(title: String, body: String, timestamp: String) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
shape = RoundedCornerShape(12.dp)
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(24.dp)
) {
Text(
text = title,
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 8.dp)
)
Text(
text = body, style = MaterialTheme.typography.bodyMedium,
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 8.dp)
)
Text(
text = timestamp, style = MaterialTheme.typography.bodySmall,
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 8.dp)
)
}
}
}

class NotificationUiStateProvider :
PreviewParameterProvider<NotificationUiState> {
override val values: Sequence<NotificationUiState>
get() = sequenceOf(
NotificationUiState.Success(sampleNotificationList),
NotificationUiState.Error("Error Occurred"),
NotificationUiState.Loading,
)
}

@Preview(showBackground = true)
@Composable
fun NotificationScreenPreview(
@PreviewParameter(NotificationUiStateProvider::class) notificationUiState: NotificationUiState
) {
MifosTheme {
NotificationScreen(
uiState = notificationUiState,
isRefreshing = false,
onRefresh = {}
)
}
}

val sampleNotificationList = List(10) {
NotificationPayload("Title", "Body", "TimeStamp")
}
2 changes: 2 additions & 0 deletions mifospay/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -361,5 +361,7 @@
<string name="self_amount_transfer_is_not_allowed">Self Account transfer is not allowed</string>
<string name="insufficient_balance">Insufficient balance</string>
<string name="error_fetching_balance">Error fetching balance</string>
<string name="nothing_to_notify">Nothing To Notify</string>
<string name="there_is_nothing_to_show">There is nothing to show</string>

</resources>
Loading