email sender
This commit is contained in:
parent
39297abc6c
commit
02138f3f2f
|
|
@ -22,6 +22,5 @@ kotlin.code.style=official
|
|||
# resources declared in the library itself and none from the library's dependencies,
|
||||
# thereby reducing the size of the R class for that library
|
||||
android.nonTransitiveRClass=true
|
||||
android.defaults.buildfeatures.buildconfig=true
|
||||
android.nonFinalResIds=true
|
||||
org.gradle.unsafe.configuration-cache=true
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ android {
|
|||
applicationId = "llc.arma.vgate"
|
||||
minSdk = 26
|
||||
targetSdk = 35
|
||||
versionCode = 2
|
||||
versionName = "1.0.1"
|
||||
versionCode = 3
|
||||
versionName = "1.0.2"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,10 +33,12 @@ import dagger.hilt.android.AndroidEntryPoint
|
|||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.merge
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import llc.arma.vgate.app.framework.SendRequestWorker
|
||||
import llc.arma.vgate.app.ui.screens.main.MainScreen
|
||||
import llc.arma.vgate.app.ui.theme.BleTheme
|
||||
import llc.arma.vgate.domain.usecase.GetReceiverEmailFlow
|
||||
import llc.arma.vgate.domain.usecase.GetWaitingReportsFlow
|
||||
import java.time.Duration
|
||||
import javax.inject.Inject
|
||||
|
|
@ -45,6 +47,7 @@ import javax.inject.Inject
|
|||
class MainActivity : ComponentActivity() {
|
||||
|
||||
@Inject lateinit var getWaitingReportsFlow: GetWaitingReportsFlow
|
||||
@Inject lateinit var getReceiverEmailFlow: GetReceiverEmailFlow
|
||||
|
||||
@OptIn(ExperimentalPermissionsApi::class)
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
|
@ -63,11 +66,13 @@ class MainActivity : ComponentActivity() {
|
|||
|
||||
val workManager = WorkManager.getInstance(this)
|
||||
|
||||
getWaitingReportsFlow().onEach {
|
||||
merge(getWaitingReportsFlow.invoke(), getReceiverEmailFlow.invoke()).onEach {
|
||||
|
||||
println(it)
|
||||
|
||||
workManager.enqueueUniqueWork(
|
||||
uniqueWorkName = "Upload",
|
||||
existingWorkPolicy = ExistingWorkPolicy.KEEP,
|
||||
existingWorkPolicy = ExistingWorkPolicy.REPLACE,
|
||||
request = uploadWorkRequest
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ fun HomeScreen(
|
|||
}
|
||||
) {
|
||||
|
||||
Image(
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.Settings,
|
||||
contentDescription = null
|
||||
)
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ import llc.arma.vgate.app.ui.screens.result.ReadResultContract
|
|||
import llc.arma.vgate.app.ui.screens.result.ReadResultScreen
|
||||
import llc.arma.vgate.app.ui.screens.selector.BleSelectorContract
|
||||
import llc.arma.vgate.app.ui.screens.selector.BleSelectorScreen
|
||||
import llc.arma.vgate.app.ui.screens.sender.EmailSenderContract
|
||||
import llc.arma.vgate.app.ui.screens.sender.EmailSenderScreen
|
||||
import llc.arma.vgate.app.ui.screens.vehicle.form.VehicleFormContract
|
||||
import llc.arma.vgate.app.ui.screens.vehicle.form.VehicleFormScreen
|
||||
import llc.arma.vgate.app.ui.screens.vehicle.selector.VehicleSelectorContract
|
||||
|
|
@ -54,6 +56,11 @@ data class ReadResultScreenRoute(
|
|||
val resultId: Long
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class EmailSenderScreenRoute(
|
||||
val requestId: Long
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data object VehiclesScreenRoute
|
||||
|
||||
|
|
@ -145,6 +152,17 @@ fun MainScreen() {
|
|||
|
||||
}
|
||||
|
||||
composable<EmailSenderScreenRoute> {
|
||||
EmailSenderScreen(
|
||||
onNavigationEvent = {
|
||||
when(it){
|
||||
EmailSenderContract.Effect.Navigation.Up ->
|
||||
controller.navigateUp()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
composable<SendRequestsScreenRoute> {
|
||||
|
||||
SendRequestsScreen(
|
||||
|
|
@ -152,6 +170,9 @@ fun MainScreen() {
|
|||
when(it){
|
||||
SendRequestsContract.Effect.Navigation.NavigateUp ->
|
||||
controller.popBackStack()
|
||||
|
||||
is SendRequestsContract.Effect.Navigation.RequestSend ->
|
||||
controller.navigate(EmailSenderScreenRoute(it.requestId))
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,13 @@ import llc.arma.vgate.domain.model.Vehicle
|
|||
|
||||
class SendRequestsContract {
|
||||
|
||||
sealed class Event : ViewEvent
|
||||
sealed class Event : ViewEvent {
|
||||
|
||||
data class OnSend(
|
||||
val requestId: Long
|
||||
) : Event()
|
||||
|
||||
}
|
||||
|
||||
sealed class State : ViewState {
|
||||
|
||||
|
|
@ -24,6 +30,10 @@ class SendRequestsContract {
|
|||
|
||||
sealed class Navigation : Effect() {
|
||||
|
||||
data class RequestSend(
|
||||
val requestId: Long
|
||||
) : Navigation()
|
||||
|
||||
data object NavigateUp : Navigation()
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.Arrangement
|
|||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
|
|
@ -12,7 +13,7 @@ import androidx.compose.foundation.lazy.items
|
|||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.rounded.ArrowBack
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material.icons.rounded.Upload
|
||||
import androidx.compose.material3.ContainedLoadingIndicator
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
|
|
@ -25,6 +26,7 @@ import androidx.compose.material3.Text
|
|||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
|
|
@ -53,6 +55,16 @@ fun SendRequestsScreen(
|
|||
val viewModel = hiltViewModel<SendRequestsViewModel>()
|
||||
val state = viewModel.viewState.value
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.effect.collect {
|
||||
|
||||
when(it){
|
||||
is SendRequestsContract.Effect.Navigation -> onNavigationEvent(it)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
|
||||
|
||||
Scaffold(
|
||||
|
|
@ -126,7 +138,9 @@ private fun DisplayState(
|
|||
|
||||
items(items = state.sendRequests.toList()){
|
||||
|
||||
RequestItem(it.first, it.second)
|
||||
RequestItem(it.first, it.second){
|
||||
viewModel.setEvent(SendRequestsContract.Event.OnSend(it.first.id))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -139,7 +153,8 @@ private val formatter: DateFormat = SimpleDateFormat.getDateTimeInstance(3, 2)
|
|||
@Composable
|
||||
private fun RequestItem(
|
||||
request: SendRequest,
|
||||
vehicle: Vehicle
|
||||
vehicle: Vehicle,
|
||||
onSend: () -> Unit
|
||||
){
|
||||
|
||||
Surface(
|
||||
|
|
@ -148,10 +163,14 @@ private fun RequestItem(
|
|||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
|
||||
Column(
|
||||
Row(
|
||||
modifier = Modifier.padding(16.dp)
|
||||
) {
|
||||
|
||||
Column(
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
|
||||
Text(
|
||||
text = vehicle.name
|
||||
)
|
||||
|
|
@ -168,6 +187,21 @@ private fun RequestItem(
|
|||
|
||||
}
|
||||
|
||||
if(request.status == SendRequest.Status.Waiting) {
|
||||
|
||||
IconButton(
|
||||
onClick = onSend
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.Upload,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -42,7 +42,18 @@ class SendRequestsViewModel @Inject constructor(
|
|||
override fun setInitialState() = SendRequestsContract.State.Loading
|
||||
|
||||
override fun handleEvents(event: SendRequestsContract.Event) {
|
||||
when(event){
|
||||
is SendRequestsContract.Event.OnSend -> reduce(viewState.value, event)
|
||||
}
|
||||
}
|
||||
|
||||
private fun reduce(
|
||||
state: SendRequestsContract.State,
|
||||
event: SendRequestsContract.Event.OnSend
|
||||
){
|
||||
setEffect {
|
||||
SendRequestsContract.Effect.Navigation.RequestSend(event.requestId)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package llc.arma.vgate.app.ui.screens.sender
|
||||
|
||||
import llc.arma.vgate.app.ui.ViewEvent
|
||||
import llc.arma.vgate.app.ui.ViewSideEffect
|
||||
import llc.arma.vgate.app.ui.ViewState
|
||||
|
||||
class EmailSenderContract {
|
||||
|
||||
sealed class Event : ViewEvent {
|
||||
|
||||
data object OnNavigateUp : Event()
|
||||
|
||||
data object OnRetry : Event()
|
||||
|
||||
}
|
||||
|
||||
sealed class State : ViewState {
|
||||
|
||||
data object Loading : State()
|
||||
|
||||
data object Success : State()
|
||||
|
||||
data class Error(
|
||||
val error: Throwable
|
||||
) : State()
|
||||
|
||||
}
|
||||
|
||||
sealed class Effect : ViewSideEffect {
|
||||
|
||||
sealed class Navigation : Effect() {
|
||||
|
||||
data object Up : Navigation()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,377 @@
|
|||
package llc.arma.vgate.app.ui.screens.sender
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.rounded.ArrowBack
|
||||
import androidx.compose.material.icons.rounded.Close
|
||||
import androidx.compose.material.icons.rounded.Done
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ContainedLoadingIndicator
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun EmailSenderScreen(
|
||||
onNavigationEvent: (EmailSenderContract.Effect.Navigation) -> Unit
|
||||
) {
|
||||
|
||||
val viewModel = hiltViewModel<EmailSenderViewModel>()
|
||||
val state = viewModel.viewState.value
|
||||
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.effect.collect {
|
||||
when(it){
|
||||
is EmailSenderContract.Effect.Navigation -> onNavigationEvent(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
scrollBehavior = scrollBehavior,
|
||||
navigationIcon = {
|
||||
IconButton(
|
||||
onClick = {
|
||||
onNavigationEvent(EmailSenderContract.Effect.Navigation.Up)
|
||||
}
|
||||
){
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Rounded.ArrowBack,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
},
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
scrolledContainerColor = MaterialTheme.colorScheme.surfaceContainerHighest
|
||||
),
|
||||
title = {
|
||||
Text(
|
||||
text = "Отправка"
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
},
|
||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||
) {
|
||||
|
||||
Column(
|
||||
modifier = Modifier.padding(it)
|
||||
) {
|
||||
when(state){
|
||||
is EmailSenderContract.State.Error -> ErrorState(viewModel, state)
|
||||
EmailSenderContract.State.Loading -> LoadingState()
|
||||
EmailSenderContract.State.Success -> SuccessState(viewModel)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ErrorState(
|
||||
viewModel: EmailSenderViewModel,
|
||||
state: EmailSenderContract.State.Error
|
||||
){
|
||||
|
||||
var showError by remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = Modifier.fillMaxSize()
|
||||
){
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.widthIn(max = 270.dp)
|
||||
) {
|
||||
|
||||
Surface(
|
||||
shape = CircleShape,
|
||||
color = MaterialTheme.colorScheme.errorContainer,
|
||||
modifier = Modifier.size(128.dp)
|
||||
) {
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.Close,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(56.dp)
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Spacer(
|
||||
modifier = Modifier.height(16.dp)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "Ошибка"
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "Во время отправки данных произошла ошибка",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
Spacer(
|
||||
modifier = Modifier.height(16.dp)
|
||||
)
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
viewModel.setEvent(EmailSenderContract.Event.OnRetry)
|
||||
},
|
||||
modifier = Modifier.widthIn(min = 128.dp)
|
||||
) {
|
||||
|
||||
Text(
|
||||
text = "Повторить"
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
OutlinedButton(
|
||||
onClick = {
|
||||
showError = true
|
||||
},
|
||||
modifier = Modifier.widthIn(min = 128.dp)
|
||||
) {
|
||||
|
||||
Text(
|
||||
text = "Показать ошибку"
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
Spacer(
|
||||
modifier = Modifier.height(8.dp)
|
||||
)
|
||||
|
||||
TextButton (
|
||||
onClick = {
|
||||
viewModel.setEvent(EmailSenderContract.Event.OnNavigateUp)
|
||||
},
|
||||
modifier = Modifier.widthIn(min = 128.dp)
|
||||
) {
|
||||
|
||||
Text(
|
||||
text = "Отмена"
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(showError){
|
||||
|
||||
Dialog(
|
||||
onDismissRequest = {
|
||||
showError = false
|
||||
}
|
||||
) {
|
||||
|
||||
Surface(
|
||||
shape = RoundedCornerShape(20.dp)
|
||||
) {
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
modifier = Modifier.padding(20.dp)
|
||||
) {
|
||||
|
||||
Text(
|
||||
text = "Ошибка отправки",
|
||||
style = MaterialTheme.typography.titleLarge
|
||||
)
|
||||
|
||||
Column(
|
||||
modifier = Modifier.heightIn(max = 300.dp).verticalScroll(rememberScrollState())
|
||||
) {
|
||||
|
||||
Text(
|
||||
text = state.error.stackTraceToString(),
|
||||
style = MaterialTheme.typography.bodySmall
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
showError = false
|
||||
},
|
||||
modifier = Modifier.align(Alignment.End)
|
||||
) {
|
||||
|
||||
Text(
|
||||
text = "Ок"
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Composable
|
||||
private fun LoadingState(){
|
||||
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = Modifier.fillMaxSize()
|
||||
){
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.widthIn(max = 270.dp)
|
||||
) {
|
||||
|
||||
ContainedLoadingIndicator()
|
||||
|
||||
Spacer(
|
||||
modifier = Modifier.height(16.dp)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "Отправка данных",
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SuccessState(
|
||||
viewModel: EmailSenderViewModel
|
||||
){
|
||||
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = Modifier.fillMaxSize()
|
||||
){
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.widthIn(max = 270.dp)
|
||||
) {
|
||||
|
||||
Surface(
|
||||
shape = CircleShape,
|
||||
color = Color.Green.copy(alpha = 0.8f),
|
||||
modifier = Modifier.size(128.dp)
|
||||
) {
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
|
||||
Icon(
|
||||
tint = Color.White,
|
||||
imageVector = Icons.Rounded.Done,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(56.dp)
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Spacer(
|
||||
modifier = Modifier.height(16.dp)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "Успешно"
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "Данные с устройства успешно отправлены",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
Spacer(
|
||||
modifier = Modifier.height(16.dp)
|
||||
)
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
viewModel.setEvent(EmailSenderContract.Event.OnNavigateUp)
|
||||
},
|
||||
modifier = Modifier.widthIn(min = 128.dp)
|
||||
) {
|
||||
|
||||
Text(
|
||||
text = "Ок"
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
package llc.arma.vgate.app.ui.screens.sender
|
||||
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.navigation.toRoute
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import llc.arma.vgate.app.ui.BaseViewModel
|
||||
import llc.arma.vgate.app.ui.screens.main.EmailSenderScreenRoute
|
||||
import llc.arma.vgate.domain.usecase.SendWaitingReports
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class EmailSenderViewModel @Inject constructor(
|
||||
private val savedStateHandle: SavedStateHandle,
|
||||
private val sendWaitingReports: SendWaitingReports
|
||||
) : BaseViewModel<EmailSenderContract.State, EmailSenderContract.Event, EmailSenderContract.Effect>() {
|
||||
|
||||
private var sendJob: Job? = null
|
||||
|
||||
init {
|
||||
|
||||
send()
|
||||
|
||||
}
|
||||
|
||||
override fun setInitialState() = EmailSenderContract.State.Loading
|
||||
|
||||
override fun handleEvents(event: EmailSenderContract.Event) {
|
||||
when(event){
|
||||
is EmailSenderContract.Event.OnRetry -> reduce(viewState.value, event)
|
||||
is EmailSenderContract.Event.OnNavigateUp -> reduce(viewState.value, event)
|
||||
}
|
||||
}
|
||||
|
||||
private fun reduce(
|
||||
state: EmailSenderContract.State,
|
||||
event: EmailSenderContract.Event.OnRetry
|
||||
){
|
||||
|
||||
send()
|
||||
|
||||
}
|
||||
|
||||
private fun reduce(
|
||||
state: EmailSenderContract.State,
|
||||
event: EmailSenderContract.Event.OnNavigateUp
|
||||
){
|
||||
|
||||
setEffect {
|
||||
EmailSenderContract.Effect.Navigation.Up
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun send() {
|
||||
|
||||
val params = savedStateHandle.toRoute<EmailSenderScreenRoute>()
|
||||
|
||||
setState {
|
||||
EmailSenderContract.State.Loading
|
||||
}
|
||||
|
||||
sendJob = viewModelScope.launch {
|
||||
sendWaitingReports.invoke(params.requestId).fold(
|
||||
onSuccess = {
|
||||
setState {
|
||||
EmailSenderContract.State.Success
|
||||
}
|
||||
},
|
||||
onFailure = {
|
||||
setState {
|
||||
EmailSenderContract.State.Error(it)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@ import androidx.datastore.preferences.preferencesDataStore
|
|||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.flow.map
|
||||
import llc.arma.common.domain.Result
|
||||
import llc.arma.vgate.domain.repository.EmailRepository
|
||||
import java.io.File
|
||||
import java.util.Properties
|
||||
|
|
@ -87,7 +88,7 @@ class EmailRepositoryImpl @Inject constructor(
|
|||
body: String,
|
||||
fileName: String,
|
||||
file: File
|
||||
): Boolean {
|
||||
): Result<Unit, Throwable> {
|
||||
|
||||
return coroutineScope {
|
||||
|
||||
|
|
@ -103,10 +104,10 @@ class EmailRepositoryImpl @Inject constructor(
|
|||
body = body,
|
||||
)
|
||||
)
|
||||
true
|
||||
Result.success(Unit)
|
||||
} catch (err: Throwable) {
|
||||
err.printStackTrace()
|
||||
false
|
||||
Result.failure(err)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package llc.arma.vgate.domain.repository
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import llc.arma.common.domain.Result
|
||||
import java.io.File
|
||||
|
||||
interface EmailRepository {
|
||||
|
|
@ -10,7 +11,7 @@ interface EmailRepository {
|
|||
body: String,
|
||||
fileName: String,
|
||||
file: File
|
||||
) : Boolean
|
||||
): Result<Unit, Throwable>
|
||||
|
||||
suspend fun saveEmail(email: String)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
package llc.arma.vgate.domain.usecase
|
||||
|
||||
import android.app.Application
|
||||
import android.content.res.Resources.NotFoundException
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import llc.arma.common.domain.Result
|
||||
import llc.arma.vgate.domain.model.ReadResult
|
||||
import llc.arma.vgate.domain.model.ReadResultPoint
|
||||
import llc.arma.vgate.domain.model.SendRequest
|
||||
|
|
@ -45,15 +47,25 @@ class SendWaitingReports @Inject constructor(
|
|||
|
||||
return sendRequestRepository.getByStatus(SendRequest.Status.Waiting).all {
|
||||
|
||||
send(it)
|
||||
send(it).isSuccess
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
suspend operator fun invoke(id: Long): Result<Unit, Throwable> {
|
||||
|
||||
val request = sendRequestRepository.getById(id) ?: return Result.failure(NotFoundException())
|
||||
|
||||
return send(request)
|
||||
|
||||
}
|
||||
|
||||
private suspend fun send(
|
||||
request: SendRequest
|
||||
): Boolean {
|
||||
): Result<Unit, Throwable> {
|
||||
|
||||
return withContext(Dispatchers.IO) {
|
||||
|
||||
val formatter = SimpleDateFormat.getDateInstance()
|
||||
|
||||
|
|
@ -67,10 +79,13 @@ class SendWaitingReports @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
val statistic = getReadResultSummary(request.readResultId) ?: return run { finishRequest(); true}
|
||||
val statistic = getReadResultSummary(request.readResultId)
|
||||
?: return@withContext run { finishRequest(); Result.success(Unit) }
|
||||
|
||||
val readResult = readResultRepository.getById(request.readResultId) ?: return run { finishRequest(); true}
|
||||
val vehicle = vehicleRepository.getById(readResult.vehicleId) ?: return run { finishRequest(); true}
|
||||
val readResult = readResultRepository.getById(request.readResultId)
|
||||
?: return@withContext run { finishRequest(); Result.success(Unit) }
|
||||
val vehicle = vehicleRepository.getById(readResult.vehicleId)
|
||||
?: return@withContext run { finishRequest(); Result.success(Unit) }
|
||||
val ranges = vibrationRangeRepository.getByVehicleId(vehicle.id)
|
||||
|
||||
val workbook = XSSFWorkbook()
|
||||
|
|
@ -97,13 +112,21 @@ class SendWaitingReports @Inject constructor(
|
|||
val sendResult = emailRepository.sendFile(
|
||||
subject = "Отчет от vGate по \"${vehicle.name}\"",
|
||||
fileName = "Report.xlsx",
|
||||
body = "Статистика активности \"${vehicle.name}\" за ${formatter.format(Date(readResult.date))}" ,
|
||||
body = "Статистика активности \"${vehicle.name}\" за ${
|
||||
formatter.format(
|
||||
Date(
|
||||
readResult.date
|
||||
)
|
||||
)
|
||||
}",
|
||||
file = reportFile
|
||||
)
|
||||
|
||||
if(sendResult) finishRequest(SendRequest.Status.Sent)
|
||||
if (sendResult.isSuccess) finishRequest(SendRequest.Status.Sent)
|
||||
|
||||
return sendResult
|
||||
return@withContext sendResult
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue