Merge confirm & option dialog / bottomsheet
This commit is contained in:
		
							parent
							
								
									3e234cc37d
								
							
						
					
					
						commit
						2a92fd2b8b
					
				
					 6 changed files with 103 additions and 272 deletions
				
			
		| 
						 | 
					@ -1,19 +1,18 @@
 | 
				
			||||||
package com.pixelized.biblib.ui.screen.home.detail
 | 
					package com.pixelized.biblib.ui.screen.home.detail
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import android.app.Application
 | 
					 | 
				
			||||||
import android.util.Log
 | 
					import android.util.Log
 | 
				
			||||||
import androidx.compose.runtime.State
 | 
					import androidx.compose.runtime.State
 | 
				
			||||||
import androidx.compose.runtime.mutableStateOf
 | 
					import androidx.compose.runtime.mutableStateOf
 | 
				
			||||||
import androidx.lifecycle.AndroidViewModel
 | 
					import androidx.lifecycle.ViewModel
 | 
				
			||||||
import androidx.lifecycle.viewModelScope
 | 
					import androidx.lifecycle.viewModelScope
 | 
				
			||||||
import com.pixelized.biblib.R
 | 
					import com.pixelized.biblib.R
 | 
				
			||||||
import com.pixelized.biblib.network.client.IBibLibClient
 | 
					import com.pixelized.biblib.network.client.IBibLibClient
 | 
				
			||||||
import com.pixelized.biblib.network.factory.BookFactory
 | 
					import com.pixelized.biblib.network.factory.BookFactory
 | 
				
			||||||
import com.pixelized.biblib.repository.book.BookRepository
 | 
					import com.pixelized.biblib.repository.book.BookRepository
 | 
				
			||||||
import com.pixelized.biblib.utils.extention.stringResource
 | 
					 | 
				
			||||||
import com.pixelized.biblib.utils.extention.toDetailUio
 | 
					import com.pixelized.biblib.utils.extention.toDetailUio
 | 
				
			||||||
import dagger.hilt.android.lifecycle.HiltViewModel
 | 
					import dagger.hilt.android.lifecycle.HiltViewModel
 | 
				
			||||||
import kotlinx.coroutines.Dispatchers
 | 
					import kotlinx.coroutines.Dispatchers
 | 
				
			||||||
 | 
					import kotlinx.coroutines.Job
 | 
				
			||||||
import kotlinx.coroutines.flow.Flow
 | 
					import kotlinx.coroutines.flow.Flow
 | 
				
			||||||
import kotlinx.coroutines.flow.MutableSharedFlow
 | 
					import kotlinx.coroutines.flow.MutableSharedFlow
 | 
				
			||||||
import kotlinx.coroutines.launch
 | 
					import kotlinx.coroutines.launch
 | 
				
			||||||
| 
						 | 
					@ -21,14 +20,15 @@ import javax.inject.Inject
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@HiltViewModel
 | 
					@HiltViewModel
 | 
				
			||||||
class BookDetailViewModel @Inject constructor(
 | 
					class BookDetailViewModel @Inject constructor(
 | 
				
			||||||
    application: Application,
 | 
					 | 
				
			||||||
    private val bookRepository: BookRepository,
 | 
					    private val bookRepository: BookRepository,
 | 
				
			||||||
    private val client: IBibLibClient,
 | 
					    private val client: IBibLibClient,
 | 
				
			||||||
) : AndroidViewModel(application) {
 | 
					) : ViewModel() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private var detailJob: Job? = null
 | 
				
			||||||
    private val _detail = mutableStateOf<BookDetailUio?>(null)
 | 
					    private val _detail = mutableStateOf<BookDetailUio?>(null)
 | 
				
			||||||
    val detail: State<BookDetailUio?> get() = _detail
 | 
					    val detail: State<BookDetailUio?> get() = _detail
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private var sendJob: Job? = null
 | 
				
			||||||
    private val _sendStatus = MutableSharedFlow<Boolean>()
 | 
					    private val _sendStatus = MutableSharedFlow<Boolean>()
 | 
				
			||||||
    val sendStatus: Flow<Boolean> get() = _sendStatus
 | 
					    val sendStatus: Flow<Boolean> get() = _sendStatus
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,7 +36,8 @@ class BookDetailViewModel @Inject constructor(
 | 
				
			||||||
    val error: Flow<BookDetailUioErrorUio> get() = _error
 | 
					    val error: Flow<BookDetailUioErrorUio> get() = _error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun getDetail(id: Int) {
 | 
					    fun getDetail(id: Int) {
 | 
				
			||||||
        viewModelScope.launch(Dispatchers.IO) {
 | 
					        detailJob?.cancel()
 | 
				
			||||||
 | 
					        detailJob = viewModelScope.launch(Dispatchers.IO) {
 | 
				
			||||||
            try {
 | 
					            try {
 | 
				
			||||||
                _detail.value = getCacheBookDetail(id = id)
 | 
					                _detail.value = getCacheBookDetail(id = id)
 | 
				
			||||||
                _detail.value = getBookDetail(id = id)
 | 
					                _detail.value = getBookDetail(id = id)
 | 
				
			||||||
| 
						 | 
					@ -46,8 +47,9 @@ class BookDetailViewModel @Inject constructor(
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    suspend fun send(bookId: Int, email: String, format: String) {
 | 
					    fun send(bookId: Int, email: String, format: String) {
 | 
				
			||||||
        viewModelScope.launch(Dispatchers.IO) {
 | 
					        sendJob?.cancel()
 | 
				
			||||||
 | 
					        sendJob = viewModelScope.launch(Dispatchers.IO) {
 | 
				
			||||||
            try {
 | 
					            try {
 | 
				
			||||||
                val data = client.service.send(bookId = bookId, mail = email, format = format)
 | 
					                val data = client.service.send(bookId = bookId, mail = email, format = format)
 | 
				
			||||||
                _sendStatus.emit(true)
 | 
					                _sendStatus.emit(true)
 | 
				
			||||||
| 
						 | 
					@ -72,8 +74,8 @@ class BookDetailViewModel @Inject constructor(
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private fun toDetailErrorUio(bookId: Int) = BookDetailUioErrorUio.GetDetailInput(
 | 
					    private fun toDetailErrorUio(bookId: Int) = BookDetailUioErrorUio.GetDetailInput(
 | 
				
			||||||
        message = stringResource(R.string.error_get_book_detail_message),
 | 
					        message = R.string.error_get_book_detail_message,
 | 
				
			||||||
        action = stringResource(R.string.error_get_book_detail_action),
 | 
					        action = R.string.error_get_book_detail_action,
 | 
				
			||||||
        bookId = bookId,
 | 
					        bookId = bookId,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -82,8 +84,8 @@ class BookDetailViewModel @Inject constructor(
 | 
				
			||||||
        mail: String,
 | 
					        mail: String,
 | 
				
			||||||
        format: String
 | 
					        format: String
 | 
				
			||||||
    ) = BookDetailUioErrorUio.SendBookInput(
 | 
					    ) = BookDetailUioErrorUio.SendBookInput(
 | 
				
			||||||
        message = stringResource(R.string.error_send_book_message),
 | 
					        message = R.string.error_send_book_message,
 | 
				
			||||||
        action = stringResource(R.string.error_send_book_action),
 | 
					        action = R.string.error_send_book_action,
 | 
				
			||||||
        bookId = bookId,
 | 
					        bookId = bookId,
 | 
				
			||||||
        mail = mail,
 | 
					        mail = mail,
 | 
				
			||||||
        format = format,
 | 
					        format = format,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,6 +8,7 @@ import androidx.lifecycle.ViewModel
 | 
				
			||||||
import androidx.lifecycle.viewModelScope
 | 
					import androidx.lifecycle.viewModelScope
 | 
				
			||||||
import com.pixelized.biblib.network.client.IBibLibClient
 | 
					import com.pixelized.biblib.network.client.IBibLibClient
 | 
				
			||||||
import com.pixelized.biblib.network.factory.UserFactory
 | 
					import com.pixelized.biblib.network.factory.UserFactory
 | 
				
			||||||
 | 
					import com.pixelized.biblib.utils.extention.capitalize
 | 
				
			||||||
import dagger.hilt.android.lifecycle.HiltViewModel
 | 
					import dagger.hilt.android.lifecycle.HiltViewModel
 | 
				
			||||||
import kotlinx.coroutines.Dispatchers
 | 
					import kotlinx.coroutines.Dispatchers
 | 
				
			||||||
import kotlinx.coroutines.launch
 | 
					import kotlinx.coroutines.launch
 | 
				
			||||||
| 
						 | 
					@ -21,7 +22,7 @@ class BookOptionViewModel @Inject constructor(
 | 
				
			||||||
    var emails by mutableStateOf(listOf<OptionUio>())
 | 
					    var emails by mutableStateOf(listOf<OptionUio>())
 | 
				
			||||||
        private set
 | 
					        private set
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var formats by mutableStateOf(listOf(OptionUio(Format.EPUB, true), OptionUio(Format.MOBI)))
 | 
					    var formats by mutableStateOf(initialFormat())
 | 
				
			||||||
        private set
 | 
					        private set
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    init {
 | 
					    init {
 | 
				
			||||||
| 
						 | 
					@ -51,4 +52,9 @@ class BookOptionViewModel @Inject constructor(
 | 
				
			||||||
            it.copy(selected = it.value == format)
 | 
					            it.copy(selected = it.value == format)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private fun initialFormat() = listOf(
 | 
				
			||||||
 | 
					        OptionUio(Format.EPUB.capitalize(), true),
 | 
				
			||||||
 | 
					        OptionUio(Format.MOBI.capitalize()),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,30 +0,0 @@
 | 
				
			||||||
package com.pixelized.biblib.ui.screen.home.detail
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import android.app.Application
 | 
					 | 
				
			||||||
import androidx.compose.runtime.getValue
 | 
					 | 
				
			||||||
import androidx.compose.runtime.mutableStateOf
 | 
					 | 
				
			||||||
import androidx.compose.runtime.setValue
 | 
					 | 
				
			||||||
import androidx.lifecycle.AndroidViewModel
 | 
					 | 
				
			||||||
import dagger.hilt.android.lifecycle.HiltViewModel
 | 
					 | 
				
			||||||
import javax.inject.Inject
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@HiltViewModel
 | 
					 | 
				
			||||||
class ConfirmDialogViewModel @Inject constructor(
 | 
					 | 
				
			||||||
    application: Application,
 | 
					 | 
				
			||||||
) : AndroidViewModel(application) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    var dialog by mutableStateOf<ConfirmDialogUio?>(null)
 | 
					 | 
				
			||||||
        private set
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fun show(bookId: Int, email: String, @Format format: String) {
 | 
					 | 
				
			||||||
        this.dialog = ConfirmDialogUio(
 | 
					 | 
				
			||||||
            bookId = bookId,
 | 
					 | 
				
			||||||
            email = email,
 | 
					 | 
				
			||||||
            format = format,
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fun hide() {
 | 
					 | 
				
			||||||
        dialog = null
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,7 @@
 | 
				
			||||||
package com.pixelized.biblib.ui.screen.home.detail
 | 
					package com.pixelized.biblib.ui.screen.home.detail
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import androidx.activity.compose.BackHandler
 | 
					import androidx.activity.compose.BackHandler
 | 
				
			||||||
 | 
					import androidx.annotation.StringRes
 | 
				
			||||||
import androidx.compose.animation.animateContentSize
 | 
					import androidx.compose.animation.animateContentSize
 | 
				
			||||||
import androidx.compose.foundation.layout.*
 | 
					import androidx.compose.foundation.layout.*
 | 
				
			||||||
import androidx.compose.foundation.rememberScrollState
 | 
					import androidx.compose.foundation.rememberScrollState
 | 
				
			||||||
| 
						 | 
					@ -19,7 +20,6 @@ import com.pixelized.biblib.ui.screen.home.page.profile.ProfileViewModel
 | 
				
			||||||
import com.pixelized.biblib.ui.theme.color.ShadowPalette
 | 
					import com.pixelized.biblib.ui.theme.color.ShadowPalette
 | 
				
			||||||
import com.pixelized.biblib.utils.extention.bibLib
 | 
					import com.pixelized.biblib.utils.extention.bibLib
 | 
				
			||||||
import com.pixelized.biblib.utils.extention.showToast
 | 
					import com.pixelized.biblib.utils.extention.showToast
 | 
				
			||||||
import kotlinx.coroutines.flow.collect
 | 
					 | 
				
			||||||
import kotlinx.coroutines.launch
 | 
					import kotlinx.coroutines.launch
 | 
				
			||||||
import java.io.Serializable
 | 
					import java.io.Serializable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -54,22 +54,22 @@ data class BookDetailUio(
 | 
				
			||||||
@Stable
 | 
					@Stable
 | 
				
			||||||
@Immutable
 | 
					@Immutable
 | 
				
			||||||
sealed class BookDetailUioErrorUio(
 | 
					sealed class BookDetailUioErrorUio(
 | 
				
			||||||
    val message: String,
 | 
					    @StringRes val message: Int,
 | 
				
			||||||
    val action: String?,
 | 
					    @StringRes val action: Int,
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
    @Stable
 | 
					    @Stable
 | 
				
			||||||
    @Immutable
 | 
					    @Immutable
 | 
				
			||||||
    class GetDetailInput(
 | 
					    class GetDetailInput(
 | 
				
			||||||
        message: String,
 | 
					        @StringRes message: Int,
 | 
				
			||||||
        action: String?,
 | 
					        @StringRes action: Int,
 | 
				
			||||||
        val bookId: Int,
 | 
					        val bookId: Int,
 | 
				
			||||||
    ) : BookDetailUioErrorUio(message, action)
 | 
					    ) : BookDetailUioErrorUio(message, action)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Stable
 | 
					    @Stable
 | 
				
			||||||
    @Immutable
 | 
					    @Immutable
 | 
				
			||||||
    class SendBookInput(
 | 
					    class SendBookInput(
 | 
				
			||||||
        message: String,
 | 
					        @StringRes message: Int,
 | 
				
			||||||
        action: String?,
 | 
					        @StringRes action: Int,
 | 
				
			||||||
        val bookId: Int,
 | 
					        val bookId: Int,
 | 
				
			||||||
        val mail: String,
 | 
					        val mail: String,
 | 
				
			||||||
        val format: String,
 | 
					        val format: String,
 | 
				
			||||||
| 
						 | 
					@ -80,7 +80,6 @@ sealed class BookDetailUioErrorUio(
 | 
				
			||||||
@Composable
 | 
					@Composable
 | 
				
			||||||
fun DetailScreen(
 | 
					fun DetailScreen(
 | 
				
			||||||
    detailViewModel: BookDetailViewModel = hiltViewModel(),
 | 
					    detailViewModel: BookDetailViewModel = hiltViewModel(),
 | 
				
			||||||
    confirmViewModel: ConfirmDialogViewModel = hiltViewModel(),
 | 
					 | 
				
			||||||
    profileViewModel: ProfileViewModel = hiltViewModel(),
 | 
					    profileViewModel: ProfileViewModel = hiltViewModel(),
 | 
				
			||||||
    bookId: Int? = null,
 | 
					    bookId: Int? = null,
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
| 
						 | 
					@ -107,8 +106,12 @@ fun DetailScreen(
 | 
				
			||||||
                        modifier = Modifier
 | 
					                        modifier = Modifier
 | 
				
			||||||
                            .navigationBarsPadding()
 | 
					                            .navigationBarsPadding()
 | 
				
			||||||
                            .padding(bottom = MaterialTheme.bibLib.dimen.dp16),
 | 
					                            .padding(bottom = MaterialTheme.bibLib.dimen.dp16),
 | 
				
			||||||
 | 
					                        onHelp = { uri ->
 | 
				
			||||||
 | 
					                            uriHandler.openUri(uri)
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
                        onSend = { mail, format ->
 | 
					                        onSend = { mail, format ->
 | 
				
			||||||
                            confirmViewModel.show(bookId = detail.id, email = mail, format = format)
 | 
					                            scope.launch { emailSheetState.hide() }
 | 
				
			||||||
 | 
					                            detailViewModel.send(bookId = detail.id, email = mail, format = format)
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    )
 | 
					                    )
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
| 
						 | 
					@ -131,13 +134,6 @@ fun DetailScreen(
 | 
				
			||||||
                                        mails.isEmpty() -> {
 | 
					                                        mails.isEmpty() -> {
 | 
				
			||||||
                                            context.showToast(context.getString(R.string.error_no_amazon_email))
 | 
					                                            context.showToast(context.getString(R.string.error_no_amazon_email))
 | 
				
			||||||
                                        }
 | 
					                                        }
 | 
				
			||||||
                                        mails.size == 1 -> {
 | 
					 | 
				
			||||||
                                            confirmViewModel.show(
 | 
					 | 
				
			||||||
                                                bookId = detail.id,
 | 
					 | 
				
			||||||
                                                email = mails.first(),
 | 
					 | 
				
			||||||
                                                format = Format.EPUB,
 | 
					 | 
				
			||||||
                                            )
 | 
					 | 
				
			||||||
                                        }
 | 
					 | 
				
			||||||
                                        else -> {
 | 
					                                        else -> {
 | 
				
			||||||
                                            scope.launch { emailSheetState.show() }
 | 
					                                            scope.launch { emailSheetState.show() }
 | 
				
			||||||
                                        }
 | 
					                                        }
 | 
				
			||||||
| 
						 | 
					@ -152,20 +148,6 @@ fun DetailScreen(
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 | 
					 | 
				
			||||||
            ConfirmDialog(
 | 
					 | 
				
			||||||
                modifier = Modifier.padding(all = MaterialTheme.bibLib.dimen.dp32),
 | 
					 | 
				
			||||||
                confirmViewModel = confirmViewModel,
 | 
					 | 
				
			||||||
                onConfirm = { id, email, format ->
 | 
					 | 
				
			||||||
                    scope.launch {
 | 
					 | 
				
			||||||
                        confirmViewModel.hide()
 | 
					 | 
				
			||||||
                        emailSheetState.hide()
 | 
					 | 
				
			||||||
                        detailViewModel.send(bookId = id, email = email, format = format)
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
                onDismiss = { confirmViewModel.hide() },
 | 
					 | 
				
			||||||
                onHelp = { url -> uriHandler.openUri(url) }
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -173,9 +155,8 @@ fun DetailScreen(
 | 
				
			||||||
        LaunchedEffect(key1 = "DetailScreenError") {
 | 
					        LaunchedEffect(key1 = "DetailScreenError") {
 | 
				
			||||||
            detailViewModel.error.collect {
 | 
					            detailViewModel.error.collect {
 | 
				
			||||||
                val result = snackBarHost.showSnackbar(
 | 
					                val result = snackBarHost.showSnackbar(
 | 
				
			||||||
                    message = it.message,
 | 
					                    message = context.getString(it.message),
 | 
				
			||||||
                    actionLabel = it.action,
 | 
					                    actionLabel = context.getString(it.action),
 | 
				
			||||||
                    duration = SnackbarDuration.Indefinite,
 | 
					 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
                if (result == SnackbarResult.ActionPerformed) {
 | 
					                if (result == SnackbarResult.ActionPerformed) {
 | 
				
			||||||
                    when (it) {
 | 
					                    when (it) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,191 +0,0 @@
 | 
				
			||||||
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.*
 | 
					 | 
				
			||||||
import androidx.compose.runtime.Composable
 | 
					 | 
				
			||||||
import androidx.compose.runtime.Immutable
 | 
					 | 
				
			||||||
import androidx.compose.runtime.Stable
 | 
					 | 
				
			||||||
import androidx.compose.runtime.remember
 | 
					 | 
				
			||||||
import androidx.compose.ui.Alignment
 | 
					 | 
				
			||||||
import androidx.compose.ui.ExperimentalComposeUiApi
 | 
					 | 
				
			||||||
import androidx.compose.ui.Modifier
 | 
					 | 
				
			||||||
import androidx.compose.ui.res.stringResource
 | 
					 | 
				
			||||||
import androidx.compose.ui.text.AnnotatedString
 | 
					 | 
				
			||||||
import androidx.compose.ui.text.style.TextAlign
 | 
					 | 
				
			||||||
import androidx.compose.ui.text.style.TextOverflow
 | 
					 | 
				
			||||||
import androidx.compose.ui.tooling.preview.Preview
 | 
					 | 
				
			||||||
import androidx.compose.ui.unit.dp
 | 
					 | 
				
			||||||
import androidx.compose.ui.window.Dialog
 | 
					 | 
				
			||||||
import androidx.compose.ui.window.DialogProperties
 | 
					 | 
				
			||||||
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.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,
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@Composable
 | 
					 | 
				
			||||||
fun ConfirmDialog(
 | 
					 | 
				
			||||||
    modifier: Modifier = Modifier,
 | 
					 | 
				
			||||||
    confirmViewModel: ConfirmDialogViewModel,
 | 
					 | 
				
			||||||
    onConfirm: (bookId: Int, mail: String, format: String) -> Unit = { _, _, _ -> },
 | 
					 | 
				
			||||||
    onHelp: (url: String) -> Unit = default<String>(),
 | 
					 | 
				
			||||||
    onDismiss: () -> Unit
 | 
					 | 
				
			||||||
) {
 | 
					 | 
				
			||||||
    confirmViewModel.dialog?.let { dialog ->
 | 
					 | 
				
			||||||
        ConfirmDialog(
 | 
					 | 
				
			||||||
            modifier = modifier,
 | 
					 | 
				
			||||||
            uio = dialog,
 | 
					 | 
				
			||||||
            onDismissRequest = onDismiss,
 | 
					 | 
				
			||||||
            onHelp = onHelp,
 | 
					 | 
				
			||||||
            onConfirm = onConfirm,
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@OptIn(ExperimentalComposeUiApi::class)
 | 
					 | 
				
			||||||
@Composable
 | 
					 | 
				
			||||||
fun ConfirmDialog(
 | 
					 | 
				
			||||||
    modifier: Modifier = Modifier,
 | 
					 | 
				
			||||||
    uio: ConfirmDialogUio,
 | 
					 | 
				
			||||||
    onDismissRequest: () -> Unit = default(),
 | 
					 | 
				
			||||||
    onHelp: (url: String) -> Unit = default<String>(),
 | 
					 | 
				
			||||||
    onConfirm: (bookId: Int, mail: String, format: String) -> Unit = { _, _, _ -> },
 | 
					 | 
				
			||||||
) {
 | 
					 | 
				
			||||||
    Dialog(
 | 
					 | 
				
			||||||
        onDismissRequest = onDismissRequest,
 | 
					 | 
				
			||||||
        properties = remember { DialogProperties(usePlatformDefaultWidth = false) },
 | 
					 | 
				
			||||||
    ) {
 | 
					 | 
				
			||||||
        ConfirmDialogContent(
 | 
					 | 
				
			||||||
            modifier = modifier,
 | 
					 | 
				
			||||||
            uio = uio,
 | 
					 | 
				
			||||||
            onDismissRequest = onDismissRequest,
 | 
					 | 
				
			||||||
            onHelp = onHelp,
 | 
					 | 
				
			||||||
            onConfirm = onConfirm,
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@Composable
 | 
					 | 
				
			||||||
fun ConfirmDialogContent(
 | 
					 | 
				
			||||||
    modifier: Modifier = Modifier,
 | 
					 | 
				
			||||||
    uio: ConfirmDialogUio,
 | 
					 | 
				
			||||||
    onDismissRequest: () -> Unit = default(),
 | 
					 | 
				
			||||||
    onHelp: (url: String) -> Unit = default<String>(),
 | 
					 | 
				
			||||||
    onConfirm: (bookId: Int, mail: String, format: String) -> Unit = { _, _, _ -> },
 | 
					 | 
				
			||||||
) {
 | 
					 | 
				
			||||||
    val amazonHelpUri = stringResource(R.string.detail_send_confirm_help_url)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Card(
 | 
					 | 
				
			||||||
        modifier = modifier,
 | 
					 | 
				
			||||||
        backgroundColor = MaterialTheme.bibLib.colors.dialogBackground,
 | 
					 | 
				
			||||||
    ) {
 | 
					 | 
				
			||||||
        Column(modifier = Modifier.padding(all = MaterialTheme.bibLib.dimen.dp16)) {
 | 
					 | 
				
			||||||
            Text(
 | 
					 | 
				
			||||||
                modifier = Modifier.padding(bottom = MaterialTheme.bibLib.dimen.dp16),
 | 
					 | 
				
			||||||
                style = MaterialTheme.typography.h6,
 | 
					 | 
				
			||||||
                color = MaterialTheme.bibLib.colors.typography.medium,
 | 
					 | 
				
			||||||
                text = stringResource(R.string.detail_send_confirm_title)
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            Text(
 | 
					 | 
				
			||||||
                modifier = Modifier.padding(bottom = MaterialTheme.bibLib.dimen.dp16),
 | 
					 | 
				
			||||||
                style = MaterialTheme.typography.body1,
 | 
					 | 
				
			||||||
                color = MaterialTheme.bibLib.colors.typography.medium,
 | 
					 | 
				
			||||||
                text = rememberDescription()
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            Text(
 | 
					 | 
				
			||||||
                modifier = Modifier.padding(bottom = MaterialTheme.bibLib.dimen.dp8),
 | 
					 | 
				
			||||||
                style = MaterialTheme.typography.caption,
 | 
					 | 
				
			||||||
                color = MaterialTheme.bibLib.colors.typography.easy,
 | 
					 | 
				
			||||||
                text = uio.email
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            Text(
 | 
					 | 
				
			||||||
                modifier = Modifier
 | 
					 | 
				
			||||||
                    .clickable(onClick = { onHelp(amazonHelpUri) })
 | 
					 | 
				
			||||||
                    .padding(vertical = MaterialTheme.bibLib.dimen.dp8),
 | 
					 | 
				
			||||||
                textAlign = TextAlign.Center,
 | 
					 | 
				
			||||||
                style = MaterialTheme.typography.caption,
 | 
					 | 
				
			||||||
                color = MaterialTheme.bibLib.colors.typography.strong,
 | 
					 | 
				
			||||||
                overflow = TextOverflow.Ellipsis,
 | 
					 | 
				
			||||||
                maxLines = 1,
 | 
					 | 
				
			||||||
                text = stringResource(R.string.detail_send_confirm_help),
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            Row(
 | 
					 | 
				
			||||||
                modifier = Modifier
 | 
					 | 
				
			||||||
                    .fillMaxWidth()
 | 
					 | 
				
			||||||
                    .padding(top = MaterialTheme.bibLib.dimen.dp8),
 | 
					 | 
				
			||||||
                horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.End),
 | 
					 | 
				
			||||||
            ) {
 | 
					 | 
				
			||||||
                Button(
 | 
					 | 
				
			||||||
                    colors = ButtonDefaults.outlinedButtonColors(),
 | 
					 | 
				
			||||||
                    onClick = onDismissRequest,
 | 
					 | 
				
			||||||
                ) {
 | 
					 | 
				
			||||||
                    Text(
 | 
					 | 
				
			||||||
                        text = stringResource(R.string.detail_send_confirm_cancel_action)
 | 
					 | 
				
			||||||
                    )
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                Button(
 | 
					 | 
				
			||||||
                    colors = ButtonDefaults.buttonColors(),
 | 
					 | 
				
			||||||
                    onClick = { onConfirm(uio.bookId, uio.email, uio.format) }
 | 
					 | 
				
			||||||
                ) {
 | 
					 | 
				
			||||||
                    Text(
 | 
					 | 
				
			||||||
                        text = stringResource(R.string.detail_send_confirm_confirm_action)
 | 
					 | 
				
			||||||
                    )
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@Composable
 | 
					 | 
				
			||||||
private fun rememberDescription(): AnnotatedString {
 | 
					 | 
				
			||||||
    val email = stringResource(id = R.string.martin_sender)
 | 
					 | 
				
			||||||
    val description = stringResource(R.string.detail_send_confirm_description, email)
 | 
					 | 
				
			||||||
    return description.highlight(highlight = stringRegex(email))
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@Composable
 | 
					 | 
				
			||||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
 | 
					 | 
				
			||||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
 | 
					 | 
				
			||||||
private fun DetailConfirmPreview() {
 | 
					 | 
				
			||||||
    BibLibTheme {
 | 
					 | 
				
			||||||
        ConfirmDialogContent(
 | 
					 | 
				
			||||||
            uio = ConfirmDialogUio.preview(),
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,10 +1,11 @@
 | 
				
			||||||
package com.pixelized.biblib.ui.screen.home.detail
 | 
					package com.pixelized.biblib.ui.screen.home.detail
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import android.content.res.Configuration
 | 
					import android.content.res.Configuration
 | 
				
			||||||
 | 
					import androidx.annotation.StringDef
 | 
				
			||||||
import androidx.compose.foundation.clickable
 | 
					import androidx.compose.foundation.clickable
 | 
				
			||||||
 | 
					import androidx.compose.foundation.layout.Column
 | 
				
			||||||
import androidx.compose.foundation.layout.Row
 | 
					import androidx.compose.foundation.layout.Row
 | 
				
			||||||
import androidx.compose.foundation.layout.fillMaxWidth
 | 
					import androidx.compose.foundation.layout.fillMaxWidth
 | 
				
			||||||
import androidx.compose.foundation.layout.height
 | 
					 | 
				
			||||||
import androidx.compose.foundation.layout.padding
 | 
					import androidx.compose.foundation.layout.padding
 | 
				
			||||||
import androidx.compose.foundation.lazy.LazyColumn
 | 
					import androidx.compose.foundation.lazy.LazyColumn
 | 
				
			||||||
import androidx.compose.foundation.lazy.items
 | 
					import androidx.compose.foundation.lazy.items
 | 
				
			||||||
| 
						 | 
					@ -18,13 +19,15 @@ import androidx.compose.runtime.Stable
 | 
				
			||||||
import androidx.compose.ui.Alignment
 | 
					import androidx.compose.ui.Alignment
 | 
				
			||||||
import androidx.compose.ui.Modifier
 | 
					import androidx.compose.ui.Modifier
 | 
				
			||||||
import androidx.compose.ui.res.stringResource
 | 
					import androidx.compose.ui.res.stringResource
 | 
				
			||||||
 | 
					import androidx.compose.ui.text.AnnotatedString
 | 
				
			||||||
 | 
					import androidx.compose.ui.text.TextStyle
 | 
				
			||||||
 | 
					import androidx.compose.ui.text.style.TextAlign
 | 
				
			||||||
import androidx.compose.ui.text.style.TextOverflow
 | 
					import androidx.compose.ui.text.style.TextOverflow
 | 
				
			||||||
import androidx.compose.ui.tooling.preview.Preview
 | 
					import androidx.compose.ui.tooling.preview.Preview
 | 
				
			||||||
import androidx.hilt.navigation.compose.hiltViewModel
 | 
					import androidx.hilt.navigation.compose.hiltViewModel
 | 
				
			||||||
import com.pixelized.biblib.R
 | 
					import com.pixelized.biblib.R
 | 
				
			||||||
import com.pixelized.biblib.ui.theme.BibLibTheme
 | 
					import com.pixelized.biblib.ui.theme.BibLibTheme
 | 
				
			||||||
import com.pixelized.biblib.utils.extention.bibLib
 | 
					import com.pixelized.biblib.utils.extention.*
 | 
				
			||||||
import com.pixelized.biblib.utils.extention.capitalize
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Stable
 | 
					@Stable
 | 
				
			||||||
@Immutable
 | 
					@Immutable
 | 
				
			||||||
| 
						 | 
					@ -33,10 +36,20 @@ data class OptionUio(
 | 
				
			||||||
    val selected: Boolean = false,
 | 
					    val selected: Boolean = false,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Retention(AnnotationRetention.SOURCE)
 | 
				
			||||||
 | 
					@StringDef(Format.EPUB, Format.MOBI)
 | 
				
			||||||
 | 
					annotation class Format {
 | 
				
			||||||
 | 
					    companion object {
 | 
				
			||||||
 | 
					        const val EPUB = "epub"
 | 
				
			||||||
 | 
					        const val MOBI = "mobi"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Composable
 | 
					@Composable
 | 
				
			||||||
fun DetailScreenSendOption(
 | 
					fun DetailScreenSendOption(
 | 
				
			||||||
    modifier: Modifier = Modifier,
 | 
					    modifier: Modifier = Modifier,
 | 
				
			||||||
    optionViewModel: BookOptionViewModel = hiltViewModel(),
 | 
					    optionViewModel: BookOptionViewModel = hiltViewModel(),
 | 
				
			||||||
 | 
					    onHelp: (url: String) -> Unit = default<String>(),
 | 
				
			||||||
    onSend: (email: String, format: String) -> Unit = { _, _ -> },
 | 
					    onSend: (email: String, format: String) -> Unit = { _, _ -> },
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
    DetailScreenSendOption(
 | 
					    DetailScreenSendOption(
 | 
				
			||||||
| 
						 | 
					@ -45,6 +58,7 @@ fun DetailScreenSendOption(
 | 
				
			||||||
        formats = optionViewModel.formats,
 | 
					        formats = optionViewModel.formats,
 | 
				
			||||||
        onEmail = { optionViewModel.selectMail(it.value) },
 | 
					        onEmail = { optionViewModel.selectMail(it.value) },
 | 
				
			||||||
        onFormat = { optionViewModel.selectFormat(it.value) },
 | 
					        onFormat = { optionViewModel.selectFormat(it.value) },
 | 
				
			||||||
 | 
					        onHelp = onHelp,
 | 
				
			||||||
        onSend = onSend,
 | 
					        onSend = onSend,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -54,19 +68,56 @@ fun DetailScreenSendOption(
 | 
				
			||||||
    modifier: Modifier = Modifier,
 | 
					    modifier: Modifier = Modifier,
 | 
				
			||||||
    emails: List<OptionUio>,
 | 
					    emails: List<OptionUio>,
 | 
				
			||||||
    formats: List<OptionUio>,
 | 
					    formats: List<OptionUio>,
 | 
				
			||||||
 | 
					    onHelp: (url: String) -> Unit = default<String>(),
 | 
				
			||||||
    onEmail: (OptionUio) -> Unit = { },
 | 
					    onEmail: (OptionUio) -> Unit = { },
 | 
				
			||||||
    onFormat: (OptionUio) -> Unit = { },
 | 
					    onFormat: (OptionUio) -> Unit = { },
 | 
				
			||||||
    onSend: (email: String, format: String) -> Unit = { _, _ -> },
 | 
					    onSend: (email: String, format: String) -> Unit = { _, _ -> },
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
 | 
					    val amazonHelpUri = stringResource(R.string.detail_send_confirm_help_url)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    LazyColumn(
 | 
					    LazyColumn(
 | 
				
			||||||
        modifier = modifier.fillMaxWidth(),
 | 
					        modifier = modifier.fillMaxWidth(),
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
 | 
					        item {
 | 
				
			||||||
 | 
					            Column(
 | 
				
			||||||
 | 
					                modifier = Modifier
 | 
				
			||||||
 | 
					                    .padding(top = MaterialTheme.bibLib.dimen.dp16)
 | 
				
			||||||
 | 
					                    .padding(horizontal = MaterialTheme.bibLib.dimen.dp16),
 | 
				
			||||||
 | 
					            ) {
 | 
				
			||||||
 | 
					                Text(
 | 
				
			||||||
 | 
					                    modifier = Modifier.padding(bottom = MaterialTheme.bibLib.dimen.dp16),
 | 
				
			||||||
 | 
					                    style = MaterialTheme.typography.h6,
 | 
				
			||||||
 | 
					                    color = MaterialTheme.bibLib.colors.typography.medium,
 | 
				
			||||||
 | 
					                    text = stringResource(R.string.detail_send_confirm_title)
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                Text(
 | 
				
			||||||
 | 
					                    modifier = Modifier.padding(bottom = MaterialTheme.bibLib.dimen.dp8),
 | 
				
			||||||
 | 
					                    style = MaterialTheme.typography.caption,
 | 
				
			||||||
 | 
					                    color = MaterialTheme.bibLib.colors.typography.easy,
 | 
				
			||||||
 | 
					                    text = rememberDescription()
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                Text(
 | 
				
			||||||
 | 
					                    modifier = Modifier
 | 
				
			||||||
 | 
					                        .clickable(onClick = { onHelp(amazonHelpUri) })
 | 
				
			||||||
 | 
					                        .padding(vertical = MaterialTheme.bibLib.dimen.dp8),
 | 
				
			||||||
 | 
					                    textAlign = TextAlign.Center,
 | 
				
			||||||
 | 
					                    style = MaterialTheme.typography.caption,
 | 
				
			||||||
 | 
					                    color = MaterialTheme.bibLib.colors.typography.strong,
 | 
				
			||||||
 | 
					                    overflow = TextOverflow.Ellipsis,
 | 
				
			||||||
 | 
					                    maxLines = 1,
 | 
				
			||||||
 | 
					                    text = stringResource(R.string.detail_send_confirm_help),
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        item {
 | 
					        item {
 | 
				
			||||||
            Text(
 | 
					            Text(
 | 
				
			||||||
                modifier = Modifier
 | 
					                modifier = Modifier
 | 
				
			||||||
                    .padding(vertical = MaterialTheme.bibLib.dimen.dp8)
 | 
					                    .padding(vertical = MaterialTheme.bibLib.dimen.dp8)
 | 
				
			||||||
                    .padding(horizontal = MaterialTheme.bibLib.dimen.dp16),
 | 
					                    .padding(horizontal = MaterialTheme.bibLib.dimen.dp16),
 | 
				
			||||||
                color = MaterialTheme.colors.primary,
 | 
					                color = MaterialTheme.bibLib.colors.typography.easy,
 | 
				
			||||||
                style = MaterialTheme.typography.caption,
 | 
					                style = MaterialTheme.typography.caption,
 | 
				
			||||||
                text = stringResource(id = R.string.detail_option_mail),
 | 
					                text = stringResource(id = R.string.detail_option_mail),
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
| 
						 | 
					@ -85,7 +136,7 @@ fun DetailScreenSendOption(
 | 
				
			||||||
                modifier = Modifier
 | 
					                modifier = Modifier
 | 
				
			||||||
                    .padding(vertical = MaterialTheme.bibLib.dimen.dp8)
 | 
					                    .padding(vertical = MaterialTheme.bibLib.dimen.dp8)
 | 
				
			||||||
                    .padding(horizontal = MaterialTheme.bibLib.dimen.dp16),
 | 
					                    .padding(horizontal = MaterialTheme.bibLib.dimen.dp16),
 | 
				
			||||||
                color = MaterialTheme.colors.primary,
 | 
					                color = MaterialTheme.bibLib.colors.typography.easy,
 | 
				
			||||||
                style = MaterialTheme.typography.caption,
 | 
					                style = MaterialTheme.typography.caption,
 | 
				
			||||||
                text = stringResource(id = R.string.detail_option_mail),
 | 
					                text = stringResource(id = R.string.detail_option_mail),
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
| 
						 | 
					@ -147,6 +198,18 @@ private fun OptionItem(
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Composable
 | 
				
			||||||
 | 
					private fun rememberDescription(
 | 
				
			||||||
 | 
					    style: TextStyle = MaterialTheme.typography.caption,
 | 
				
			||||||
 | 
					): AnnotatedString {
 | 
				
			||||||
 | 
					    val email = stringResource(id = R.string.martin_sender)
 | 
				
			||||||
 | 
					    val description = stringResource(R.string.detail_send_confirm_description, email)
 | 
				
			||||||
 | 
					    return description.highlight(
 | 
				
			||||||
 | 
					        defaultStyle = style.toSpanStyle(),
 | 
				
			||||||
 | 
					        highlight = stringRegex(email),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Composable
 | 
					@Composable
 | 
				
			||||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
 | 
					@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
 | 
				
			||||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
 | 
					@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue