diff --git a/app/src/main/java/com/pixelized/biblib/network/client/IBibLibWebServiceAPI.kt b/app/src/main/java/com/pixelized/biblib/network/client/IBibLibWebServiceAPI.kt index cb0a5f9..12bfa19 100644 --- a/app/src/main/java/com/pixelized/biblib/network/client/IBibLibWebServiceAPI.kt +++ b/app/src/main/java/com/pixelized/biblib/network/client/IBibLibWebServiceAPI.kt @@ -25,7 +25,7 @@ interface IBibLibWebServiceAPI { suspend fun detail(@Path("id") bookId: Int): BookDetailResponse @GET("book/{id}/send/kindle") - suspend fun send(@Path("id") bookId: Int, @Query("mail") mail: String): LinkedTreeMap + suspend fun send(@Path("id") bookId: Int, @Query("mail") mail: String, @Query("format") format: String): LinkedTreeMap @GET("series") suspend fun series(): SeriesListResponse diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/BookDetailViewModel.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/BookDetailViewModel.kt index 974f550..7854aaa 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/BookDetailViewModel.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/BookDetailViewModel.kt @@ -46,16 +46,16 @@ class BookDetailViewModel @Inject constructor( } } - suspend fun send(bookId: Int, email: String) { + suspend fun send(bookId: Int, email: String, format: String) { viewModelScope.launch(Dispatchers.IO) { try { - val data = client.service.send(bookId = bookId, mail = email) + val data = client.service.send(bookId = bookId, mail = email, format = format) _sendStatus.emit(true) Log.d("send", data.toString()) } catch (exception: Exception) { Log.d("send", exception.message, exception) _sendStatus.emit(false) - _error.emit(toSendBookUio(bookId = bookId, mail = email)) + _error.emit(toSendBookUio(bookId = bookId, mail = email, format)) } } } @@ -77,10 +77,15 @@ class BookDetailViewModel @Inject constructor( bookId = bookId, ) - private fun toSendBookUio(bookId: Int, mail: String) = BookDetailUioErrorUio.SendBookInput( + private fun toSendBookUio( + bookId: Int, + mail: String, + format: String + ) = BookDetailUioErrorUio.SendBookInput( message = stringResource(R.string.error_send_book_message), action = stringResource(R.string.error_send_book_action), bookId = bookId, mail = mail, + format = format, ) } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/BookOptionViewModel.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/BookOptionViewModel.kt new file mode 100644 index 0000000..a7a411d --- /dev/null +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/BookOptionViewModel.kt @@ -0,0 +1,54 @@ +package com.pixelized.biblib.ui.screen.home.detail + +import android.util.Log +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.pixelized.biblib.network.client.IBibLibClient +import com.pixelized.biblib.network.factory.UserFactory +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class BookOptionViewModel @Inject constructor( + private val client: IBibLibClient, +) : ViewModel() { + + var emails by mutableStateOf(listOf()) + private set + + var formats by mutableStateOf(listOf(OptionUio(Format.EPUB, true), OptionUio(Format.MOBI))) + private set + + init { + viewModelScope.launch(Dispatchers.IO) { + try { + val factory = UserFactory() + val response = client.service.user() + val data = factory.fromUserResponseToUser(response) + + emails = data.amazonEmails.mapIndexed { index, mail -> + OptionUio(value = mail, selected = index == 0) + } + } catch (exception: Exception) { + Log.e("AccountViewModel", exception.message, exception) + } + } + } + + fun selectMail(mail: String) { + emails = emails.map { + it.copy(selected = it.value == mail) + } + } + + fun selectFormat(@Format format: String) { + formats = formats.map { + it.copy(selected = it.value == format) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/ConfirmDialogViewModel.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/ConfirmDialogViewModel.kt index 1cfc22e..8130b03 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/ConfirmDialogViewModel.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/ConfirmDialogViewModel.kt @@ -16,10 +16,11 @@ class ConfirmDialogViewModel @Inject constructor( var dialog by mutableStateOf(null) private set - fun show(bookId: Int, email: String) { + fun show(bookId: Int, email: String, @Format format: String) { this.dialog = ConfirmDialogUio( bookId = bookId, email = email, + format = format, ) } diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/DetailScreen.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/DetailScreen.kt index a48ff8e..d7e07df 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/DetailScreen.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/DetailScreen.kt @@ -1,5 +1,6 @@ package com.pixelized.biblib.ui.screen.home.detail +import androidx.activity.compose.BackHandler import androidx.compose.animation.animateContentSize import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState @@ -18,6 +19,7 @@ import com.pixelized.biblib.ui.screen.home.page.profile.ProfileViewModel import com.pixelized.biblib.ui.theme.color.ShadowPalette import com.pixelized.biblib.utils.extention.bibLib import com.pixelized.biblib.utils.extention.showToast +import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import java.io.Serializable @@ -70,6 +72,7 @@ sealed class BookDetailUioErrorUio( action: String?, val bookId: Int, val mail: String, + val format: String, ) : BookDetailUioErrorUio(message, action) } @@ -100,13 +103,12 @@ fun DetailScreen( sheetState = emailSheetState, scrimColor = ShadowPalette.scrim, sheetContent = { - DetailScreenEmailList( + DetailScreenSendOption( modifier = Modifier .navigationBarsPadding() .padding(bottom = MaterialTheme.bibLib.dimen.dp16), - profileViewModel = profileViewModel, - onEmail = { mail -> - confirmViewModel.show(bookId = detail.id, email = mail) + onSend = { mail, format -> + confirmViewModel.show(bookId = detail.id, email = mail, format = format) } ) }, @@ -132,7 +134,8 @@ fun DetailScreen( mails.size == 1 -> { confirmViewModel.show( bookId = detail.id, - email = mails.first() + email = mails.first(), + format = Format.EPUB, ) } else -> { @@ -153,11 +156,11 @@ fun DetailScreen( ConfirmDialog( modifier = Modifier.padding(all = MaterialTheme.bibLib.dimen.dp32), confirmViewModel = confirmViewModel, - onConfirm = { id, email -> + onConfirm = { id, email, format -> scope.launch { confirmViewModel.hide() emailSheetState.hide() - detailViewModel.send(bookId = id, email = email) + detailViewModel.send(bookId = id, email = email, format = format) } }, onDismiss = { confirmViewModel.hide() }, @@ -176,20 +179,34 @@ fun DetailScreen( ) if (result == SnackbarResult.ActionPerformed) { when (it) { - is BookDetailUioErrorUio.GetDetailInput -> { - detailViewModel.getDetail(id = it.bookId) - } - is BookDetailUioErrorUio.SendBookInput -> { - detailViewModel.send(bookId = it.bookId, email = it.mail) - } + is BookDetailUioErrorUio.GetDetailInput -> detailViewModel.getDetail( + id = it.bookId + ) + is BookDetailUioErrorUio.SendBookInput -> detailViewModel.send( + bookId = it.bookId, + email = it.mail, + format = it.format, + ) } } } } + LaunchedEffect(key1 = "") { + detailViewModel.sendStatus.collect { + + } + } + LaunchedEffect(key1 = bookId) { bookId?.let { detailViewModel.getDetail(it) } } + + BackHandler(emailSheetState.isVisible || emailSheetState.isAnimationRunning) { + scope.launch { + emailSheetState.hide() + } + } } } diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/DetailScreenConfirm.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/DetailScreenConfirm.kt index 6628ff2..67655fd 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/DetailScreenConfirm.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/DetailScreenConfirm.kt @@ -1,6 +1,8 @@ package com.pixelized.biblib.ui.screen.home.detail +import android.content.Context.* import android.content.res.Configuration +import androidx.annotation.StringDef import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.material.* @@ -26,17 +28,29 @@ import com.pixelized.biblib.utils.extention.default import com.pixelized.biblib.utils.extention.highlight import com.pixelized.biblib.utils.extention.stringRegex + +@Retention(AnnotationRetention.SOURCE) +@StringDef(Format.EPUB, Format.MOBI) +annotation class Format { + companion object { + const val EPUB = "epub" + const val MOBI = "mobi" + } +} + @Stable @Immutable data class ConfirmDialogUio( val bookId: Int, val email: String, + @Format val format: String, ) { companion object { @Composable fun preview() = ConfirmDialogUio( bookId = 90, email = "R.Daneel.Olivaw.Kindle@gmail.com", + format = Format.EPUB, ) } } @@ -45,7 +59,7 @@ data class ConfirmDialogUio( fun ConfirmDialog( modifier: Modifier = Modifier, confirmViewModel: ConfirmDialogViewModel, - onConfirm: (bookId: Int, mail: String) -> Unit = { _, _ -> }, + onConfirm: (bookId: Int, mail: String, format: String) -> Unit = { _, _, _ -> }, onHelp: (url: String) -> Unit = default(), onDismiss: () -> Unit ) { @@ -67,7 +81,7 @@ fun ConfirmDialog( uio: ConfirmDialogUio, onDismissRequest: () -> Unit = default(), onHelp: (url: String) -> Unit = default(), - onConfirm: (bookId: Int, mail: String) -> Unit = { _, _ -> }, + onConfirm: (bookId: Int, mail: String, format: String) -> Unit = { _, _, _ -> }, ) { Dialog( onDismissRequest = onDismissRequest, @@ -89,7 +103,7 @@ fun ConfirmDialogContent( uio: ConfirmDialogUio, onDismissRequest: () -> Unit = default(), onHelp: (url: String) -> Unit = default(), - onConfirm: (bookId: Int, mail: String) -> Unit = { _, _ -> }, + onConfirm: (bookId: Int, mail: String, format: String) -> Unit = { _, _, _ -> }, ) { val amazonHelpUri = stringResource(R.string.detail_send_confirm_help_url) @@ -142,15 +156,15 @@ fun ConfirmDialogContent( onClick = onDismissRequest, ) { Text( - text = stringResource(R.string.detail_send_confirm_confirm_action) + text = stringResource(R.string.detail_send_confirm_cancel_action) ) } Button( colors = ButtonDefaults.buttonColors(), - onClick = { onConfirm(uio.bookId, uio.email) } + onClick = { onConfirm(uio.bookId, uio.email, uio.format) } ) { Text( - text = stringResource(R.string.detail_send_confirm_cancel_action) + text = stringResource(R.string.detail_send_confirm_confirm_action) ) } } diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/DetailScreenEmailList.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/DetailScreenEmailList.kt deleted file mode 100644 index 258d3be..0000000 --- a/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/DetailScreenEmailList.kt +++ /dev/null @@ -1,114 +0,0 @@ -package com.pixelized.biblib.ui.screen.home.detail - -import android.content.res.Configuration -import androidx.compose.animation.animateContentSize -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.material.Icon -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.NavigateNext -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel -import com.pixelized.biblib.ui.composable.isSuccessful -import com.pixelized.biblib.ui.screen.home.page.profile.ProfileViewModel -import com.pixelized.biblib.ui.theme.BibLibTheme -import com.pixelized.biblib.utils.extention.bibLib -import com.pixelized.biblib.utils.extention.default - -@Composable -fun DetailScreenEmailList( - modifier: Modifier = Modifier, - profileViewModel: ProfileViewModel, - onEmail: (email: String) -> Unit = default(), -) { - Box( - modifier = Modifier - .fillMaxWidth() - .heightIn(min = 1.dp) - .animateContentSize() - ) { - val user = profileViewModel.user - if (user.isSuccessful()) { - DetailScreenEmailList( - modifier = modifier, - emails = user.value.amazonEmails, - onEmail = onEmail, - ) - } - } -} - -@Composable -fun DetailScreenEmailList( - modifier: Modifier = Modifier, - emails: List, - onEmail: (email: String) -> Unit = default(), -) { - Column( - modifier = modifier.fillMaxWidth(), - ) { - Text( - modifier = Modifier - .padding(top = MaterialTheme.bibLib.dimen.dp8) - .padding(horizontal = MaterialTheme.bibLib.dimen.dp16), - color = MaterialTheme.colors.primary, - style = MaterialTheme.typography.caption, - text = "Send this eBook to:", - ) - - LazyColumn { - items(items = emails) { email -> - Row( - modifier = Modifier - .clickable(onClick = { onEmail(email) }) - .height(height = MaterialTheme.bibLib.dimen.dp52) - .fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically, - ) { - Text( - modifier = Modifier - .padding( - start = MaterialTheme.bibLib.dimen.dp16, - end = MaterialTheme.bibLib.dimen.dp8, - ) - .weight(1f), - style = MaterialTheme.typography.body1, - color = MaterialTheme.colors.onSurface, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - text = email - ) - - Icon( - modifier = Modifier.padding(end = MaterialTheme.bibLib.dimen.dp16), - imageVector = Icons.Default.NavigateNext, - contentDescription = null - ) - } - } - } - } -} - -@Composable -@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO) -@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES) -private fun DetailScreenEmailListPreview() { - BibLibTheme { - DetailScreenEmailList( - emails = listOf( - "R.Giskard.Reventlov.Kindle@gmail.com", - "R.Daneel.Olivaw.Kindle@gmail.com", - ), - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/DetailScreenSendOption.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/DetailScreenSendOption.kt new file mode 100644 index 0000000..ca58265 --- /dev/null +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/home/detail/DetailScreenSendOption.kt @@ -0,0 +1,166 @@ +package com.pixelized.biblib.ui.screen.home.detail + +import android.content.res.Configuration +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material.Button +import androidx.compose.material.MaterialTheme +import androidx.compose.material.RadioButton +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.Stable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.hilt.navigation.compose.hiltViewModel +import com.pixelized.biblib.R +import com.pixelized.biblib.ui.theme.BibLibTheme +import com.pixelized.biblib.utils.extention.bibLib +import com.pixelized.biblib.utils.extention.capitalize + +@Stable +@Immutable +data class OptionUio( + val value: String, + val selected: Boolean = false, +) + +@Composable +fun DetailScreenSendOption( + modifier: Modifier = Modifier, + optionViewModel: BookOptionViewModel = hiltViewModel(), + onSend: (email: String, format: String) -> Unit = { _, _ -> }, +) { + DetailScreenSendOption( + modifier = modifier, + emails = optionViewModel.emails, + formats = optionViewModel.formats, + onEmail = { optionViewModel.selectMail(it.value) }, + onFormat = { optionViewModel.selectFormat(it.value) }, + onSend = onSend, + ) +} + +@Composable +fun DetailScreenSendOption( + modifier: Modifier = Modifier, + emails: List, + formats: List, + onEmail: (OptionUio) -> Unit = { }, + onFormat: (OptionUio) -> Unit = { }, + onSend: (email: String, format: String) -> Unit = { _, _ -> }, +) { + LazyColumn( + modifier = modifier.fillMaxWidth(), + ) { + item { + Text( + modifier = Modifier + .padding(vertical = MaterialTheme.bibLib.dimen.dp8) + .padding(horizontal = MaterialTheme.bibLib.dimen.dp16), + color = MaterialTheme.colors.primary, + style = MaterialTheme.typography.caption, + text = stringResource(id = R.string.detail_option_mail), + ) + } + items(items = emails) { email -> + OptionItem( + modifier = Modifier + .clickable { onEmail(email) } + .fillMaxWidth() + .padding(all = MaterialTheme.bibLib.dimen.dp16), + uio = email, + ) + } + item { + Text( + modifier = Modifier + .padding(vertical = MaterialTheme.bibLib.dimen.dp8) + .padding(horizontal = MaterialTheme.bibLib.dimen.dp16), + color = MaterialTheme.colors.primary, + style = MaterialTheme.typography.caption, + text = stringResource(id = R.string.detail_option_mail), + ) + } + items(items = formats) { format -> + OptionItem( + modifier = Modifier + .clickable { onFormat(format) } + .fillMaxWidth() + .padding(all = MaterialTheme.bibLib.dimen.dp16), + uio = format, + ) + } + item { + Button( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = MaterialTheme.bibLib.dimen.dp16), + onClick = { + val format = formats.first { it.selected } + val email = emails.first { it.selected } + onSend(email.value, format.value) + }, + ) { + Text( + text = stringResource(id = R.string.detail_option_action) + ) + } + } + } +} + +@Composable +private fun OptionItem( + modifier: Modifier = Modifier, + uio: OptionUio, +) { + Row( + modifier = modifier, + verticalAlignment = Alignment.CenterVertically, + ) { + RadioButton( + selected = uio.selected, + onClick = null, + ) + Text( + modifier = Modifier + .padding( + start = MaterialTheme.bibLib.dimen.dp16, + end = MaterialTheme.bibLib.dimen.dp8, + ) + .weight(1f), + style = MaterialTheme.typography.body1, + color = MaterialTheme.colors.onSurface, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + text = uio.value + ) + } +} + +@Composable +@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO) +@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES) +private fun DetailScreenEmailListPreview() { + BibLibTheme { + DetailScreenSendOption( + emails = listOf( + OptionUio("R.Giskard.Reventlov.Kindle@gmail.com", selected = true), + OptionUio("R.Daneel.Olivaw.Kindle@gmail.com"), + ), + formats = listOf( + OptionUio(Format.EPUB.capitalize(), selected = true), + OptionUio(Format.MOBI.capitalize()), + ), + ) + } +} \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 87e494a..721344f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -59,7 +59,10 @@ Release Genre Series - Send this eBook to: + + Send this eBook to: + Use this format: + Confirm Send to your Kindle Make sure your Kindle has an internet connection, that %1$s is an approved expeditor and that the following email address is correctly set up on your Amazon Account.